From 476e131ab3bb7a8cdf6d83d8c248c52a2a36a867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 25 Oct 2020 18:39:28 +0100 Subject: [PATCH] Add tile-entity renderer for computer. --- .../java/li/cil/oc2/client/ClientSetup.java | 4 + .../render/OpenComputersRenderType.java | 29 ++++ .../cil/oc2/client/render/package-info.java | 7 + .../tile/ComputerTileEntityRenderer.java | 143 ++++++++++++++++++ .../oc2/client/render/tile/package-info.java | 7 + 5 files changed, 190 insertions(+) create mode 100644 src/main/java/li/cil/oc2/client/render/OpenComputersRenderType.java create mode 100644 src/main/java/li/cil/oc2/client/render/package-info.java create mode 100644 src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java create mode 100644 src/main/java/li/cil/oc2/client/render/tile/package-info.java diff --git a/src/main/java/li/cil/oc2/client/ClientSetup.java b/src/main/java/li/cil/oc2/client/ClientSetup.java index 4dd7c14c..5e412f70 100644 --- a/src/main/java/li/cil/oc2/client/ClientSetup.java +++ b/src/main/java/li/cil/oc2/client/ClientSetup.java @@ -2,11 +2,15 @@ package li.cil.oc2.client; import li.cil.oc2.OpenComputers; import li.cil.oc2.client.gui.ComputerContainerScreen; +import li.cil.oc2.client.render.tile.ComputerTileEntityRenderer; import net.minecraft.client.gui.ScreenManager; +import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; public final class ClientSetup { public static void run(final FMLClientSetupEvent event) { ScreenManager.registerFactory(OpenComputers.COMPUTER_CONTAINER.get(), ComputerContainerScreen::new); + + ClientRegistry.bindTileEntityRenderer(OpenComputers.COMPUTER_TILE_ENTITY.get(), ComputerTileEntityRenderer::new); } } diff --git a/src/main/java/li/cil/oc2/client/render/OpenComputersRenderType.java b/src/main/java/li/cil/oc2/client/render/OpenComputersRenderType.java new file mode 100644 index 00000000..0c03344f --- /dev/null +++ b/src/main/java/li/cil/oc2/client/render/OpenComputersRenderType.java @@ -0,0 +1,29 @@ +package li.cil.oc2.client.render; + +import li.cil.oc2.api.API; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.opengl.GL11; + +public abstract class OpenComputersRenderType extends RenderType { + public static RenderType getUnlitBlock(final ResourceLocation location) { + final TextureState texture = new TextureState(location, false, true); + final RenderType.State state = RenderType.State.getBuilder() + .texture(texture) + .writeMask(COLOR_WRITE) + .transparency(ADDITIVE_TRANSPARENCY) + .build(false); + return RenderType.makeType( + API.MOD_ID + ":unlit_block", + DefaultVertexFormats.POSITION_TEX, + GL11.GL_QUADS, + 256, + state); + } + + private OpenComputersRenderType(final String name, final VertexFormat format, final int drawMode, final int bufferSize, final boolean useDelegate, final boolean needsSorting, final Runnable setupTask, final Runnable clearTask) { + super(name, format, drawMode, bufferSize, useDelegate, needsSorting, setupTask, clearTask); + } +} diff --git a/src/main/java/li/cil/oc2/client/render/package-info.java b/src/main/java/li/cil/oc2/client/render/package-info.java new file mode 100644 index 00000000..f47bcf05 --- /dev/null +++ b/src/main/java/li/cil/oc2/client/render/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package li.cil.oc2.client.render; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java b/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java new file mode 100644 index 00000000..65a874ed --- /dev/null +++ b/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java @@ -0,0 +1,143 @@ +package li.cil.oc2.client.render.tile; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.vertex.IVertexBuilder; +import li.cil.oc2.api.API; +import li.cil.oc2.client.gui.terminal.Terminal; +import li.cil.oc2.client.render.OpenComputersRenderType; +import li.cil.oc2.common.block.ComputerBlock; +import li.cil.oc2.common.tile.ComputerTileEntity; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.Matrix4f; +import net.minecraft.client.renderer.Quaternion; +import net.minecraft.client.renderer.Vector3f; +import net.minecraft.client.renderer.model.Material; +import net.minecraft.client.renderer.tileentity.TileEntityRenderer; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.inventory.container.PlayerContainer; +import net.minecraft.util.Direction; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) +public final class ComputerTileEntityRenderer extends TileEntityRenderer { + private static final ResourceLocation OVERLAY_POWER_LOCATION = new ResourceLocation(API.MOD_ID, "blocks/computer/computer_overlay_power"); + private static final ResourceLocation OVERLAY_STATUS_LOCATION = new ResourceLocation(API.MOD_ID, "blocks/computer/computer_overlay_status"); + private static final ResourceLocation OVERLAY_TERMINAL_LOCATION = new ResourceLocation(API.MOD_ID, "blocks/computer/computer_overlay_terminal"); + + private static final Material TEXTURE_POWER = new Material(PlayerContainer.LOCATION_BLOCKS_TEXTURE, OVERLAY_POWER_LOCATION); + private static final Material TEXTURE_STATUS = new Material(PlayerContainer.LOCATION_BLOCKS_TEXTURE, OVERLAY_STATUS_LOCATION); + private static final Material TEXTURE_TERMINAL = new Material(PlayerContainer.LOCATION_BLOCKS_TEXTURE, OVERLAY_TERMINAL_LOCATION); + + @SubscribeEvent + public static void handleTextureStitchEvent(final TextureStitchEvent.Pre event) { + event.addSprite(OVERLAY_POWER_LOCATION); + event.addSprite(OVERLAY_STATUS_LOCATION); + event.addSprite(OVERLAY_TERMINAL_LOCATION); + } + + public ComputerTileEntityRenderer(final TileEntityRendererDispatcher dispatcher) { + super(dispatcher); + } + + @Override + public void render(final ComputerTileEntity tileEntity, final float partialTicks, final MatrixStack stack, final IRenderTypeBuffer buffer, final int combinedLightIn, final int combinedOverlayIn) { + final Direction blockFacing = tileEntity.getBlockState().get(ComputerBlock.FACING); + final Vec3d cameraPosition = renderDispatcher.renderInfo.getRenderViewEntity().getEyePosition(partialTicks); + + // If viewer is not in front of the block we can skip all of the rest, it cannot be visible. + // We check against the center of the block instead of the actual relevant face for simplicity. + final Vec3d relativeCameraPosition = cameraPosition.subtract(new Vec3d(tileEntity.getPos()).add(0.5, 0.5, 0.5)); + final double projectedCameraPosition = relativeCameraPosition.dotProduct(new Vec3d(blockFacing.getDirectionVec())); + if (projectedCameraPosition <= 0) { + return; + } + + stack.push(); + + // Align with front face of block. + final Quaternion rotation = new Quaternion(Vector3f.YN, blockFacing.getHorizontalAngle() + 180, true); + stack.translate(0.5f, 0, 0.5f); + stack.rotate(rotation); + stack.translate(-0.5f, 0, -0.5f); + + // Flip and align with top left corner. + stack.translate(1, 1, 0); + stack.scale(-1, -1, -1); + + // Scale to make 1/16th of the block one unit and align with top left of terminal area. + final float pixelScale = 1 / 16f; + stack.scale(pixelScale, pixelScale, pixelScale); + + // Render terminal content if close enough. + if (tileEntity.getDistanceSq(cameraPosition.x, cameraPosition.y, cameraPosition.z) < 6f * 6f) { + stack.push(); + stack.translate(2, 2, -0.9f); + + // Scale to make terminal fit fully. + final Terminal terminal = tileEntity.getTerminal(); + final float textScaleX = 12f / terminal.getWidth(); + final float textScaleY = 7f / terminal.getHeight(); + final float scale = Math.min(textScaleX, textScaleY) * 0.95f; + + // Center it on both axes. + final float scaleDeltaX = textScaleX - scale; + final float scaleDeltaY = textScaleY - scale; + stack.translate( + terminal.getWidth() * scaleDeltaX * 0.5f, + terminal.getHeight() * scaleDeltaY * 0.5f, + 0f); + + stack.scale(scale, scale, 1f); + + // TODO Make terminal renderer use buffer+rendertype. + GlStateManager.enableBlend(); + GlStateManager.enableDepthTest(); + GlStateManager.depthMask(false); + terminal.render(stack); + + stack.pop(); + } else { + stack.push(); + stack.translate(0, 0, -0.9f); + + final Matrix4f matrix = stack.getLast().getMatrix(); + drawQuad(matrix, TEXTURE_TERMINAL.getBuffer(buffer, OpenComputersRenderType::getUnlitBlock)); + + stack.pop(); + } + + stack.translate(0, 0, -0.1f); + final Matrix4f matrix = stack.getLast().getMatrix(); + + // TODO Sync power state and status change and check if LEDs should be on. +// drawQuad(matrix, TEXTURE_STATUS.getBuffer(buffer, OpenComputersRenderState::getEmissiveBlock)); + drawQuad(matrix, TEXTURE_POWER.getBuffer(buffer, OpenComputersRenderType::getUnlitBlock)); + + stack.pop(); + } + + private static void drawQuad(final Matrix4f matrix, final IVertexBuilder buffer) { + // NB: We may get a SpriteAwareVertexBuilder here. Sadly, its chaining is broken, + // because methods may return the underlying vertex builder, so e.g. calling + // buffer.pos(...).tex(...) will not actually call SpriteAwareVertexBuilder.tex(...) + // but SpriteAwareVertexBuilder.vertexBuilder.tex(...), skipping the UV remapping. + buffer.pos(matrix, 0, 0, 0); + buffer.tex(0, 0); + buffer.endVertex(); + buffer.pos(matrix, 0, 16, 0); + buffer.tex(0, 1); + buffer.endVertex(); + buffer.pos(matrix, 16, 16, 0); + buffer.tex(1, 1); + buffer.endVertex(); + buffer.pos(matrix, 16, 0, 0); + buffer.tex(1, 0); + buffer.endVertex(); + } +} diff --git a/src/main/java/li/cil/oc2/client/render/tile/package-info.java b/src/main/java/li/cil/oc2/client/render/tile/package-info.java new file mode 100644 index 00000000..a941fc47 --- /dev/null +++ b/src/main/java/li/cil/oc2/client/render/tile/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package li.cil.oc2.client.render.tile; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file