terminal screen no worky
parent
44b600a835
commit
b9be0549c9
@ -0,0 +1,93 @@
|
|||||||
|
package net.brokenmoon.redcontrol
|
||||||
|
|
||||||
|
import net.brokenmoon.redcontrol.RedControl.modloc
|
||||||
|
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener
|
||||||
|
import net.fabricmc.fabric.api.resource.ResourceManagerHelper
|
||||||
|
import net.minecraft.resource.ResourceManager
|
||||||
|
import net.minecraft.resource.ResourceReloader.Synchronizer
|
||||||
|
import net.minecraft.resource.ResourceType
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.profiler.Profiler
|
||||||
|
import org.lwjgl.opengl.GL11
|
||||||
|
import org.lwjgl.opengl.GL30
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
|
object Shaders {
|
||||||
|
|
||||||
|
private var screen = 0
|
||||||
|
|
||||||
|
fun screen() = screen
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(object : IdentifiableResourceReloadListener {
|
||||||
|
|
||||||
|
override fun reload(s: Synchronizer, rm: ResourceManager, profiler: Profiler, profiler1: Profiler, executor: Executor, executor1: Executor): CompletableFuture<Void> {
|
||||||
|
return CompletableFuture.runAsync({
|
||||||
|
if (screen != 0) GL30.glDeleteProgram(screen)
|
||||||
|
|
||||||
|
screen = loadShader(rm, "screen")
|
||||||
|
}, executor1).thenCompose { s.whenPrepared(null) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFabricId(): Identifier = modloc("shaders")
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadShader(rm: ResourceManager, id: String): Int {
|
||||||
|
val vshs = rm.getResource(modloc("shaders/$id.vert")).getOrNull()?.inputStream?.bufferedReader()?.readText()
|
||||||
|
val fshs = rm.getResource(modloc("shaders/$id.frag")).getOrNull()?.inputStream?.bufferedReader()?.readText()
|
||||||
|
|
||||||
|
val vsh = GL30.glCreateShader(GL30.GL_VERTEX_SHADER)
|
||||||
|
val fsh = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER)
|
||||||
|
val prog = GL30.glCreateProgram()
|
||||||
|
|
||||||
|
// No goto? I'll make my own.
|
||||||
|
run {
|
||||||
|
GL30.glShaderSource(vsh, vshs)
|
||||||
|
GL30.glShaderSource(fsh, fshs)
|
||||||
|
|
||||||
|
GL30.glCompileShader(vsh)
|
||||||
|
if (GL30.glGetShaderi(vsh, GL30.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||||
|
// TODO use logger
|
||||||
|
val log = GL30.glGetShaderInfoLog(vsh, 32768)
|
||||||
|
println("Failed to compile vertex shader '$id'")
|
||||||
|
for (line in log.lineSequence()) println(line)
|
||||||
|
return@run
|
||||||
|
}
|
||||||
|
|
||||||
|
GL30.glCompileShader(fsh)
|
||||||
|
if (GL30.glGetShaderi(fsh, GL30.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||||
|
// TODO use logger
|
||||||
|
val log = GL30.glGetShaderInfoLog(fsh, 32768)
|
||||||
|
println("Failed to compile fragment shader '$id'")
|
||||||
|
for (line in log.lineSequence()) println(line)
|
||||||
|
return@run
|
||||||
|
}
|
||||||
|
|
||||||
|
GL30.glAttachShader(prog, vsh)
|
||||||
|
GL30.glAttachShader(prog, fsh)
|
||||||
|
GL30.glLinkProgram(prog)
|
||||||
|
|
||||||
|
if (GL30.glGetProgrami(prog, GL30.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||||
|
// TODO use logger
|
||||||
|
val log = GL30.glGetProgramInfoLog(prog, 32768)
|
||||||
|
println("Failed to link program '$id'")
|
||||||
|
for (line in log.lineSequence()) println(line)
|
||||||
|
return@run
|
||||||
|
}
|
||||||
|
|
||||||
|
GL30.glDeleteShader(vsh)
|
||||||
|
GL30.glDeleteShader(fsh)
|
||||||
|
return prog
|
||||||
|
}
|
||||||
|
|
||||||
|
GL30.glDeleteShader(vsh)
|
||||||
|
GL30.glDeleteShader(fsh)
|
||||||
|
GL30.glDeleteProgram(prog)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package net.brokenmoon.redcontrol.screen;
|
|
||||||
|
|
||||||
import net.brokenmoon.redcontrol.RedControl;
|
|
||||||
import net.minecraft.client.gui.DrawContext;
|
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
|
|
||||||
public class MonitorScreen extends Screen {
|
|
||||||
Identifier screenTexture = new Identifier("redcontrol", "gui/display.png");
|
|
||||||
public MonitorScreen(Text title) {
|
|
||||||
super(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init() {
|
|
||||||
super.init();
|
|
||||||
RedControl.LOGGER.info("Opened Monitor Screen");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
super.render(context, mouseX, mouseY, delta);
|
|
||||||
context.drawTexture(screenTexture,0,0,0,0,350,230);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldPause() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,261 @@
|
|||||||
|
package net.brokenmoon.redcontrol.screen
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.GlStateManager
|
||||||
|
import com.mojang.blaze3d.platform.TextureUtil
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
import io.netty.buffer.Unpooled
|
||||||
|
import net.brokenmoon.redcontrol.RedControlNetworking
|
||||||
|
import net.brokenmoon.redcontrol.Shaders
|
||||||
|
import net.brokenmoon.redcontrol.blocks.TerminalEntity
|
||||||
|
import net.dblsaiko.qcommon.croco.Mat4
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import net.minecraft.client.gl.Framebuffer
|
||||||
|
import net.minecraft.client.gl.SimpleFramebuffer
|
||||||
|
import net.minecraft.client.gui.DrawContext
|
||||||
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
import net.minecraft.client.render.VertexFormat.DrawMode
|
||||||
|
import net.minecraft.client.render.VertexFormats
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.util.math.Vec3d
|
||||||
|
import org.lwjgl.BufferUtils
|
||||||
|
import org.lwjgl.glfw.GLFW
|
||||||
|
import org.lwjgl.opengl.*
|
||||||
|
import org.lwjgl.opengl.GL11.GL_FLOAT
|
||||||
|
import org.lwjgl.opengl.GL11.GL_TRIANGLES
|
||||||
|
import kotlin.experimental.xor
|
||||||
|
import kotlin.math.round
|
||||||
|
|
||||||
|
private val buf = BufferUtils.createByteBuffer(16384)
|
||||||
|
|
||||||
|
private val vbo = GL30.glGenBuffers()
|
||||||
|
private val vao = GL30.glGenVertexArrays()
|
||||||
|
private val screenTex = createTexture()
|
||||||
|
private val charsetTex = createTexture()
|
||||||
|
|
||||||
|
class TerminalScreen(val te: TerminalEntity) : Screen(Text.translatable("block.redcontrol.terminal")) {
|
||||||
|
|
||||||
|
private var uMvp = 0
|
||||||
|
private var uCharset = 0
|
||||||
|
private var uScreen = 0
|
||||||
|
private var aXyz = 0
|
||||||
|
private var aUv = 0
|
||||||
|
|
||||||
|
private var fb: Framebuffer? = null
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
val minecraft = client ?: return
|
||||||
|
val dist = minecraft.player?.getCameraPosVec(1f)?.squaredDistanceTo(Vec3d.ofCenter(te.pos))
|
||||||
|
?: Double.POSITIVE_INFINITY
|
||||||
|
if (dist > 10 * 10) minecraft.setScreen(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(ctx: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
renderBackground(ctx,mouseX,mouseY,delta)
|
||||||
|
val matrices = ctx.matrices
|
||||||
|
val sh = Shaders.screen()
|
||||||
|
val fb = fb ?: return
|
||||||
|
val mc = client ?: return
|
||||||
|
|
||||||
|
fb.setTexFilter(if ((mc.window.scaleFactor.toInt() % 2) == 0) GL11.GL_NEAREST else GL11.GL_LINEAR)
|
||||||
|
|
||||||
|
fb.beginWrite(true)
|
||||||
|
val mat = Mat4.ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f)
|
||||||
|
|
||||||
|
GL30.glUseProgram(sh)
|
||||||
|
GL30.glBindVertexArray(vao)
|
||||||
|
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo)
|
||||||
|
|
||||||
|
GL20.glEnableVertexAttribArray(aXyz)
|
||||||
|
GL20.glEnableVertexAttribArray(aUv)
|
||||||
|
|
||||||
|
RenderSystem.activeTexture(GL13.GL_TEXTURE0)
|
||||||
|
//RenderSystem.enableTexture() //TODO: figure out why this does not exists
|
||||||
|
RenderSystem.bindTexture(screenTex)
|
||||||
|
|
||||||
|
buf.clear()
|
||||||
|
val fbuf = buf.asFloatBuffer()
|
||||||
|
mat.intoBuffer(fbuf)
|
||||||
|
fbuf.flip()
|
||||||
|
GL30.glUniformMatrix4fv(uMvp, false, fbuf)
|
||||||
|
|
||||||
|
GL30.glUniform1i(uScreen, 0)
|
||||||
|
|
||||||
|
buf.clear()
|
||||||
|
buf.put(te.screen)
|
||||||
|
|
||||||
|
if (te.cm == 1 || (te.cm == 2 && (System.currentTimeMillis() / 500) % 2 == 0L)) {
|
||||||
|
val ci = te.cx + te.cy * 80
|
||||||
|
buf.put(ci, te.screen[ci] xor 0x80.toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.rewind()
|
||||||
|
GlStateManager._texImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_R16I, 80, 60, 0, GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_BYTE, buf.asIntBuffer())
|
||||||
|
|
||||||
|
RenderSystem.activeTexture(GL13.GL_TEXTURE2)
|
||||||
|
//RenderSystem.enableTexture() //TODO: figure out why this does not exists
|
||||||
|
RenderSystem.bindTexture(charsetTex)
|
||||||
|
GL30.glUniform1i(uCharset, 2)
|
||||||
|
|
||||||
|
buf.clear()
|
||||||
|
buf.put(te.charset)
|
||||||
|
buf.rewind()
|
||||||
|
GlStateManager._texImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_R16I, 8, 256, 0, GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_BYTE, buf.asIntBuffer())
|
||||||
|
|
||||||
|
GL11.glDrawArrays(GL_TRIANGLES, 0, 6)
|
||||||
|
|
||||||
|
GL20.glDisableVertexAttribArray(aXyz)
|
||||||
|
GL20.glDisableVertexAttribArray(aUv)
|
||||||
|
|
||||||
|
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0)
|
||||||
|
GL30.glBindVertexArray(0)
|
||||||
|
GL30.glUseProgram(0)
|
||||||
|
|
||||||
|
RenderSystem.bindTexture(0)
|
||||||
|
//RenderSystem.disableTexture() //TODO: figure out why this does not exists
|
||||||
|
RenderSystem.activeTexture(GL13.GL_TEXTURE0)
|
||||||
|
RenderSystem.bindTexture(0)
|
||||||
|
|
||||||
|
mc.framebuffer.beginWrite(true)
|
||||||
|
|
||||||
|
val swidth = 8 * 80 * 0.5
|
||||||
|
val sheight = 8 * 50 * 0.5
|
||||||
|
val x1 = round(width / 2.0 - swidth / 2.0)
|
||||||
|
val y1 = round(height / 2.0 - sheight / 2.0)
|
||||||
|
|
||||||
|
matrices.push()
|
||||||
|
matrices.translate(x1, y1, -2000.0) // why the -2000? not sure
|
||||||
|
|
||||||
|
val shader = mc.gameRenderer.blitScreenProgram
|
||||||
|
shader.addSampler("DiffuseSampler", fb.colorAttachment)
|
||||||
|
shader.modelViewMat?.set(matrices.peek().positionMatrix)
|
||||||
|
shader.projectionMat?.set(RenderSystem.getProjectionMatrix())
|
||||||
|
shader.bind()
|
||||||
|
|
||||||
|
val t = RenderSystem.renderThreadTesselator()
|
||||||
|
val buf = t.buffer
|
||||||
|
buf.begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR)
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).texture(0f, 1f).color(255, 255, 255, 255).next()
|
||||||
|
buf.vertex(0.0, sheight, 0.0).texture(0f, 0f).color(255, 255, 255, 255).next()
|
||||||
|
buf.vertex(swidth, sheight, 0.0).texture(1f, 0f).color(255, 255, 255, 255).next()
|
||||||
|
buf.vertex(swidth, 0.0, 0.0).texture(1f, 1f).color(255, 255, 255, 255).next()
|
||||||
|
buf.end()
|
||||||
|
//BufferRenderer.postDraw(buf) //TODO: figure out why this is gone
|
||||||
|
|
||||||
|
shader.unbind()
|
||||||
|
|
||||||
|
matrices.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun keyPressed(key: Int, scancode: Int, modifiers: Int): Boolean {
|
||||||
|
if (super.keyPressed(key, scancode, modifiers)) return true
|
||||||
|
|
||||||
|
val result: Byte? = when (key) {
|
||||||
|
GLFW.GLFW_KEY_BACKSPACE -> 0x08
|
||||||
|
GLFW.GLFW_KEY_ENTER -> 0x0D
|
||||||
|
GLFW.GLFW_KEY_HOME -> 0x80
|
||||||
|
GLFW.GLFW_KEY_END -> 0x81
|
||||||
|
GLFW.GLFW_KEY_UP -> 0x82
|
||||||
|
GLFW.GLFW_KEY_DOWN -> 0x83
|
||||||
|
GLFW.GLFW_KEY_LEFT -> 0x84
|
||||||
|
GLFW.GLFW_KEY_RIGHT -> 0x85
|
||||||
|
else -> null
|
||||||
|
}?.toByte()
|
||||||
|
|
||||||
|
if (result != null) pushKey(result)
|
||||||
|
|
||||||
|
return result != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun charTyped(c: Char, modifiers: Int): Boolean {
|
||||||
|
if (super.charTyped(c, modifiers)) return true
|
||||||
|
|
||||||
|
val result: Byte? = when (c) {
|
||||||
|
in '\u0001'..'\u007F' -> c.code.toByte()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != null) pushKey(result)
|
||||||
|
|
||||||
|
return result != null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pushKey(c: Byte) {
|
||||||
|
val buffer = PacketByteBuf(Unpooled.buffer())
|
||||||
|
buffer.writeBlockPos(te.pos)
|
||||||
|
buffer.writeByte(c.toInt())
|
||||||
|
ClientPlayNetworking.send(RedControlNetworking.KEY_PRESS, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
//client!!.keyboard.setRepeatEvents(true) //TODO: figure out why this does not exists
|
||||||
|
|
||||||
|
initDrawData()
|
||||||
|
initFb()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initDrawData() {
|
||||||
|
val sh = Shaders.screen()
|
||||||
|
|
||||||
|
GL30.glUseProgram(sh)
|
||||||
|
GL30.glBindVertexArray(vao)
|
||||||
|
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo)
|
||||||
|
|
||||||
|
uMvp = GL30.glGetUniformLocation(sh, "mvp")
|
||||||
|
uCharset = GL30.glGetUniformLocation(sh, "charset")
|
||||||
|
uScreen = GL30.glGetUniformLocation(sh, "screen")
|
||||||
|
|
||||||
|
aXyz = GL30.glGetAttribLocation(sh, "xyz")
|
||||||
|
aUv = GL30.glGetAttribLocation(sh, "uv")
|
||||||
|
|
||||||
|
GL20.glVertexAttribPointer(aXyz, 3, GL_FLOAT, false, 20, 0)
|
||||||
|
GL20.glVertexAttribPointer(aUv, 2, GL_FLOAT, false, 20, 12)
|
||||||
|
|
||||||
|
buf.clear()
|
||||||
|
|
||||||
|
floatArrayOf(
|
||||||
|
0f, 0f, 0f, 0f, 0f,
|
||||||
|
1f, 1f, 0f, 1f, 1f,
|
||||||
|
1f, 0f, 0f, 1f, 0f,
|
||||||
|
|
||||||
|
0f, 0f, 0f, 0f, 0f,
|
||||||
|
0f, 1f, 0f, 0f, 1f,
|
||||||
|
1f, 1f, 0f, 1f, 1f
|
||||||
|
).forEach { buf.putFloat(it) }
|
||||||
|
|
||||||
|
buf.rewind()
|
||||||
|
|
||||||
|
GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buf, GL15.GL_STATIC_DRAW)
|
||||||
|
|
||||||
|
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0)
|
||||||
|
GL30.glBindVertexArray(0)
|
||||||
|
GL30.glUseProgram(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initFb() {
|
||||||
|
fb?.delete()
|
||||||
|
val scale = 4
|
||||||
|
fb = SimpleFramebuffer(80 * 8 * scale, 50 * 8 * scale, false, MinecraftClient.IS_SYSTEM_MAC)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removed() {
|
||||||
|
//client!!.keyboard.setRepeatEvents(false) //TODO: check if this is still okay. or if we need to change this
|
||||||
|
fb?.delete()
|
||||||
|
fb = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldPause() = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTexture(): Int {
|
||||||
|
val tex = TextureUtil.generateTextureId()
|
||||||
|
RenderSystem.bindTexture(tex)
|
||||||
|
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST)
|
||||||
|
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST)
|
||||||
|
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT)
|
||||||
|
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT)
|
||||||
|
RenderSystem.bindTexture(0)
|
||||||
|
return tex
|
||||||
|
}
|
@ -1,276 +0,0 @@
|
|||||||
package net.brokenmoon.redcontrol.blockentities;
|
|
||||||
|
|
||||||
import net.brokenmoon.redcontrol.RedControl;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
|
||||||
import net.minecraft.network.packet.Packet;
|
|
||||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class MonitorEntity extends Peripheral {
|
|
||||||
|
|
||||||
public static final int WIDTH = 80;
|
|
||||||
public static final int HEIGHT = 50;
|
|
||||||
|
|
||||||
private int accessRow;
|
|
||||||
private int cursorX;
|
|
||||||
private int cursorY;
|
|
||||||
private int cursorMode = 2; // (0: hidden, 1: solid, 2: blink)
|
|
||||||
private int keyBufferStart;
|
|
||||||
private int keyBufferPos;
|
|
||||||
private int blitMode; // (1: fill, 2: invert; 3: shift)
|
|
||||||
private int blitXStartOrFill;
|
|
||||||
private int blitYStart;
|
|
||||||
private int blitXOffset;
|
|
||||||
private int blitYOffset;
|
|
||||||
private int blitWidth;
|
|
||||||
private int blitHeight;
|
|
||||||
|
|
||||||
private byte[] keyBuffer = new byte[0x10];
|
|
||||||
private byte[][] windowData = new byte[HEIGHT][WIDTH];
|
|
||||||
|
|
||||||
public String[] getText(){
|
|
||||||
String[] l = new String[HEIGHT];
|
|
||||||
for(int y = 0; y < HEIGHT; y++) {
|
|
||||||
String temp = "";
|
|
||||||
for (int x = 0; x < WIDTH; x++) {
|
|
||||||
temp += (char) windowData[y][x];
|
|
||||||
}
|
|
||||||
l[y] = temp;
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDisplayDirty;
|
|
||||||
private boolean isCursorDirty;
|
|
||||||
|
|
||||||
public MonitorEntity(BlockPos pos, BlockState state) {
|
|
||||||
super(RedControl.MONITOR_BLOCK_ENTITY, pos, state, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int address, int data) {
|
|
||||||
switch (address) {
|
|
||||||
case 0x00:
|
|
||||||
this.accessRow = data;
|
|
||||||
break;
|
|
||||||
case 0x01:
|
|
||||||
this.isCursorDirty = this.cursorX != data;
|
|
||||||
this.cursorX = data;
|
|
||||||
break;
|
|
||||||
case 0x02:
|
|
||||||
this.isCursorDirty = this.cursorY != data;
|
|
||||||
this.cursorY = data;
|
|
||||||
break;
|
|
||||||
case 0x03:
|
|
||||||
this.isCursorDirty = this.cursorMode != data;
|
|
||||||
this.cursorMode = data;
|
|
||||||
break;
|
|
||||||
case 0x04:
|
|
||||||
this.keyBufferStart = data & 0x0f;
|
|
||||||
break;
|
|
||||||
case 0x05:
|
|
||||||
this.keyBufferPos = data & 0x0f;
|
|
||||||
break;
|
|
||||||
case 0x06:
|
|
||||||
break;
|
|
||||||
case 0x07:
|
|
||||||
this.blitMode = data;
|
|
||||||
break;
|
|
||||||
case 0x08:
|
|
||||||
this.blitXStartOrFill = data;
|
|
||||||
break;
|
|
||||||
case 0x09:
|
|
||||||
this.blitYStart = data;
|
|
||||||
break;
|
|
||||||
case 0x0A:
|
|
||||||
this.blitXOffset = data;
|
|
||||||
break;
|
|
||||||
case 0x0B:
|
|
||||||
this.blitYOffset = data;
|
|
||||||
break;
|
|
||||||
case 0x0C:
|
|
||||||
this.blitWidth = data;
|
|
||||||
break;
|
|
||||||
case 0x0D:
|
|
||||||
this.blitHeight = data;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (address >= 0x10 && address < 0x60) {
|
|
||||||
this.isDisplayDirty = true;
|
|
||||||
this.windowData[this.accessRow][address - 0x10] = (byte) data;
|
|
||||||
}
|
|
||||||
if (this.isDisplayDirty) {
|
|
||||||
this.isDisplayDirty = false;
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(int address) {
|
|
||||||
switch (address) {
|
|
||||||
case 0x00:
|
|
||||||
return this.accessRow;
|
|
||||||
case 0x01:
|
|
||||||
return this.cursorX;
|
|
||||||
case 0x02:
|
|
||||||
return this.cursorY;
|
|
||||||
case 0x03:
|
|
||||||
return this.cursorMode;
|
|
||||||
case 0x04:
|
|
||||||
return this.keyBufferStart;
|
|
||||||
case 0x05:
|
|
||||||
return this.keyBufferPos;
|
|
||||||
case 0x06:
|
|
||||||
return this.keyBuffer[this.keyBufferStart] & 0xff;
|
|
||||||
case 0x07:
|
|
||||||
return this.blitMode;
|
|
||||||
case 0x08:
|
|
||||||
return this.blitXStartOrFill;
|
|
||||||
case 0x09:
|
|
||||||
return this.blitYStart;
|
|
||||||
case 0x0A:
|
|
||||||
return this.blitXOffset;
|
|
||||||
case 0x0B:
|
|
||||||
return this.blitYOffset;
|
|
||||||
case 0x0C:
|
|
||||||
return this.blitWidth;
|
|
||||||
case 0x0D:
|
|
||||||
return this.blitHeight;
|
|
||||||
default:
|
|
||||||
if (address >= 0x10 && address < 0x60) {
|
|
||||||
return this.windowData[this.accessRow][address - 0x10] & 0xff;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update() {
|
|
||||||
int maxWidth = Math.min(WIDTH, this.blitWidth + this.blitXOffset);
|
|
||||||
int maxHeight = Math.min(HEIGHT, this.blitHeight + this.blitYOffset);
|
|
||||||
|
|
||||||
int row = this.blitYOffset;
|
|
||||||
int col;
|
|
||||||
|
|
||||||
this.isDisplayDirty |= this.blitMode != 0;
|
|
||||||
|
|
||||||
switch (this.blitMode) {
|
|
||||||
case 1: // fill
|
|
||||||
for (; row < maxHeight; row++) {
|
|
||||||
for (col = this.blitXOffset; col < maxWidth; col++) {
|
|
||||||
this.windowData[row][col] = (byte) this.blitXStartOrFill;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2: // invert
|
|
||||||
for (; row < maxHeight; row++) {
|
|
||||||
for (col = this.blitXOffset; col < maxWidth; col++) {
|
|
||||||
this.windowData[row][col] ^= 0x80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3: // shift
|
|
||||||
int shiftX = this.blitXStartOrFill - this.blitXOffset;
|
|
||||||
int shiftY = this.blitYStart - this.blitYOffset;
|
|
||||||
for (; row < maxHeight; row++) {
|
|
||||||
int srcRow = row + shiftY;
|
|
||||||
if (srcRow >= 0 & srcRow < HEIGHT) {
|
|
||||||
for (col = this.blitXOffset; col < maxWidth; col++) {
|
|
||||||
int srcCol = col + shiftX;
|
|
||||||
if (srcCol >= 0 && srcCol < WIDTH) {
|
|
||||||
this.windowData[row][col] = this.windowData[srcRow][srcCol];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blitMode = 0;
|
|
||||||
|
|
||||||
if (this.isCursorDirty) {
|
|
||||||
this.isCursorDirty = false;
|
|
||||||
this.updateCursor(this.cursorX, this.cursorY, this.cursorMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCursor(int cursorX, int cursorY, int cursorMode) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a key code to the key buffer.
|
|
||||||
*
|
|
||||||
* @param key The key code
|
|
||||||
*/
|
|
||||||
public void onKey(byte key) {
|
|
||||||
int nextPos = (this.keyBufferPos + 1) & 0x0f;
|
|
||||||
if (nextPos != this.keyBufferStart) {
|
|
||||||
this.keyBuffer[this.keyBufferPos] = key;
|
|
||||||
this.keyBufferPos = nextPos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeNbt(NbtCompound nbt) {
|
|
||||||
nbt.putByteArray("keyBuffer", this.keyBuffer);
|
|
||||||
for(int i = 0; i < windowData.length; i++){
|
|
||||||
nbt.putByteArray("winData" + i, windowData[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
nbt.putInt("acessRow", accessRow);
|
|
||||||
nbt.putInt("cursorX", cursorX);
|
|
||||||
nbt.putInt("cursorY", cursorY);
|
|
||||||
nbt.putInt("cursorMode", cursorMode);
|
|
||||||
nbt.putInt("keyBufferStart", keyBufferStart);
|
|
||||||
nbt.putInt("keyBufferPos", keyBufferPos);
|
|
||||||
nbt.putInt("blitMode", blitMode);
|
|
||||||
nbt.putInt("blitXStartOrFill", blitXStartOrFill);
|
|
||||||
nbt.putInt("blitYStart", blitYStart);
|
|
||||||
nbt.putInt("blitXOffset", blitXOffset);
|
|
||||||
nbt.putInt("blitYOffset", blitYOffset);
|
|
||||||
nbt.putInt("blitWidth", blitWidth);
|
|
||||||
nbt.putInt("blitHeight", blitHeight);
|
|
||||||
|
|
||||||
super.writeNbt(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readNbt(NbtCompound nbt) {
|
|
||||||
this.keyBuffer = nbt.getByteArray("keyBuffer");
|
|
||||||
|
|
||||||
for(int i = 0; i < windowData.length; i++){
|
|
||||||
windowData[i] = nbt.getByteArray("winData" + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
accessRow = nbt.getInt("accessRow");
|
|
||||||
cursorX = nbt.getInt("cursorX");
|
|
||||||
cursorY = nbt.getInt("cursorY");
|
|
||||||
cursorMode = nbt.getInt("cursorMode");
|
|
||||||
keyBufferStart = nbt.getInt("keyBufferStart");
|
|
||||||
keyBufferPos = nbt.getInt("keyBufferPos");
|
|
||||||
blitMode = nbt.getInt("blitMode");
|
|
||||||
blitXStartOrFill = nbt.getInt("blitXStartOrFill");
|
|
||||||
blitYStart = nbt.getInt("blitYStart");
|
|
||||||
blitXOffset = nbt.getInt("blitXOffset");
|
|
||||||
blitYOffset = nbt.getInt("blitYOffset");
|
|
||||||
blitWidth = nbt.getInt("blitWidth");
|
|
||||||
blitHeight = nbt.getInt("blitHeight");
|
|
||||||
|
|
||||||
super.readNbt(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
|
||||||
return BlockEntityUpdateS2CPacket.create(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NbtCompound toInitialChunkDataNbt() {
|
|
||||||
return createNbt();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package net.brokenmoon.redcontrol.blocks;
|
|
||||||
|
|
||||||
import com.mojang.serialization.MapCodec;
|
|
||||||
import net.brokenmoon.redcontrol.RedControl;
|
|
||||||
import net.brokenmoon.redcontrol.RedControlNetworking;
|
|
||||||
import net.brokenmoon.redcontrol.blockentities.MonitorEntity;
|
|
||||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
|
||||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.block.BlockEntityProvider;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.block.BlockWithEntity;
|
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.ActionResult;
|
|
||||||
import net.minecraft.util.Hand;
|
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class MonitorBlock extends NetworkCarrier {
|
|
||||||
public MonitorBlock(Settings settings) {
|
|
||||||
super(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MapCodec<? extends BlockWithEntity> getCodec() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
|
||||||
return new MonitorEntity(pos, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
|
||||||
if (!world.isClient) {
|
|
||||||
if (player.getStackInHand(hand).getItem() == RedControl.SQUEAKY_HAMMER) {
|
|
||||||
super.onUse(state,world,pos,player,hand,hit);
|
|
||||||
MonitorEntity monitor = (MonitorEntity) world.getBlockEntity(pos);
|
|
||||||
player.sendMessage(Text.literal(String.valueOf(monitor.getBus().hashCode())), false);
|
|
||||||
String[] text = monitor.getText();
|
|
||||||
for (String s : text) {
|
|
||||||
player.sendMessage(Text.literal(s), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PacketByteBuf byteBuf = PacketByteBufs.create();
|
|
||||||
byteBuf.writeBlockPos(pos);
|
|
||||||
ServerPlayNetworking.send((ServerPlayerEntity) player, RedControlNetworking.MONITOR_PACKET_ID,byteBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ActionResult.SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,220 @@
|
|||||||
|
package net.brokenmoon.redcontrol.blocks
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec
|
||||||
|
import net.brokenmoon.redcontrol.RedControl
|
||||||
|
import net.brokenmoon.redcontrol.RedControlNetworking
|
||||||
|
import net.brokenmoon.redcontrol.blockentities.Peripheral
|
||||||
|
import net.brokenmoon.redcontrol.util.unsigned
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||||
|
import net.minecraft.block.BlockState
|
||||||
|
import net.minecraft.block.BlockWithEntity
|
||||||
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
|
import net.minecraft.nbt.NbtCompound
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity
|
||||||
|
import net.minecraft.util.ActionResult
|
||||||
|
import net.minecraft.util.ActionResult.FAIL
|
||||||
|
import net.minecraft.util.ActionResult.SUCCESS
|
||||||
|
import net.minecraft.util.Hand
|
||||||
|
import net.minecraft.util.hit.BlockHitResult
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.world.World
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import kotlin.experimental.xor
|
||||||
|
|
||||||
|
class TerminalBlock(settings: Settings) : NetworkCarrier(settings) {
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION")
|
||||||
|
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||||
|
if (world.getBlockEntity(pos) !is TerminalEntity) return FAIL
|
||||||
|
if (!world.isClient) {
|
||||||
|
val bbuf = PacketByteBufs.create()
|
||||||
|
bbuf.writeBlockPos(pos)
|
||||||
|
ServerPlayNetworking.send(player as ServerPlayerEntity,RedControlNetworking.MONITOR_PACKET_ID,bbuf)
|
||||||
|
}
|
||||||
|
return SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCodec(): MapCodec<out BlockWithEntity?>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun createBlockEntity(pos: BlockPos, state: BlockState) = TerminalEntity(pos, state)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TerminalEntity(pos: BlockPos, state: BlockState) : Peripheral(RedControl.TERMINAL_BLOCK_ENTITY, pos, state,1) {
|
||||||
|
|
||||||
|
val screen = ByteArray(80 * 50) { 0x20 }
|
||||||
|
val charset = RedControl.images["charset.bin"]?.clone()?: throw FileNotFoundException("expected a charset.bin in images datapack")
|
||||||
|
val kb = ByteArray(16)
|
||||||
|
|
||||||
|
var command: Byte = 0
|
||||||
|
|
||||||
|
var row = 0
|
||||||
|
var cx = 0
|
||||||
|
var cy = 0
|
||||||
|
var cm = 2
|
||||||
|
var kbs = 0
|
||||||
|
var kbp = 0
|
||||||
|
|
||||||
|
var bx1 = 0
|
||||||
|
var by1 = 0
|
||||||
|
var bx2 = 0
|
||||||
|
var by2 = 0
|
||||||
|
var bw = 0
|
||||||
|
var bh = 0
|
||||||
|
|
||||||
|
var char = 0
|
||||||
|
|
||||||
|
fun pushKey(byte: Byte): Boolean {
|
||||||
|
return if ((kbp + 1) % 16 != kbs) {
|
||||||
|
kb[kbp] = byte
|
||||||
|
kbp = (kbp + 1) % 16
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIndices(x1: Int, y1: Int, w: Int, h: Int): Sequence<Int> = sequence {
|
||||||
|
for (i in 0 until h) for (j in 0 until w) {
|
||||||
|
val x = j + x1
|
||||||
|
val y = i + y1
|
||||||
|
|
||||||
|
if (x in 0 until 80 && y in 0 until 60)
|
||||||
|
yield(x + 80 * y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(address: Int): Int = readData(address.toByte()).toInt()
|
||||||
|
override fun update() {
|
||||||
|
val data = this
|
||||||
|
var error = false
|
||||||
|
|
||||||
|
when (data.command.unsigned) {
|
||||||
|
1 -> data.getIndices(data.bx2, data.by2, data.bw, data.bh).forEach { data.screen[it] = data.bx1.toByte() }
|
||||||
|
2 -> data.getIndices(data.bx2, data.by2, data.bw, data.bh).forEach { data.screen[it] = data.screen[it] xor 0x80.toByte() }
|
||||||
|
3 -> data.getIndices(data.bx2, data.by2, data.bw, data.bh).zip(data.getIndices(data.bx1, data.by1, data.bw, data.bh)).forEach { (dest, src) -> data.screen[dest] = data.screen[src] }
|
||||||
|
4 -> RedControl.images["charset.bin"]!!.copyInto(data.charset)
|
||||||
|
255 -> Unit
|
||||||
|
else -> error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.command in 1..4) world?.updateListeners(pos, cachedState, cachedState, 3)
|
||||||
|
|
||||||
|
data.command = if (error) -1 else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(address: Int, data: Int) = storeData(address.toByte(),data.toByte())
|
||||||
|
|
||||||
|
private fun readData(at: Byte): Byte {
|
||||||
|
return when (val at = at.unsigned) {
|
||||||
|
0x00 -> row.toByte()
|
||||||
|
0x01 -> cx.toByte()
|
||||||
|
0x02 -> cy.toByte()
|
||||||
|
0x03 -> cm.toByte()
|
||||||
|
0x04 -> kbs.toByte()
|
||||||
|
0x05 -> kbp.toByte()
|
||||||
|
0x06 -> kb[kbs]
|
||||||
|
0x07 -> command
|
||||||
|
0x08 -> bx1.toByte()
|
||||||
|
0x09 -> by1.toByte()
|
||||||
|
0x0A -> bx2.toByte()
|
||||||
|
0x0B -> by2.toByte()
|
||||||
|
0x0C -> bw.toByte()
|
||||||
|
0x0D -> bh.toByte()
|
||||||
|
0x0E -> char.toByte()
|
||||||
|
in 0x10..0x5F -> screen[row * 80 + at - 0x10]
|
||||||
|
in 0x60..0x67 -> charset[char * 8 + at - 0x60]
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun storeData(at: Byte, data: Byte) {
|
||||||
|
when (val at = at.unsigned) {
|
||||||
|
0x00 -> row = data.unsigned % 50
|
||||||
|
0x01 -> cx = data.unsigned % 80
|
||||||
|
0x02 -> cy = data.unsigned % 50
|
||||||
|
0x03 -> cm = data.unsigned % 3
|
||||||
|
0x04 -> kbs = data.unsigned % 16
|
||||||
|
0x05 -> kbp = data.unsigned % 16
|
||||||
|
0x06 -> kb[kbs] = data
|
||||||
|
0x07 -> command = data
|
||||||
|
0x08 -> bx1 = data.unsigned % 80
|
||||||
|
0x09 -> by1 = data.unsigned % 50
|
||||||
|
0x0A -> bx2 = data.unsigned % 80
|
||||||
|
0x0B -> by2 = data.unsigned % 50
|
||||||
|
0x0C -> bw = data.unsigned
|
||||||
|
0x0D -> bh = data.unsigned
|
||||||
|
0x0E -> char = data.unsigned
|
||||||
|
in 0x10..0x5F -> screen[row * 80 + at - 0x10] = data
|
||||||
|
in 0x60..0x67 -> charset[char * 8 + at - 0x60] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
val needsClientUpdate = at.unsigned in setOf(0x01, 0x02, 0x03) + (0x10..0x67)
|
||||||
|
if (needsClientUpdate)
|
||||||
|
getWorld()?.updateListeners(getPos(), cachedState, cachedState, 3)
|
||||||
|
markDirty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||||
|
val tag = super.toInitialChunkDataNbt()
|
||||||
|
// these are big, TODO: only send changed data
|
||||||
|
tag.putByteArray("screen", screen)
|
||||||
|
tag.putByteArray("charset", charset)
|
||||||
|
tag.putByte("cx", cx.toByte())
|
||||||
|
tag.putByte("cy", cy.toByte())
|
||||||
|
tag.putByte("cm", cm.toByte())
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeNbt(tag: NbtCompound) {
|
||||||
|
super.writeNbt(tag)
|
||||||
|
tag.putByteArray("screen", screen)
|
||||||
|
tag.putByteArray("charset", charset)
|
||||||
|
tag.putByteArray("kb", kb)
|
||||||
|
tag.putByte("command", command)
|
||||||
|
tag.putByte("row", row.toByte())
|
||||||
|
tag.putByte("cx", cx.toByte())
|
||||||
|
tag.putByte("cy", cy.toByte())
|
||||||
|
tag.putByte("cm", cm.toByte())
|
||||||
|
tag.putByte("kbs", kbs.toByte())
|
||||||
|
tag.putByte("kbp", kbp.toByte())
|
||||||
|
tag.putByte("bx1", bx1.toByte())
|
||||||
|
tag.putByte("by1", by1.toByte())
|
||||||
|
tag.putByte("bx2", bx2.toByte())
|
||||||
|
tag.putByte("by2", by2.toByte())
|
||||||
|
tag.putByte("bw", bw.toByte())
|
||||||
|
tag.putByte("bh", bh.toByte())
|
||||||
|
tag.putByte("char", char.toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readNbt(tag: NbtCompound) {
|
||||||
|
super.readNbt(tag)
|
||||||
|
val world = getWorld()
|
||||||
|
|
||||||
|
tag.getByteArray("screen").copyInto(screen)
|
||||||
|
tag.getByteArray("charset").copyInto(charset)
|
||||||
|
cx = tag.getByte("cx").unsigned
|
||||||
|
cy = tag.getByte("cy").unsigned
|
||||||
|
cm = tag.getByte("cm").unsigned
|
||||||
|
|
||||||
|
if (world == null || !world.isClient) {
|
||||||
|
tag.getByteArray("screen").copyInto(screen)
|
||||||
|
tag.getByteArray("charset").copyInto(charset)
|
||||||
|
tag.getByteArray("kb").copyInto(kb)
|
||||||
|
command = tag.getByte("command")
|
||||||
|
row = tag.getByte("row").unsigned
|
||||||
|
kbs = tag.getByte("kbs").unsigned
|
||||||
|
kbp = tag.getByte("kbp").unsigned
|
||||||
|
bx1 = tag.getByte("bx1").unsigned
|
||||||
|
by1 = tag.getByte("by1").unsigned
|
||||||
|
bx2 = tag.getByte("bx2").unsigned
|
||||||
|
by2 = tag.getByte("by2").unsigned
|
||||||
|
bw = tag.getByte("bw").unsigned
|
||||||
|
bh = tag.getByte("bh").unsigned
|
||||||
|
char = tag.getByte("char").unsigned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package net.brokenmoon.redcontrol.util
|
||||||
|
|
||||||
|
val Byte.unsigned: Int
|
||||||
|
get() = this.toInt() and 0xFF
|
||||||
|
|
||||||
|
val Short.unsigned: Int
|
||||||
|
get() = this.toInt() and 0xFFFF
|
||||||
|
|
||||||
|
val Int.unsigned: Long
|
||||||
|
get() = this.toLong() and 0xFFFFFFFF
|
||||||
|
|
||||||
|
infix fun Int.pmod(i: Int): Int = (this % i).let { if (it < 0) it + i else it }
|
||||||
|
infix fun Long.pmod(i: Int): Int = (this % i).let { if (it < 0) (it + i).toInt() else it.toInt() }
|
||||||
|
infix fun Long.pmod(l: Long): Long = (this % l).let { if (it < 0) it + l else it }
|
||||||
|
|
||||||
|
infix fun Byte.pmod(i: Int): Byte = (this % i).let { if (it < 0) (it + i).toByte() else it.toByte() }
|
||||||
|
|
||||||
|
infix fun Byte.shr(i: Int): Byte = (this.toInt() shr i).toByte()
|
||||||
|
infix fun Byte.ushr(i: Int): Byte = (this.unsigned ushr i).toByte()
|
||||||
|
infix fun Byte.shl(i: Int): Byte = (this.toInt() shl i).toByte()
|
||||||
|
|
||||||
|
infix fun Short.shr(i: Int): Short = (this.toInt() shr i).toShort()
|
||||||
|
infix fun Short.ushr(i: Int): Short = (this.unsigned ushr i).toShort()
|
||||||
|
infix fun Short.shl(i: Int): Short = (this.toInt() shl i).toShort()
|
@ -0,0 +1,64 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 80
|
||||||
|
#define SCREEN_HEIGHT 50
|
||||||
|
|
||||||
|
#define BGCOLOR vec3(0.09, 0.07, 0)
|
||||||
|
#define FGCOLOR vec3(0.78, 0.57, 0.01)
|
||||||
|
|
||||||
|
uniform usampler2D charset;
|
||||||
|
uniform usampler2D screen;
|
||||||
|
|
||||||
|
in vec2 f_uv;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
float get_pixel(in ivec2 px) {
|
||||||
|
// where is this character on the screen? (0,0) - (SCREEN_WIDTH,SCREEN_HEIGHT)
|
||||||
|
ivec2 char = px / 8;
|
||||||
|
|
||||||
|
// which pixel of this character is this? (0,0)-(8,8)
|
||||||
|
ivec2 chPixel = px % 8;
|
||||||
|
|
||||||
|
// which character is this? 0-255
|
||||||
|
int chIndex = int(texelFetch(screen, char, 0).x);
|
||||||
|
|
||||||
|
// the bitmap of the currently drawing line of the character
|
||||||
|
int lineData = int(texelFetch(charset, ivec2(chPixel.y, chIndex), 0).x);
|
||||||
|
|
||||||
|
return float((lineData >> (7 - chPixel.x)) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float get_pixel_with_fx(in vec2 screenPos) {
|
||||||
|
ivec2 px = ivec2(screenPos);
|
||||||
|
vec2 partial = fract(screenPos);
|
||||||
|
|
||||||
|
float strength = get_pixel(px);
|
||||||
|
|
||||||
|
float x = 0;
|
||||||
|
for (int dist = 1; dist < 6; dist++) {
|
||||||
|
float f = get_pixel(px - ivec2(dist, 0)) * 0.3;
|
||||||
|
if (f > 0) {
|
||||||
|
x = f * pow(0.5, dist + partial.x - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strength = min(1, strength + x);
|
||||||
|
|
||||||
|
if (px.y % 2 == 0) strength *= 0.9;
|
||||||
|
strength *= mix(0.25, 1, sin(partial.y * 3.141592654));
|
||||||
|
|
||||||
|
strength *= dot(vec3(0, 0, 1), normalize(vec3(px - 0.5 * vec2(SCREEN_WIDTH * 8, SCREEN_HEIGHT * 8), 300)));
|
||||||
|
|
||||||
|
return strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 screenPos = vec2(f_uv.x * SCREEN_WIDTH * 8, f_uv.y * SCREEN_HEIGHT * 8);
|
||||||
|
|
||||||
|
float strength = get_pixel_with_fx(screenPos);
|
||||||
|
|
||||||
|
vec3 color = mix(BGCOLOR, FGCOLOR, strength);
|
||||||
|
|
||||||
|
fragColor = vec4(color, 1);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 xyz;
|
||||||
|
in vec2 uv;
|
||||||
|
|
||||||
|
out vec2 f_uv;
|
||||||
|
|
||||||
|
uniform mat4 mvp;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_uv = uv;
|
||||||
|
gl_Position = mvp * vec4(xyz, 1);
|
||||||
|
}
|
Binary file not shown.
Loading…
Reference in New Issue