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 {
BLANK("blank.png"),
CHECKERED("checkerboard.png"),
HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"),
SELECTION("selection.png"),
;

View file

@ -67,8 +67,6 @@ public class ClientEvents {
@SubscribeEvent
public static void onRenderWorld(RenderWorldLastEvent event) {
CreateClient.schematicHandler.render();
CreateClient.schematicAndQuillHandler.render();
CreateClient.schematicHologram.render();
KineticDebugger.renderSourceOutline();
ChassisRangeDisplay.renderOutlines(event.getPartialTicks());
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.client.SchematicAndQuillHandler;
import com.simibubi.create.modules.schematics.client.SchematicHandler;
import com.simibubi.create.modules.schematics.client.SchematicHologram;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
@ -40,10 +39,8 @@ public class CreateClient {
public static ClientSchematicLoader schematicSender;
public static SchematicHandler schematicHandler;
public static SchematicHologram schematicHologram;
public static SchematicAndQuillHandler schematicAndQuillHandler;
public static SuperByteBufferCache bufferCache;
public static int renderTicks;
public static void addListeners(IEventBus modEventBus) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
@ -58,7 +55,6 @@ public class CreateClient {
public static void clientInit(FMLClientSetupEvent event) {
schematicSender = new ClientSchematicLoader();
schematicHandler = new SchematicHandler();
schematicHologram = new SchematicHologram();
schematicAndQuillHandler = new SchematicAndQuillHandler();
bufferCache = new SuperByteBufferCache();
@ -81,7 +77,6 @@ public class CreateClient {
schematicSender.tick();
schematicAndQuillHandler.tick();
schematicHandler.tick();
schematicHologram.tick();
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,12 +7,16 @@ public class InterpolatedChasingValue extends InterpolatedValue {
float eps = 1 / 4096f;
public void tick() {
float diff = target - value;
float diff = getCurrentDiff();
if (Math.abs(diff) < eps)
return;
set(value + (diff) * speed);
}
protected float getCurrentDiff() {
return getTarget() - value;
}
public InterpolatedChasingValue withSpeed(float speed) {
this.speed = speed;
return this;
@ -23,4 +27,14 @@ public class InterpolatedChasingValue extends InterpolatedValue {
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);
}
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 f1 = playerIn.rotationYaw;
float f2 = MathHelper.cos(-f1 * 0.017453292F - (float) Math.PI);
@ -42,7 +42,7 @@ public class RaycastHelper {
return vec3d1;
}
private static Vec3d getTraceOrigin(PlayerEntity playerIn) {
public static Vec3d getTraceOrigin(PlayerEntity playerIn) {
double d0 = playerIn.posX;
double d1 = playerIn.posY + (double) playerIn.getEyeHeight();
double d2 = playerIn.posZ;
@ -50,7 +50,7 @@ public class RaycastHelper {
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))
return null;
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.List;
import java.util.function.Predicate;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
@ -11,6 +12,7 @@ import net.minecraft.fluid.Fluid;
import net.minecraft.item.crafting.RecipeManager;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.tags.NetworkTagManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
@ -33,6 +35,26 @@ public class WrappedWorld extends 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
public int getLight(BlockPos pos) {
return 15;
@ -59,8 +81,7 @@ public class WrappedWorld extends World {
}
@Override
public void playEvent(PlayerEntity player, int type, BlockPos pos, int data) {
}
public void playEvent(PlayerEntity player, int type, BlockPos pos, int data) {}
@Override
public List<? extends PlayerEntity> getPlayers() {
@ -69,13 +90,11 @@ public class WrappedWorld extends World {
@Override
public void playSound(PlayerEntity player, double x, double y, double z, SoundEvent soundIn, SoundCategory category,
float volume, float pitch) {
}
float volume, float pitch) {}
@Override
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
public Entity getEntityByID(int id) {
@ -94,8 +113,7 @@ public class WrappedWorld extends World {
}
@Override
public void registerMapData(MapData mapDataIn) {
}
public void registerMapData(MapData mapDataIn) {}
@Override
public int getNextMapId() {
@ -103,8 +121,7 @@ public class WrappedWorld extends World {
}
@Override
public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {
}
public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {}
@Override
public Scoreboard getScoreboard() {

View file

@ -1,16 +1,29 @@
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.foundation.utility.ColorHelper;
import net.minecraft.client.Minecraft;
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.BlockPos;
import net.minecraft.util.math.Vec3d;
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
public void render(BufferBuilder buffer) {
@ -18,8 +31,25 @@ public class AABBOutline extends Outline {
Vec3d color = ColorHelper.getRGB(0xFFFFFF);
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.maxX, bb.minY, 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.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;
AllSpecialTextures.BLANK.bind();
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);
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();
}
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);
if (diff.x + diff.y + diff.z < 0) {
Vec3d temp = start;
@ -91,11 +91,16 @@ public abstract class Outline {
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) {
putVertex(v1, rgb, 0, 0, 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 putQuad(Vec3d v1, Vec3d v2, Vec3d v3, Vec3d v4, Vec3d rgb, float alpha, BufferBuilder buffer) {
putQuadUV(v1, v2, v3, v4, 0, 0, 1, 1, rgb, 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) {

View file

@ -11,20 +11,20 @@ import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.Vec3d;
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);
this.outline = outline;
this.maxAge = 1024;
}
public static OutlineParticle create(Outline outline) {
public static <O extends Outline> OutlineParticle<O> create(O outline) {
Minecraft mc = Minecraft.getInstance();
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);
return effect;
}
@ -39,14 +39,12 @@ public class OutlineParticle extends Particle {
GlStateManager.pushMatrix();
Vec3d view = entityIn.getProjectedView();
GlStateManager.translated(-view.x, -view.y, -view.z);
GlStateManager.depthMask(false);
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.enableBlend();
outline.render(buffer);
getOutline().render(buffer);
GlStateManager.disableBlend();
GlStateManager.depthMask(true);
GlStateManager.popMatrix();
}
@ -55,4 +53,8 @@ public class OutlineParticle extends Particle {
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.foundation.utility.TessellatorHelper;
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.modules.contraptions.components.contraptions.chassis.ChassisTileEntity;
@ -32,7 +33,7 @@ public class ChassisRangeDisplay {
private static class Entry {
BlockClusterOutline outline;
OutlineParticle particle;
OutlineParticle<Outline> particle;
ChassisTileEntity te;
int timer;

View file

@ -1,22 +1,16 @@
package com.simibubi.create.modules.contraptions.components.contraptions;
import java.util.Iterator;
import java.util.Random;
import java.util.function.Consumer;
import org.apache.commons.lang3.tuple.Pair;
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.config.AllConfigs;
import com.simibubi.create.foundation.utility.PlacementSimulationWorld;
import com.simibubi.create.foundation.utility.SuperByteBuffer;
import com.simibubi.create.foundation.utility.SuperByteBufferCache.Compartment;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.WrappedWorld;
import com.simibubi.create.foundation.utility.render.StructureRenderer;
import net.minecraft.block.BlockRenderType;
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.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
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.MutableBlockPos;
import net.minecraft.util.math.Vec3d;
@ -42,7 +32,6 @@ public class ContraptionRenderer {
public static final Compartment<Contraption> CONTRAPTION = new Compartment<>();
protected static PlacementSimulationWorld renderWorld;
protected static LightingWorld lightingWorld;
public static void render(World world, Contraption c, Consumer<SuperByteBuffer> transform, BufferBuilder buffer) {
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) {
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 = 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);
StructureRenderer.renderTileEntities(world, position, rotation, c.customRenderTEs);
}
private static SuperByteBuffer renderContraption(Contraption c) {
@ -173,36 +115,4 @@ public class ContraptionRenderer {
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;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -29,13 +30,16 @@ import net.minecraft.world.biome.Biomes;
public class SchematicWorld extends WrappedWorld {
private Map<BlockPos, BlockState> blocks;
private Map<BlockPos, TileEntity> tileEntities;
private Cuboid bounds;
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);
this.blocks = blocks;
this.setBounds(bounds);
this.blocks = new HashMap<>();
this.tileEntities = new HashMap<>();
this.bounds = new Cuboid();
this.anchor = anchor;
}
@ -45,6 +49,19 @@ public class SchematicWorld extends WrappedWorld {
@Override
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;
}
@ -52,7 +69,7 @@ public class SchematicWorld extends WrappedWorld {
public BlockState getBlockState(BlockPos globalPos) {
BlockPos pos = globalPos.subtract(anchor);
if (pos.getY() - bounds.y == -1) {
if (pos.getY() - bounds.y == -1 && !renderMode) {
return Blocks.GRASS_BLOCK.getDefaultState();
}
@ -171,4 +188,8 @@ public class SchematicWorld extends WrappedWorld {
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) {
@Override
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;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -12,7 +11,6 @@ import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.config.CSchematics;
import com.simibubi.create.foundation.behaviour.base.SmartTileEntity;
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.SchematicWorld;
import com.simibubi.create.modules.schematics.item.SchematicItem;
@ -466,7 +464,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
}
// Load blocks into reader
Template activeTemplate = SchematicItem.getSchematic(blueprint);
Template activeTemplate = SchematicItem.loadSchematic(blueprint);
BlockPos anchor = NBTUtil.readBlockPos(blueprint.getTag().getCompound("Anchor"));
if (activeTemplate.getSize().equals(BlockPos.ZERO)) {
@ -484,7 +482,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
}
schematicAnchor = anchor;
blockReader = new SchematicWorld(new HashMap<>(), new Cuboid(), schematicAnchor, world);
blockReader = new SchematicWorld(schematicAnchor, world);
activeTemplate.addBlocksToWorld(blockReader, schematicAnchor, SchematicItem.getSettings(blueprint));
schematicLoaded = true;
state = State.PAUSED;

View file

@ -8,7 +8,6 @@ import java.nio.file.StandardOpenOption;
import org.apache.commons.io.IOUtils;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys;
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.RaycastHelper;
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.client.Minecraft;
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.ItemUseContext;
import net.minecraft.nbt.CompoundNBT;
@ -32,6 +31,7 @@ import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MathHelper;
@ -49,14 +49,7 @@ public class SchematicAndQuillHandler {
private Direction selectedFace;
private int range = 10;
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;
}
private OutlineParticle<ChasingAABBOutline> particle;
public boolean mouseScrolled(double delta) {
if (!isActive())
@ -65,27 +58,32 @@ public class SchematicAndQuillHandler {
return false;
if (secondPos == null)
range = (int) MathHelper.clamp(range + delta, 1, 100);
if (selectedFace != null) {
MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos);
Vec3i vec = selectedFace.getDirectionVec();
if (selectedFace == null)
return true;
int x = (int) (vec.getX() * delta);
int y = (int) (vec.getY() * delta);
int z = (int) (vec.getZ() * delta);
AxisAlignedBB bb = new AxisAlignedBB(firstPos, secondPos);
Vec3i vec = selectedFace.getDirectionVec();
Vec3d projectedView = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView();
if (bb.contains(projectedView))
delta *= -1;
AxisDirection axisDirection = selectedFace.getAxisDirection();
if (axisDirection == AxisDirection.NEGATIVE)
bb.offset(-x, -y, -z);
int x = (int) (vec.getX() * delta);
int y = (int) (vec.getY() * delta);
int z = (int) (vec.getZ() * delta);
bb.maxX = Math.max(bb.maxX - x * axisDirection.getOffset(), bb.minX);
bb.maxY = Math.max(bb.maxY - y * axisDirection.getOffset(), bb.minY);
bb.maxZ = Math.max(bb.maxZ - z * axisDirection.getOffset(), bb.minZ);
AxisDirection axisDirection = selectedFace.getAxisDirection();
if (axisDirection == AxisDirection.NEGATIVE)
bb = bb.offset(-x, -y, -z);
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", bb.getXSize(),
bb.getYSize(), bb.getZSize());
}
double maxX = Math.max(bb.maxX - x * axisDirection.getOffset(), bb.minX);
double maxY = Math.max(bb.maxY - y * axisDirection.getOffset(), bb.minY);
double maxZ = Math.max(bb.maxZ - z * axisDirection.getOffset(), bb.minZ);
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;
}
@ -106,8 +104,7 @@ public class SchematicAndQuillHandler {
}
if (secondPos != null) {
TextInputPromptScreen guiScreenIn = new TextInputPromptScreen(this::saveSchematic, s -> {
});
TextInputPromptScreen guiScreenIn = new TextInputPromptScreen(this::saveSchematic, s -> {});
guiScreenIn.setTitle(Lang.translate("schematicAndQuill.prompt"));
guiScreenIn.setButtonTextConfirm(Lang.translate("action.saveToFile"));
guiScreenIn.setButtonTextAbort(Lang.translate("action.discard"));
@ -130,6 +127,100 @@ public class SchematicAndQuillHandler {
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) {
Template t = new Template();
MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos);
@ -160,115 +251,4 @@ public class SchematicAndQuillHandler {
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.item.ItemStack;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.gen.feature.template.PlacementSettings;
public class SchematicEditScreen extends AbstractSimiScreen {
@ -25,10 +27,10 @@ public class SchematicEditScreen extends AbstractSimiScreen {
private TextFieldWidget yInput;
private TextFieldWidget zInput;
private final List<String> rotationOptions = Lang.translatedOptions("schematic.rotation", "none", "cw90", "cw180",
"cw270");
private final List<String> mirrorOptions = Lang.translatedOptions("schematic.mirror", "none", "leftRight",
"frontBack");
private final List<String> rotationOptions =
Lang.translatedOptions("schematic.rotation", "none", "cw90", "cw180", "cw270");
private final List<String> mirrorOptions =
Lang.translatedOptions("schematic.mirror", "none", "leftRight", "frontBack");
private final String positionLabel = Lang.translate("schematic.position");
private final String rotationLabel = Lang.translate("schematic.rotation");
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, "");
zInput = new TextFieldWidget(font, x + 155, y + 32, 32, 10, "");
if (handler.deployed) {
xInput.setText("" + handler.anchor.getX());
yInput.setText("" + handler.anchor.getY());
zInput.setText("" + handler.anchor.getZ());
BlockPos anchor = handler.getTransformation().getAnchor();
if (handler.isDeployed()) {
xInput.setText("" + anchor.getX());
yInput.setText("" + anchor.getY());
zInput.setText("" + anchor.getZ());
} else {
BlockPos alt = minecraft.player.getPosition();
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();
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();
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, labelR, labelM, rotationArea, mirrorArea);
@ -127,8 +131,8 @@ public class SchematicEditScreen extends AbstractSimiScreen {
int y = guiTop;
ScreenResources.SCHEMATIC.draw(this, x, y);
font.drawStringWithShadow(handler.cachedSchematicName,
x + 103 - font.getStringWidth(handler.cachedSchematicName) / 2, y + 10, 0xDDEEFF);
font.drawStringWithShadow(handler.getCurrentSchematicName(),
x + 103 - font.getStringWidth(handler.getCurrentSchematicName()) / 2, y + 10, 0xDDEEFF);
font.drawString(positionLabel, x + 10, y + 32, ScreenResources.FONT_COLOR);
font.drawString(rotationLabel, x + 10, y + 52, ScreenResources.FONT_COLOR);
@ -152,10 +156,22 @@ public class SchematicEditScreen extends AbstractSimiScreen {
validCoords = false;
}
if (validCoords)
handler.moveTo(newLocation);
handler.setRotation(Rotation.values()[rotationArea.getState()]);
handler.setMirror(Mirror.values()[mirrorArea.getState()]);
PlacementSettings settings = new PlacementSettings();
settings.setRotation(Rotation.values()[rotationArea.getState()]);
settings.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;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys;
import com.simibubi.create.AllPackets;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.gui.ToolSelectionScreen;
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.outliner.AABBOutline;
import com.simibubi.create.modules.schematics.SchematicWorld;
import com.simibubi.create.modules.schematics.client.tools.Tools;
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.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
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.text.StringTextComponent;
import net.minecraft.world.gen.feature.template.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template;
public class SchematicHandler {
public Template cachedSchematic;
public String cachedSchematicName;
public PlacementSettings cachedSettings;
private String displayedSchematic;
private SchematicTransformation transformation;
private AxisAlignedBB bounds; // local space
private AABBOutline outline;
private boolean deployed;
private boolean active;
private Tools currentTool;
public BlockPos anchor;
public BlockPos size;
public boolean active;
public boolean deployed;
public int slot;
public ItemStack item;
private static final int SYNC_DELAY = 10;
private int syncCooldown;
private int activeHotbarSlot;
private ItemStack activeSchematicItem;
private final List<String> mirrors = Arrays.asList("none", "leftRight", "frontBack");
private final List<String> rotations = Arrays.asList("none", "cw90", "cw180", "cw270");
public Tools currentTool;
public ToolSelectionScreen selectionScreen;
public static final int SYNC_DELAY = 20;
public int syncCooldown;
private BlueprintHotbarOverlay overlay;
private SchematicRenderer renderer;
private SchematicHotbarSlotOverlay overlay;
private ToolSelectionScreen selectionScreen;
public SchematicHandler() {
overlay = new SchematicHotbarSlotOverlay();
renderer = new SchematicRenderer();
currentTool = Tools.Deploy;
overlay = new BlueprintHotbarOverlay();
selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), this::equip);
transformation = new SchematicTransformation();
}
public void tick() {
ClientPlayerEntity player = Minecraft.getInstance().player;
if (activeSchematicItem != null && transformation != null)
transformation.tick();
ItemStack stack = findBlueprintInHand(player);
if (stack == null) {
active = false;
syncCooldown = 0;
if (item != null && itemLost(player)) {
slot = 0;
item = null;
CreateClient.schematicHologram.setActive(false);
if (activeSchematicItem != null && itemLost(player)) {
activeHotbarSlot = 0;
activeSchematicItem = null;
renderer.setActive(false);
}
return;
}
// Newly equipped
if (!active || !stack.getTag().getString("File").equals(cachedSchematicName)) {
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 || !stack.getTag().getString("File").equals(displayedSchematic))
init(player, stack);
if (!active)
return;
renderer.tick();
if (syncCooldown > 0)
syncCooldown--;
if (syncCooldown == 1)
@ -108,22 +88,62 @@ public class SchematicHandler {
currentTool.getTool().updateSelection();
}
public void render() {
if (!active)
return;
if (Minecraft.getInstance().player.isSneaking())
private void init(ClientPlayerEntity player, ItemStack stack) {
loadSettings(stack);
displayedSchematic = stack.getTag().getString("File");
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;
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();
currentTool.getTool().renderTool();
transformation.applyGLTransformations();
renderer.render();
GlStateManager.disableCull();
if (active)
currentTool.getTool().renderToolLocal();
GlStateManager.enableCull();
GlStateManager.depthMask(true);
TessellatorHelper.cleanUpAfterDrawing();
GlStateManager.popMatrix();
}
public void renderOverlay() {
if (!active)
return;
if (item != null)
overlay.renderOn(slot);
if (activeSchematicItem != null)
overlay.renderOn(activeHotbarSlot);
currentTool.getTool().renderOverlay();
selectionScreen.renderPassive(Minecraft.getInstance().getRenderPartialTicks());
@ -148,7 +168,6 @@ public class SchematicHandler {
if (pressed && !selectionScreen.focused)
selectionScreen.focused = true;
if (!pressed && selectionScreen.focused) {
selectionScreen.focused = false;
selectionScreen.onClose();
@ -156,18 +175,15 @@ public class SchematicHandler {
}
public boolean mouseScrolled(double delta) {
if (!active)
return false;
if (Minecraft.getInstance().player.isSneaking())
if (!active || Minecraft.getInstance().player.isSneaking())
return false;
if (selectionScreen.focused) {
selectionScreen.cycle((int) delta);
return true;
}
if (AllKeys.ACTIVATE_TOOL.isPressed()) {
if (AllKeys.ACTIVATE_TOOL.isPressed())
return currentTool.getTool().handleMouseWheel(delta);
}
return false;
}
@ -178,16 +194,16 @@ public class SchematicHandler {
if (!stack.hasTag())
return null;
item = stack;
slot = player.inventory.currentItem;
activeSchematicItem = stack;
activeHotbarSlot = player.inventory.currentItem;
return stack;
}
private boolean itemLost(PlayerEntity player) {
for (int i = 0; i < PlayerInventory.getHotbarSize(); i++) {
if (!player.inventory.getStackInSlot(i).isItemEqual(item))
if (!player.inventory.getStackInSlot(i).isItemEqual(activeSchematicItem))
continue;
if (!ItemStack.areItemStackTagsEqual(player.inventory.getStackInSlot(i), item))
if (!ItemStack.areItemStackTagsEqual(player.inventory.getStackInSlot(i), activeSchematicItem))
continue;
return false;
}
@ -196,25 +212,20 @@ public class SchematicHandler {
public void markDirty() {
syncCooldown = SYNC_DELAY;
CreateClient.schematicHologram.setActive(false);
}
public void sync() {
message(Lang.translate("schematics.synchronizing"));
AllPackets.channel.sendToServer(new NbtPacket(item, slot));
if (activeSchematicItem == null)
return;
if (deployed) {
Template schematic = SchematicItem.getSchematic(item);
PlacementSettings settings = transformation.toSettings();
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))
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);
}
AllPackets.channel.sendToServer(new NbtPacket(activeSchematicItem, activeHotbarSlot));
}
public void equip(Tools tool) {
@ -224,149 +235,66 @@ public class SchematicHandler {
public void loadSettings(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag();
cachedSettings = new PlacementSettings();
cachedSettings.setRotation(Rotation.valueOf(tag.getString("Rotation")));
cachedSettings.setMirror(Mirror.valueOf(tag.getString("Mirror")));
BlockPos anchor = BlockPos.ZERO;
PlacementSettings settings = SchematicItem.getSettings(blueprint);
transformation = new SchematicTransformation();
deployed = tag.getBoolean("Deployed");
if (deployed)
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) {
Rotation r = cachedSettings.getRotation();
boolean rotationAt90s = r == Rotation.CLOCKWISE_90 || r == Rotation.COUNTERCLOCKWISE_90;
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())));
public void deploy() {
if (!deployed) {
List<Tools> tools = Tools.getTools(Minecraft.getInstance().player.isCreative());
selectionScreen = new ToolSelectionScreen(tools, this::equip);
}
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;
this.anchor = anchor;
item.getTag().putBoolean("Deployed", true);
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
markDirty();
setupRenderer();
}
public String getCurrentSchematicName() {
return displayedSchematic != null ? displayedSchematic : "-";
}
public void printInstantly() {
AllPackets.channel.sendToServer(new SchematicPlacePacket(item.copy()));
CompoundNBT nbt = item.getTag();
AllPackets.channel.sendToServer(new SchematicPlacePacket(activeSchematicItem.copy()));
CompoundNBT nbt = activeSchematicItem.getTag();
nbt.putBoolean("Deployed", false);
item.setTag(nbt);
CreateClient.schematicHologram.setActive(false);
activeSchematicItem.setTag(nbt);
renderer.setActive(false);
active = false;
markDirty();
}
public BlockPos getTransformedSize() {
BlockPos flipped = size;
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 AABBOutline getOutline() {
return outline;
}
public BlockPos getTransformedAnchor() {
BlockPos anchor = this.anchor;
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 boolean isActive() {
return active;
}
public BlockPos centerOfSchematic() {
BlockPos size = getTransformedSize();
BlockPos center = new BlockPos(size.getX() / 2, 0, size.getZ() / 2);
return anchor.add(center);
public AxisAlignedBB getBounds() {
return bounds;
}
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.gui.AbstractGui;
public class BlueprintHotbarOverlay extends AbstractGui {
public class SchematicHotbarSlotOverlay extends AbstractGui {
public void renderOn(int slot) {
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.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.MathHelper;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.util.math.Vec3d;
public class DeployTool extends PlacementToolBase {
@ -18,12 +24,11 @@ public class DeployTool extends PlacementToolBase {
@Override
public void updateSelection() {
if (schematicHandler.active && selectionRange == -1) {
selectionRange = (int) schematicHandler.size.manhattanDistance(BlockPos.ZERO) / 2;
if (schematicHandler.isActive() && selectionRange == -1) {
selectionRange = (int) (schematicHandler.getBounds().getCenter().length() / 2);
selectionRange = MathHelper.clamp(selectionRange, 1, 100);
}
selectIgnoreBlocks = AllKeys.ACTIVATE_TOOL.isPressed();
super.updateSelection();
}
@ -34,26 +39,29 @@ public class DeployTool extends PlacementToolBase {
if (selectedPos == null)
return;
BlockPos size = schematicHandler.getTransformedSize();
BlockPos min = selectedPos.add(Math.round(size.getX() * -.5f), 0, Math.round(size.getZ() * -.5f));
BlockPos max = min.add(size.getX(), size.getY(), size.getZ());
GlStateManager.pushMatrix();
RenderHelper.disableStandardItemLighting();
GlStateManager.enableBlend();
float pt = Minecraft.getInstance().getRenderPartialTicks();
double x = MathHelper.lerp(pt, lastChasingSelectedPos.x, chasingSelectedPos.x);
double y = MathHelper.lerp(pt, lastChasingSelectedPos.y, chasingSelectedPos.y);
double z = MathHelper.lerp(pt, lastChasingSelectedPos.z, chasingSelectedPos.z);
if (schematicHandler.deployed) {
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(schematicHandler.getTransformedSize()));
min = new BlockPos(bb.minX, bb.minY, bb.minZ);
max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
}
SchematicTransformation transformation = schematicHandler.getTransformation();
Vec3d center = schematicHandler.getBounds().getCenter();
Vec3d offset = transformation.getRotationOffset(true);
GlStateManager.lineWidth(2);
GlStateManager.color4f(.5f, .8f, 1, 1);
GlStateManager.disableTexture();
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, .8f, .9f, 1, 1);
GlStateManager.lineWidth(1);
GlStateManager.enableTexture();
if (schematicHandler.getBounds().getXSize() % 2 == 1 || schematicHandler.getBounds().getZSize() % 2 == 1)
GlStateManager.translated(.5f, 0, .5f);
GlStateManager.translated(x, y, z);
GlStateManager.rotated(transformation.getCurrentRotation(), 0, 1, 0);
GlStateManager.translated(-offset.x, 0, -offset.z);
GlStateManager.translated(-(center.x), 0, -(center.z));
schematicHandler.getOutline().setTextures(AllSpecialTextures.CHECKERED, null);
schematicHandler.getOutline().render(Tessellator.getInstance().getBuffer());
schematicHandler.getOutline().setTextures(null, null);
GlStateManager.popMatrix();
}
@Override
@ -72,10 +80,18 @@ public class DeployTool extends PlacementToolBase {
public boolean handleRightClick() {
if (selectedPos == null)
return super.handleRightClick();
Vec3d center = schematicHandler.getBounds().getCenter();
BlockPos target = selectedPos.add(-((int) center.x), 0, -((int) center.z));
BlockPos size = schematicHandler.getTransformedSize();
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;
}

View file

@ -1,11 +1,28 @@
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 {
@Override
public void init() {
super.init();
renderSelectedFace = true;
renderSelectedFace = false;
}
@Override
@ -23,17 +40,69 @@ public class FlipTool extends PlacementToolBase {
@Override
public void updateSelection() {
super.updateSelection();
if (!schematicSelected)
return;
renderSelectedFace = selectedFace.getAxis().isHorizontal();
}
private void mirror() {
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 renderOverlay();
public void renderToolLocal();
}

View file

@ -1,5 +1,11 @@
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 {
@Override
@ -20,10 +26,16 @@ public class MoveTool extends PlacementToolBase {
@Override
public boolean handleMouseWheel(double delta) {
if (schematicSelected && selectedFace.getAxis().isHorizontal()) {
schematicHandler.moveTo(delta < 0 ? schematicHandler.anchor.add(selectedFace.getDirectionVec())
: schematicHandler.anchor.subtract(selectedFace.getDirectionVec()));
}
if (!schematicSelected || !selectedFace.getAxis().isHorizontal())
return true;
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;
}

View file

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

View file

@ -1,13 +1,47 @@
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 {
@Override
public boolean handleMouseWheel(double delta) {
schematicHandler.rotate(delta > 0 ? Rotation.CLOCKWISE_90 : Rotation.COUNTERCLOCKWISE_90);
schematicHandler.getTransformation().rotate90(delta > 0);
schematicHandler.markDirty();
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;
import java.util.Arrays;
import java.util.List;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllKeys;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.utility.RaycastHelper;
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.SchematicTransformation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemUseContext;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
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.BlockRayTraceResult;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d;
@ -24,13 +29,18 @@ public abstract class SchematicToolBase implements ISchematicTool {
protected SchematicHandler schematicHandler;
public BlockPos selectedPos;
public boolean selectIgnoreBlocks;
public int selectionRange;
protected BlockPos selectedPos;
protected Vec3d chasingSelectedPos;
protected Vec3d lastChasingSelectedPos;
public boolean schematicSelected;
public boolean renderSelectedFace;
public Direction selectedFace;
protected boolean selectIgnoreBlocks;
protected int selectionRange;
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
public void init() {
@ -38,86 +48,95 @@ public abstract class SchematicToolBase implements ISchematicTool {
selectedPos = null;
selectedFace = null;
schematicSelected = false;
chasingSelectedPos = Vec3d.ZERO;
lastChasingSelectedPos = Vec3d.ZERO;
}
@Override
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;
// Select Blueprint
if (schematicHandler.deployed) {
BlockPos min = schematicHandler.getTransformedAnchor();
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(schematicHandler.getTransformedSize()));
PredicateTraceResult result = RaycastHelper.rayTraceUntil(player, 70,
pos -> bb.isVecInside(pos));
if (schematicHandler.isDeployed()) {
SchematicTransformation transformation = schematicHandler.getTransformation();
AxisAlignedBB localBounds = schematicHandler.getBounds();
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();
selectedFace = schematicSelected ? result.getFacing() : null;
}
boolean snap = this.selectedPos == null;
// Select location at distance
if (selectIgnoreBlocks) {
selectedPos = new BlockPos(player.getEyePosition(Minecraft.getInstance().getRenderPartialTicks())
.add(player.getLookVec().scale(selectionRange)));
float pt = Minecraft.getInstance().getRenderPartialTicks();
selectedPos = new BlockPos(player.getEyePosition(pt).add(player.getLookVec().scale(selectionRange)));
if (snap)
lastChasingSelectedPos = chasingSelectedPos = new Vec3d(selectedPos);
return;
}
// Select targeted Block
selectedPos = null;
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());
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;
}
BlockPos hit = new BlockPos(trace.getHitVec());
boolean replaceable = player.world.getBlockState(hit).getMaterial().isReplaceable();
if (trace.getFace().getAxis().isVertical() && !replaceable)
hit = hit.offset(trace.getFace());
selectedPos = hit;
if (snap)
lastChasingSelectedPos = chasingSelectedPos = new Vec3d(selectedPos);
}
@Override
public void renderTool() {
if (schematicHandler.deployed) {
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();
public void renderToolLocal() {
if (!schematicHandler.isDeployed())
return;
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
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) {
CompoundNBT tag = blueprint.getTag();
Template t = getSchematic(blueprint);
Template t = loadSchematic(blueprint);
tag.put("Bounds", NBTUtil.writeBlockPos(t.getSize()));
blueprint.setTag(tag);
}
public static PlacementSettings getSettings(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag();
PlacementSettings settings = new PlacementSettings();
settings.setRotation(Rotation.valueOf(tag.getString("Rotation")));
settings.setMirror(Mirror.valueOf(tag.getString("Mirror")));
return settings;
}
public static Template getSchematic(ItemStack blueprint) {
public static Template loadSchematic(ItemStack blueprint) {
Template t = new Template();
String owner = blueprint.getTag().getString("Owner");
String schematic = blueprint.getTag().getString("File");
@ -120,14 +118,25 @@ public class SchematicItem extends Item {
@Override
public ActionResultType onItemUse(ItemUseContext context) {
if (context.isPlacerSneaking() && context.getHand() == Hand.MAIN_HAND) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
displayBlueprintScreen();
});
return ActionResultType.SUCCESS;
}
if (!onItemUse(context.getPlayer(), context.getHand()))
return super.onItemUse(context);
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)
@ -135,17 +144,4 @@ public class SchematicItem extends Item {
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) {
context.get().enqueueWork(() -> {
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")),
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.condition2": "When Above a Mechanical 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_mixer.tooltip.behaviour3": "Starts to _compact_ _items_ in the basin whenever all necessary ingredients are present.",
"block.create.mechanical_press.tooltip.condition3": "When above Basin",
"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.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