Mechanical Bearing

- The mechanical bearing now rotates attached structures
This commit is contained in:
simibubi 2019-09-08 12:50:02 +02:00
parent fb35aa7e10
commit b7decf0d0f
7 changed files with 428 additions and 11 deletions

View file

@ -3,10 +3,133 @@ package com.simibubi.create.modules.contraptions.receivers.constructs;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
public class MechanicalBearingTileEntity extends KineticTileEntity {
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
public class MechanicalBearingTileEntity extends KineticTileEntity implements ITickableTileEntity {
protected RotationConstruct movingConstruct;
protected float angle;
protected boolean running;
protected boolean assembleNextTick;
public MechanicalBearingTileEntity() {
super(AllTileEntities.MECHANICAL_BEARING.type);
}
@Override
public AxisAlignedBB getRenderBoundingBox() {
return INFINITE_EXTENT_AABB;
}
@Override
public CompoundNBT write(CompoundNBT tag) {
tag.putBoolean("Running", running);
tag.putFloat("Angle", angle);
if (running)
tag.put("Construct", movingConstruct.writeNBT());
return super.write(tag);
}
@Override
public void read(CompoundNBT tag) {
running = tag.getBoolean("Running");
angle = tag.getFloat("Angle");
if (running)
movingConstruct = RotationConstruct.fromNBT(tag.getCompound("Construct"));
super.read(tag);
}
public float getInterpolatedAngle(float partialTicks) {
return MathHelper.lerp(partialTicks, angle, angle + getAngularSpeed());
}
@Override
public void onSpeedChanged() {
super.onSpeedChanged();
assembleNextTick = true;
}
public float getAngularSpeed() {
return speed / 2048;
}
public void assembleConstruct() {
Direction direction = getBlockState().get(BlockStateProperties.FACING);
// Collect Construct
movingConstruct = RotationConstruct.getAttachedForRotating(getWorld(), getPos(), direction);
if (movingConstruct == null)
return;
// Run
running = true;
angle = 0;
sendData();
for (BlockInfo info : movingConstruct.blocks.values()) {
getWorld().setBlockState(info.pos.add(pos), Blocks.AIR.getDefaultState(), 67);
}
}
public void disassembleConstruct() {
if (!running)
return;
for (BlockInfo block : movingConstruct.blocks.values()) {
BlockPos targetPos = block.pos.add(pos);
BlockState state = block.state;
for (Direction face : Direction.values())
state = state.updatePostPlacement(face, world.getBlockState(targetPos.offset(face)), world, targetPos,
targetPos.offset(face));
world.destroyBlock(targetPos, world.getBlockState(targetPos).getCollisionShape(world, targetPos).isEmpty());
getWorld().setBlockState(targetPos, state, 3);
TileEntity tileEntity = world.getTileEntity(targetPos);
if (tileEntity != null && block.nbt != null) {
((ChassisTileEntity) tileEntity).setRange(block.nbt.getInt("Range"));
}
}
running = false;
movingConstruct = null;
angle = 0;
sendData();
}
@Override
public void tick() {
if (!world.isRemote && assembleNextTick) {
assembleNextTick = false;
if (running) {
if (speed == 0 && (Math.abs(angle) < Math.PI / 4f || Math.abs(angle) > 7 * Math.PI / 4f)) {
disassembleConstruct();
}
return;
} else {
assembleConstruct();
}
return;
}
if (!running)
return;
float angularSpeed = getAngularSpeed();
float newAngle = angle + angularSpeed;
angle = (float) (newAngle % (2 * Math.PI));
}
}

View file

@ -1,22 +1,40 @@
package com.simibubi.create.modules.contraptions.receivers.constructs;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.lwjgl.opengl.GL11;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.base.IRotate;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
import net.minecraft.block.BlockState;
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.model.IBakedModel;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraftforge.client.model.animation.Animation;
import net.minecraftforge.client.model.data.EmptyModelData;
public class MechanicalBearingTileEntityRenderer extends KineticTileEntityRenderer {
protected static Cache<RotationConstruct, RotationConstructVertexBuffer> cachedConstructs;
@Override
public void renderTileEntityFast(KineticTileEntity te, double x, double y, double z, float partialTicks,
int destroyStage, BufferBuilder buffer) {
MechanicalBearingTileEntity bearingTe = (MechanicalBearingTileEntity) te;
final Direction facing = te.getBlockState().get(BlockStateProperties.FACING);
final BlockPos pos = te.getPos();
float time = Animation.getWorldTime(Minecraft.getInstance().world, partialTicks);
@ -33,9 +51,56 @@ public class MechanicalBearingTileEntityRenderer extends KineticTileEntityRender
angle += offset;
angle = angle / 180f * (float) Math.PI;
float interpolatedAngle = bearingTe.getInterpolatedAngle(partialTicks);
renderFromCache(buffer, shaftState, (float) x, (float) y, (float) z, pos, facing.getAxis(), angle);
renderFromCache(buffer, capState, (float) x, (float) y, (float) z, pos, facing.getAxis(), angle);
renderFromCache(buffer, capState, (float) x, (float) y, (float) z, pos, facing.getAxis(), interpolatedAngle);
if (!bearingTe.running)
return;
cacheConstructIfMissing(bearingTe.movingConstruct);
renderConstructFromCache(bearingTe.movingConstruct, bearingTe, x, y, z, partialTicks, buffer);
}
protected void cacheConstructIfMissing(RotationConstruct c) {
if (cachedConstructs == null)
cachedConstructs = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.SECONDS).build();
if (cachedConstructs.getIfPresent(c) != null)
return;
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
BlockModelRenderer blockRenderer = dispatcher.getBlockModelRenderer();
Random random = new Random();
BufferBuilder builder = new BufferBuilder(0);
builder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
builder.setTranslation(0, 255, 0);
for (BlockPos localPos : c.blocks.keySet()) {
BlockInfo info = c.blocks.get(localPos);
IBakedModel originalModel = dispatcher.getModelForState(info.state);
blockRenderer.renderModel(getWorld(), originalModel, info.state, info.pos.down(255), builder, true, random,
42, EmptyModelData.INSTANCE);
}
builder.finishDrawing();
cachedConstructs.put(c, new RotationConstructVertexBuffer(builder.getByteBuffer()));
}
protected void renderConstructFromCache(RotationConstruct c, MechanicalBearingTileEntity te, double x, double y,
double z, float partialTicks, BufferBuilder buffer) {
float zfightBonus = 1 / 128f;
Direction direction = te.getBlockState().get(BlockStateProperties.FACING);
Vec3i vec = direction.getDirectionVec();
buffer.putBulkData(cachedConstructs.getIfPresent(c).getTransformed(te, (float) (x) + vec.getX() * zfightBonus,
(float) (y) + vec.getY() * zfightBonus, (float) (z) + vec.getZ() * zfightBonus,
te.getInterpolatedAngle(partialTicks), direction.getAxis()));
}
@Override
protected BlockState getRenderedBlockState(KineticTileEntity te) {
return AllBlocks.SHAFT.block.getDefaultState().with(BlockStateProperties.AXIS,
((IRotate) te.getBlockState().getBlock()).getRotationAxis(te.getBlockState()));
}
}

View file

@ -35,11 +35,6 @@ public class MechanicalPistonTileEntity extends KineticTileEntity implements ITi
super(AllTileEntities.MECHANICAL_PISTON.type);
}
@Override
public boolean hasFastRenderer() {
return true;
}
@Override
public void onSpeedChanged() {
super.onSpeedChanged();

View file

@ -26,7 +26,7 @@ import net.minecraftforge.client.model.data.EmptyModelData;
public class MechanicalPistonTileEntityRenderer extends KineticTileEntityRenderer {
protected static Cache<TranslationConstruct, ConstructVertexBuffer> cachedConstructs;
protected static Cache<TranslationConstruct, TranslationConstructVertexBuffer> cachedConstructs;
@Override
public void renderTileEntityFast(KineticTileEntity te, double x, double y, double z, float partialTicks,
@ -64,7 +64,7 @@ public class MechanicalPistonTileEntityRenderer extends KineticTileEntityRendere
}
builder.finishDrawing();
cachedConstructs.put(c, new ConstructVertexBuffer(builder.getByteBuffer()));
cachedConstructs.put(c, new TranslationConstructVertexBuffer(builder.getByteBuffer()));
}
protected void renderConstructFromCache(TranslationConstruct c, MechanicalPistonTileEntity te, double x, double y, double z,

View file

@ -0,0 +1,192 @@
package com.simibubi.create.modules.contraptions.receivers.constructs;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.simibubi.create.AllBlocks;
import net.minecraft.block.BlockState;
import net.minecraft.block.PistonBlock;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
public class RotationConstruct {
public static final int MAX_CHAINED_CHASSIS = 10;
protected Map<BlockPos, BlockInfo> blocks;
public RotationConstruct() {
blocks = new HashMap<>();
}
public static RotationConstruct getAttachedForRotating(World world, BlockPos pos, Direction direction) {
RotationConstruct construct = new RotationConstruct();
if (!construct.collectAttached(world, pos, direction))
return null;
return construct;
}
protected boolean collectAttached(World world, BlockPos pos, Direction direction) {
// Find chassis
List<BlockInfo> chassis = collectChassis(world, pos, direction);
if (chassis == null)
return false;
// Get single block
if (chassis.isEmpty()) {
BlockPos blockPos = pos.offset(direction);
BlockState state = world.getBlockState(pos.offset(direction));
if (state.getMaterial().isReplaceable() || state.isAir(world, blockPos))
return true;
if (state.getCollisionShape(world, blockPos).isEmpty())
return true;
if (!canRotate(world, blockPos, direction))
return false;
blocks.put(blockPos, new BlockInfo(blockPos.subtract(pos), state, null));
// Get attached blocks by chassis
} else {
List<BlockInfo> attachedBlocksByChassis = getAttachedBlocksByChassis(world, direction, chassis);
if (attachedBlocksByChassis == null)
return false;
attachedBlocksByChassis.forEach(info -> {
blocks.put(info.pos, new BlockInfo(info.pos.subtract(pos), info.state, info.nbt));
});
}
return true;
}
private List<BlockInfo> getAttachedBlocksByChassis(World world, Direction direction, List<BlockInfo> chassis) {
List<BlockInfo> blocks = new ArrayList<>();
RotationChassisBlock def = (RotationChassisBlock) AllBlocks.ROTATION_CHASSIS.block;
for (BlockInfo chassisBlock : chassis) {
blocks.add(chassisBlock);
BlockState state = chassisBlock.state;
BlockPos currentPos = chassisBlock.pos;
TileEntity tileEntity = world.getTileEntity(currentPos);
if (!(tileEntity instanceof ChassisTileEntity))
return null;
int chassisRange = ((ChassisTileEntity) tileEntity).getRange();
Set<BlockPos> visited = new HashSet<>();
for (Direction facing : Direction.values()) {
if (facing.getAxis() == direction.getAxis())
continue;
if (!state.get(def.getGlueableSide(state, facing)))
continue;
BlockPos startPos = currentPos.offset(facing);
List<BlockPos> frontier = new LinkedList<>();
frontier.add(startPos);
CompoundNBT nbt = new CompoundNBT();
nbt.putInt("Range", chassisRange);
while (!frontier.isEmpty()) {
BlockPos searchPos = frontier.remove(0);
BlockState searchedState = world.getBlockState(searchPos);
if (visited.contains(searchPos))
continue;
if (!searchPos.withinDistance(currentPos, chassisRange + .5f))
continue;
if (searchedState.getMaterial().isReplaceable() || state.isAir(world, searchPos))
continue;
if (searchedState.getCollisionShape(world, searchPos).isEmpty())
continue;
if (!canRotate(world, searchPos, direction))
return null;
visited.add(searchPos);
blocks.add(new BlockInfo(searchPos, searchedState,
AllBlocks.ROTATION_CHASSIS.typeOf(searchedState) ? nbt : null));
for (Direction offset : Direction.values()) {
if (offset.getAxis() == direction.getAxis())
continue;
if (searchPos.equals(currentPos) && offset != facing)
continue;
frontier.add(searchPos.offset(offset));
}
}
}
}
return blocks;
}
private List<BlockInfo> collectChassis(World world, BlockPos pos, Direction direction) {
List<BlockInfo> chassis = new ArrayList<>();
for (int distance = 1; distance <= MAX_CHAINED_CHASSIS; distance++) {
BlockPos currentPos = pos.offset(direction, distance);
if (!world.isBlockPresent(currentPos))
return chassis;
BlockState state = world.getBlockState(currentPos);
if (!AllBlocks.ROTATION_CHASSIS.typeOf(state))
return chassis;
if (direction.getAxis() != state.get(BlockStateProperties.AXIS))
return chassis;
chassis.add(new BlockInfo(currentPos, state, null));
}
return chassis;
}
public CompoundNBT writeNBT() {
CompoundNBT nbt = new CompoundNBT();
ListNBT blocks = new ListNBT();
for (BlockInfo block : this.blocks.values()) {
CompoundNBT c = new CompoundNBT();
c.put("Block", NBTUtil.writeBlockState(block.state));
c.put("Pos", NBTUtil.writeBlockPos(block.pos));
if (block.nbt != null)
c.put("Data", block.nbt);
blocks.add(c);
}
nbt.put("Blocks", blocks);
return nbt;
}
public static RotationConstruct fromNBT(CompoundNBT nbt) {
RotationConstruct construct = new RotationConstruct();
nbt.getList("Blocks", 10).forEach(c -> {
CompoundNBT comp = (CompoundNBT) c;
BlockInfo info = new BlockInfo(NBTUtil.readBlockPos(comp.getCompound("Pos")),
NBTUtil.readBlockState(comp.getCompound("Block")),
comp.contains("Data") ? comp.getCompound("Data") : null);
construct.blocks.put(info.pos, info);
});
return construct;
}
private static boolean canRotate(World world, BlockPos pos, Direction direction) {
return PistonBlock.canPush(world.getBlockState(pos), world, pos, direction, true, direction)
|| AllBlocks.ROTATION_CHASSIS.typeOf(world.getBlockState(pos));
}
}

View file

@ -0,0 +1,42 @@
package com.simibubi.create.modules.contraptions.receivers.constructs;
import java.nio.ByteBuffer;
import com.simibubi.create.foundation.utility.BufferManipulator;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
public class RotationConstructVertexBuffer extends BufferManipulator {
public RotationConstructVertexBuffer(ByteBuffer original) {
super(original);
}
public ByteBuffer getTransformed(TileEntity te, float x, float y, float z, float angle, Axis axis) {
original.rewind();
mutable.rewind();
float cos = MathHelper.cos(angle);
float sin = MathHelper.sin(angle);
for (int vertex = 0; vertex < vertexCount(original); vertex++) {
float xL = getX(original, vertex) -.5f;
float yL = getY(original, vertex) -.5f;
float zL = getZ(original, vertex) -.5f;
float xL2 = rotateX(xL, yL, zL, sin, cos, axis) + .5f;
float yL2 = rotateY(xL, yL, zL, sin, cos, axis) + .5f;
float zL2 = rotateZ(xL, yL, zL, sin, cos, axis) + .5f;
putPos(mutable, vertex, xL2 + x, yL2 + y, zL2 + z);
BlockPos pos = new BlockPos(te.getPos().getX() + xL2, te.getPos().getY() + yL2, te.getPos().getZ() + zL2);
putLight(mutable, vertex, te.getWorld().getCombinedLight(pos, 0));
}
return mutable;
}
}

View file

@ -8,9 +8,9 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public class ConstructVertexBuffer extends BufferManipulator {
public class TranslationConstructVertexBuffer extends BufferManipulator {
public ConstructVertexBuffer(ByteBuffer original) {
public TranslationConstructVertexBuffer(ByteBuffer original) {
super(original);
}