From 463896ae784c6371f9c95a0cbe4637fe8279bd35 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 3 Feb 2021 20:13:33 +0100 Subject: [PATCH] Rendering of world, integrate in minosoft, minimal cleanup --- .../de/bixilon/minosoft/data/Directions.java | 11 +- .../minosoft/data/mappings/blocks/Block.java | 5 + .../de/bixilon/minosoft/data/world/Chunk.java | 23 ++-- .../minosoft/data/world/ChunkLocation.kt | 12 ++ .../data/world/InChunkSectionLocation.kt | 13 ++ .../bixilon/minosoft/gui/rendering/Camera.kt | 8 +- .../minosoft/gui/rendering/ChunkPreparer.kt | 112 ++++++++++++++++++ .../minosoft/gui/rendering/DummyData.java | 5 +- .../minosoft/gui/rendering/DummyRender.java | 17 --- .../bixilon/minosoft/gui/rendering/Mesh.java | 86 ++------------ .../gui/rendering/RenderConstants.java | 60 ++++++++++ .../minosoft/gui/rendering/RenderWindow.kt | 53 ++++----- .../minosoft/gui/rendering/Renderer.kt | 50 ++++++++ .../minosoft/gui/rendering/Shader.java | 9 +- .../exceptions/ChunkNotLoadedException.kt | 3 + .../minosoft/protocol/network/Connection.java | 11 +- .../clientbound/play/PacketChunkData.java | 1 + .../play/PacketPlayerPositionAndRotation.java | 1 + .../de/bixilon/minosoft/util/ChunkUtil.java | 12 +- .../assets/rendering/shader/vertex.glsl | 7 +- 20 files changed, 349 insertions(+), 150 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/ChunkPreparer.kt delete mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.java create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ChunkNotLoadedException.kt diff --git a/src/main/java/de/bixilon/minosoft/data/Directions.java b/src/main/java/de/bixilon/minosoft/data/Directions.java index 2e214235e..929059132 100644 --- a/src/main/java/de/bixilon/minosoft/data/Directions.java +++ b/src/main/java/de/bixilon/minosoft/data/Directions.java @@ -21,9 +21,18 @@ public enum Directions { WEST, EAST; - private static final Directions[] DIRECTIONS = values(); + public static final Directions[] DIRECTIONS = values(); public static Directions byId(int id) { return DIRECTIONS[id]; } + + public Directions inverse() { + var ordinal = ordinal(); + if (ordinal % 2 == 0) { + return byId(ordinal + 1); + } + return byId(ordinal - 1); + + } } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.java b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.java index 130579ddd..03cb181d3 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.java +++ b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.java @@ -13,6 +13,7 @@ package de.bixilon.minosoft.data.mappings.blocks; +import de.bixilon.minosoft.data.Directions; import de.bixilon.minosoft.data.mappings.ModIdentifier; import java.util.HashSet; @@ -104,4 +105,8 @@ public class Block extends ModIdentifier { } return String.format("%s%s", getFullIdentifier(), out); } + + public boolean forceDrawFace(Directions direction) { + return false; // ToDo + } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/Chunk.java b/src/main/java/de/bixilon/minosoft/data/world/Chunk.java index 3810b0e9a..a7663416f 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/Chunk.java +++ b/src/main/java/de/bixilon/minosoft/data/world/Chunk.java @@ -15,6 +15,7 @@ package de.bixilon.minosoft.data.world; import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData; import de.bixilon.minosoft.data.mappings.blocks.Block; +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import java.util.HashMap; @@ -22,9 +23,9 @@ import java.util.HashMap; * Collection of 16 chunks sections */ public class Chunk { - private final HashMap sections; + private final HashMap sections; - public Chunk(HashMap sections) { + public Chunk(HashMap sections) { this.sections = sections; } @@ -33,7 +34,7 @@ public class Chunk { } public Block getBlock(int x, int y, int z) { - byte section = (byte) (y / 16); + int section = (y / 16); if (!this.sections.containsKey(section)) { return null; } @@ -41,15 +42,15 @@ public class Chunk { } public void setBlock(int x, int y, int z, Block block) { - byte section = (byte) (y / 16); + int section = y / 16; createSection(section); this.sections.get(section).setBlock(x, y % 16, z, block); } - void createSection(byte section) { - if (this.sections.get(section) == null) { + void createSection(int height) { + if (this.sections.get(height) == null) { // section was empty before, creating it - this.sections.put(section, new ChunkSection()); + this.sections.put(height, new ChunkSection()); } } @@ -58,13 +59,13 @@ public class Chunk { } public void setBlock(InChunkLocation location, Block block) { - byte section = (byte) (location.getY() / 16); + int section = (location.getY() / ProtocolDefinition.SECTION_HEIGHT_Y); createSection(section); this.sections.get(section).setBlock(location.getInChunkSectionLocation(), block); } public void setBlockEntityData(InChunkLocation position, BlockEntityMetaData data) { - ChunkSection section = this.sections.get((byte) (position.getY() / 16)); + ChunkSection section = this.sections.get((position.getY() / ProtocolDefinition.SECTION_HEIGHT_Y)); if (section == null) { return; } @@ -72,7 +73,7 @@ public class Chunk { } public BlockEntityMetaData getBlockEntityData(InChunkLocation position) { - ChunkSection section = this.sections.get((byte) (position.getY() / 16)); + ChunkSection section = this.sections.get((position.getY() / ProtocolDefinition.SECTION_HEIGHT_Y)); if (section == null) { return null; } @@ -83,7 +84,7 @@ public class Chunk { blockEntities.forEach(this::setBlockEntityData); } - public HashMap getSections() { + public HashMap getSections() { return this.sections; } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/ChunkLocation.kt b/src/main/java/de/bixilon/minosoft/data/world/ChunkLocation.kt index b00f7c4db..752ea1c78 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/ChunkLocation.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/ChunkLocation.kt @@ -12,6 +12,8 @@ */ package de.bixilon.minosoft.data.world +import de.bixilon.minosoft.data.Directions + /** * Chunk X and Z location (block position / 16, rounded down) */ @@ -20,4 +22,14 @@ data class ChunkLocation(val x: Int, val z: Int) { override fun toString(): String { return "($x $z)" } + + fun getLocationByDirection(direction: Directions): ChunkLocation { + return when (direction) { + Directions.NORTH -> ChunkLocation(x, z - 1) + Directions.SOUTH -> ChunkLocation(x, z + 1) + Directions.WEST -> ChunkLocation(x - 1, z) + Directions.EAST -> ChunkLocation(x + 1, z) + else -> throw IllegalArgumentException("Chunk location is just 2d") + } + } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/InChunkSectionLocation.kt b/src/main/java/de/bixilon/minosoft/data/world/InChunkSectionLocation.kt index 1cc702255..919ddb19b 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/InChunkSectionLocation.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/InChunkSectionLocation.kt @@ -12,6 +12,8 @@ */ package de.bixilon.minosoft.data.world +import de.bixilon.minosoft.data.Directions + /** * Chunk X, Y and Z location (max 16x16x16) */ @@ -20,4 +22,15 @@ data class InChunkSectionLocation(val x: Int, val y: Int, val z: Int) { override fun toString(): String { return "($x $y $z)" } + + fun getLocationByDirection(direction: Directions): InChunkSectionLocation { + return when (direction) { + Directions.DOWN -> InChunkSectionLocation(x, y - 1, z) + Directions.UP -> InChunkSectionLocation(x, y + 1, z) + Directions.NORTH -> InChunkSectionLocation(x, y, z - 1) + Directions.SOUTH -> InChunkSectionLocation(x, y, z + 1) + Directions.WEST -> InChunkSectionLocation(x - 1, y, z) + Directions.EAST -> InChunkSectionLocation(x + 1, y, z) + } + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt index b5bd4c77b..3a025595f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt @@ -61,7 +61,7 @@ class Camera(private var fov: Float, private val windowId: Long) { } fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int, shader: Shader) { - shader.use().setMat4("projection", calculateProjectionMatrix(screenWidth, screenHeight)) + shader.use().setMat4("projectionMatrix", calculateProjectionMatrix(screenWidth, screenHeight)) } private fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int): Mat4 { @@ -69,7 +69,7 @@ class Camera(private var fov: Float, private val windowId: Long) { } fun calculateViewMatrix(shader: Shader) { - shader.use().setMat4("view", calculateViewMatrix()) + shader.use().setMat4("viewMatrix", calculateViewMatrix()) } private fun calculateViewMatrix(): Mat4 { @@ -80,6 +80,10 @@ class Camera(private var fov: Float, private val windowId: Long) { this.fov = fov } + fun setPosition(position: Vec3) { + cameraPosition = position + } + companion object { private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/ChunkPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/ChunkPreparer.kt new file mode 100644 index 000000000..60f65c559 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/ChunkPreparer.kt @@ -0,0 +1,112 @@ +package de.bixilon.minosoft.gui.rendering + +import de.bixilon.minosoft.data.Directions +import de.bixilon.minosoft.data.mappings.blocks.Block +import de.bixilon.minosoft.data.world.ChunkLocation +import de.bixilon.minosoft.data.world.ChunkSection +import de.bixilon.minosoft.data.world.World +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import glm_.mat4x4.Mat4 +import glm_.vec3.Vec3 +import glm_.vec4.Vec4 + +object ChunkPreparer { + fun prepareChunk(world: World, chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection): FloatArray { + val data: MutableList = mutableListOf() + + // ToDo: Greedy meshing! + + val below = world.allChunks[chunkLocation]?.sections?.get(sectionHeight - 1) + val above = world.allChunks[chunkLocation]?.sections?.get(sectionHeight + 1) + //val north = (world.allChunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?: throw ChunkNotLoadedException("North not loaded")).sections?.get(sectionHeight) + //val south = (world.allChunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?: throw ChunkNotLoadedException("South not loaded")).sections?.get(sectionHeight) + //val west = (world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?: throw ChunkNotLoadedException("West not loaded")).sections?.get(sectionHeight) + //val east = (world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?: throw ChunkNotLoadedException("North not loaded")).sections?.get(sectionHeight) + val north = world.allChunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?.sections?.get(sectionHeight) + val south = world.allChunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?.sections?.get(sectionHeight) + val west = world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight) + val east = world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight) + + for ((position, block) in section.blocks) { + for (direction in Directions.DIRECTIONS) { + var blockToCheck: Block? = null + when (direction) { + Directions.DOWN -> { + if (position.y == 0) { + below?.let { + blockToCheck = it.getBlock(position.x, ProtocolDefinition.SECTION_HEIGHT_Y - 1, position.z) + } + } + } + Directions.UP -> { + if (position.y == ProtocolDefinition.SECTION_HEIGHT_Y - 1) { + above?.let { + blockToCheck = it.getBlock(position.x, 0, position.z) + } + } + } + Directions.NORTH -> { + if (position.z == 0) { + north?.let { + blockToCheck = it.getBlock(position.x, position.y, ProtocolDefinition.SECTION_WIDTH_Z - 1) + } + } + } + Directions.SOUTH -> { + if (position.z == ProtocolDefinition.SECTION_WIDTH_Z - 1) { + south?.let { + blockToCheck = it.getBlock(position.x, position.y, 0) + } + } + } + Directions.WEST -> { + if (position.x == 0) { + west?.let { + blockToCheck = it.getBlock(ProtocolDefinition.SECTION_WIDTH_X - 1, position.y, position.z) + } + } + } + Directions.EAST -> { + if (position.x == ProtocolDefinition.SECTION_WIDTH_X - 1) { + east?.let { + blockToCheck = it.getBlock(0, position.y, position.z) + } + } + } + } + if (blockToCheck == null) { + blockToCheck = section.getBlock(position.getLocationByDirection(direction)) + } + if (blockToCheck != null) { + // if (block.forceDrawFace(direction.inverse())) { + continue + // } + } + + val model = Mat4().translate(Vec3(position.x, position.y, position.z)) + val vertexArray = RenderConstants.VERTICIES[direction.ordinal] + var vertex = 0 + while (vertex < vertexArray.size) { + val input = Vec4(vertexArray[vertex++], vertexArray[vertex++], vertexArray[vertex++], 1.0f) + val output = model * input + // Log.debug("input=%s; position=%s; output=%s;", input, position, output); + data.add(output.x) + data.add(output.y) + data.add(output.z) + data.add(vertexArray[vertex++]) + data.add(getTextureLayerByBlock(block).toFloat()) // ToDo: Compact this + } + } + } + return data.toFloatArray() + } + + private fun getTextureLayerByBlock(block: Block): Int { + return when (block.fullIdentifier) { + "minecraft:bedrock" -> 0 + "minecraft:dirt" -> 1 + "minecraft:stone" -> 2 + else -> 2 + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java index 82c05f8a7..20ec940d1 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java @@ -9,16 +9,19 @@ import java.util.HashMap; public class DummyData { private static final Block BEDROCK = new Block("bedrock"); private static final Block DIRT = new Block("dirt"); + private static final Block STONE = new Block("stone"); public static Chunk getDummyChunk() { Chunk chunk = new Chunk(new HashMap<>()); - for (int y = 0; y < ProtocolDefinition.SECTION_HEIGHT_Y; y++) { + for (int y = 0; y < ProtocolDefinition.SECTION_HEIGHT_Y * ProtocolDefinition.SECTIONS_PER_CHUNK; y++) { for (int x = 0; x < ProtocolDefinition.SECTION_WIDTH_X; x++) { for (int z = 0; z < ProtocolDefinition.SECTION_WIDTH_Z; z++) { if (y == 0) { chunk.setBlock(x, y, z, BEDROCK); } else if (y < 8) { chunk.setBlock(x, y, z, DIRT); + } else if (y < 60) { + chunk.setBlock(x, y, z, STONE); } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java deleted file mode 100644 index c293ee842..000000000 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.bixilon.minosoft.gui.rendering; - -import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; -import org.lwjgl.Version; - -import java.io.IOException; - -public class DummyRender { - public static void main(String[] args) throws IOException, ShaderLoadingException { - System.out.println("Hello LWJGL " + Version.getVersion() + "!"); - - RenderWindow renderWindow = new RenderWindow(); - renderWindow.init(); - renderWindow.startLoop(); - renderWindow.exit(); - } -} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java b/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java index 5966442a0..bbf300f2b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java @@ -1,8 +1,7 @@ package de.bixilon.minosoft.gui.rendering; -import glm_.mat4x4.Mat4; +import de.bixilon.minosoft.data.world.ChunkLocation; import glm_.vec3.Vec3; -import glm_.vec4.Vec4; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; @@ -14,87 +13,21 @@ import static org.lwjgl.opengl.GL30.glBindVertexArray; import static org.lwjgl.opengl.GL30.glGenVertexArrays; public class Mesh { - public static final float[][] VERTICIES = { - { - -0.5f, -0.5f, -0.5f, 0.0f, - 0.5f, -0.5f, -0.5f, 1.0f, - 0.5f, 0.5f, -0.5f, 2.0f, - 0.5f, 0.5f, -0.5f, 2.0f, - -0.5f, 0.5f, -0.5f, 3.0f, - -0.5f, -0.5f, -0.5f, 0.0f, - }, - { - -0.5f, -0.5f, 0.5f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, - 0.5f, 0.5f, 0.5f, 2.0f, - 0.5f, 0.5f, 0.5f, 2.0f, - -0.5f, 0.5f, 0.5f, 3.0f, - -0.5f, -0.5f, 0.5f, 0.0f, - }, - { - -0.5f, 0.5f, 0.5f, 1.0f, - -0.5f, 0.5f, -0.5f, 2.0f, - -0.5f, -0.5f, -0.5f, 3.0f, - -0.5f, -0.5f, -0.5f, 3.0f, - -0.5f, -0.5f, 0.5f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, - }, - { - 0.5f, 0.5f, 0.5f, 1.0f, - 0.5f, 0.5f, -0.5f, 2.0f, - 0.5f, -0.5f, -0.5f, 3.0f, - 0.5f, -0.5f, -0.5f, 3.0f, - 0.5f, -0.5f, 0.5f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, - }, - { - -0.5f, -0.5f, -0.5f, 3.0f, - 0.5f, -0.5f, -0.5f, 2.0f, - 0.5f, -0.5f, 0.5f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, - -0.5f, -0.5f, -0.5f, 3.0f, - }, - { - -0.5f, 0.5f, -0.5f, 3.0f, - 0.5f, 0.5f, -0.5f, 2.0f, - 0.5f, 0.5f, 0.5f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, - -0.5f, 0.5f, -0.5f, 3.0f, - } - }; - int textureLayer; int vAO; int vBO; + Vec3 chunkPosition; + int trianglesCount; - public Mesh(int textureLayer, Vec3 position) { - this.textureLayer = textureLayer; - float[] result = new float[VERTICIES.length * VERTICIES[0].length + (VERTICIES.length * VERTICIES[0].length / 2)]; - int resultIndex = 0; - - var model = new Mat4().translate(position); - - for (float[] side : VERTICIES) { - for (int vertex = 0; vertex < side.length; ) { - Vec4 input = new Vec4(side[vertex++], side[vertex++], side[vertex++], 1.0f); - var output = model.times(input); - // Log.debug("input=%s; position=%s; output=%s;", input, position, output); - result[resultIndex++] = output.x; - result[resultIndex++] = output.y; - result[resultIndex++] = output.z; - result[resultIndex++] = side[vertex++]; - result[resultIndex++] = textureLayer; - } - } - + public Mesh(float[] data, ChunkLocation location, int sectionHeight) { + this.chunkPosition = new Vec3(location.getX(), sectionHeight, location.getZ()); + this.trianglesCount = data.length / 5; this.vAO = glGenVertexArrays(); this.vBO = glGenBuffers(); // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). glBindVertexArray(this.vAO); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vBO); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, result, GL15.GL_STATIC_DRAW); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW); GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 5 * Float.BYTES, 0L); GL20.glEnableVertexAttribArray(0); GL20.glVertexAttribPointer(1, 1, GL11.GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES); @@ -106,8 +39,9 @@ public class Mesh { GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); } - public void draw() { + public void draw(Shader chunkShader) { + chunkShader.setVec3("chunkPosition", this.chunkPosition); glBindVertexArray(this.vAO); - glDrawArrays(GL_TRIANGLES, 0, 36); + glDrawArrays(GL_TRIANGLES, 0, this.trianglesCount); } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.java b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.java new file mode 100644 index 000000000..edfae6150 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.java @@ -0,0 +1,60 @@ +package de.bixilon.minosoft.gui.rendering; + +public class RenderConstants { + public static final float[][] VERTICIES = { + // down + { + -0.5f, -0.5f, -0.5f, 3.0f, + 0.5f, -0.5f, -0.5f, 2.0f, + 0.5f, -0.5f, 0.5f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, + -0.5f, -0.5f, -0.5f, 3.0f, + }, + // up + { + -0.5f, 0.5f, -0.5f, 3.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + 0.5f, 0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, 0.5f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, + -0.5f, 0.5f, -0.5f, 3.0f, + }, + // north + { + -0.5f, -0.5f, -0.5f, 0.0f, + 0.5f, -0.5f, -0.5f, 1.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + -0.5f, 0.5f, -0.5f, 3.0f, + -0.5f, -0.5f, -0.5f, 0.0f, + }, + // south + { + -0.5f, -0.5f, 0.5f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, 0.5f, 2.0f, + 0.5f, 0.5f, 0.5f, 2.0f, + -0.5f, 0.5f, 0.5f, 3.0f, + -0.5f, -0.5f, 0.5f, 0.0f, + }, + // west + { + -0.5f, 0.5f, 0.5f, 1.0f, + -0.5f, 0.5f, -0.5f, 2.0f, + -0.5f, -0.5f, -0.5f, 3.0f, + -0.5f, -0.5f, -0.5f, 3.0f, + -0.5f, -0.5f, 0.5f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, + }, + // east + { + 0.5f, 0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + 0.5f, -0.5f, -0.5f, 3.0f, + 0.5f, -0.5f, -0.5f, 3.0f, + 0.5f, -0.5f, 0.5f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, + }, + }; +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt index 8ad7db582..f4f832d65 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -1,7 +1,6 @@ package de.bixilon.minosoft.gui.rendering -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition -import glm_.vec3.Vec3 +import de.bixilon.minosoft.protocol.network.Connection import org.lwjgl.* import org.lwjgl.glfw.Callbacks import org.lwjgl.glfw.GLFW.* @@ -11,8 +10,9 @@ import org.lwjgl.opengl.* import org.lwjgl.opengl.GL11.glClear import org.lwjgl.system.MemoryStack import org.lwjgl.system.MemoryUtil +import java.util.concurrent.ConcurrentLinkedQueue -class RenderWindow { +class RenderWindow(private val connection: Connection) { private var screenWidth = 800 private var screenHeight = 600 private var polygonEnabled = false @@ -22,7 +22,11 @@ class RenderWindow { private var deltaTime = 0.0 // time between current frame and last frame private var lastFrame = 0.0 - private lateinit var camera: Camera + lateinit var camera: Camera + + val meshesToDraw = ConcurrentLinkedQueue() + val renderQueue = ConcurrentLinkedQueue() + fun init() { // Setup an error callback. The default implementation @@ -104,9 +108,11 @@ class RenderWindow { } fun startLoop() { - val chunk = DummyData.getDummyChunk() + // val world = World() + // world.setChunk(ChunkLocation(0, 0), DummyData.getDummyChunk()) + // world.setChunk(ChunkLocation(1, 0), DummyData.getDummyChunk()) - texture0 = TextureArray(arrayOf("/textures/emerald_block.png", "/textures/brown_wool.png")) + texture0 = TextureArray(arrayOf("/textures/bedrock.png", "/textures/dirt.png", "/textures/stone.png")) texture0.load() shader = Shader("vertex.glsl", "fragment.glsl") @@ -117,21 +123,12 @@ class RenderWindow { camera.calculateProjectionMatrix(screenWidth, screenHeight, shader) camera.calculateViewMatrix(shader) - val preparedChunks = mutableListOf() - - - for ((sectionHeight, section) in chunk.sections) { - for ((location, block) in section.blocks) { - val textureIndex = when (block.fullIdentifier) { - "minecraft:dirt" -> 1 - else -> 0 - } - preparedChunks.add(Mesh(textureIndex, Vec3(location.x, location.y + ProtocolDefinition.SECTION_HEIGHT_Y * sectionHeight, location.z))) - // break - } - // break - } + // for ((chunkLocation, chunk) in world.allChunks) { + // for ((sectionHeight, section) in chunk.sections) { + // meshesToDraw.add(Mesh(ChunkPreparer.prepareChunk(world, chunkLocation, sectionHeight, section), chunkLocation, sectionHeight)) + // } + // } var framesLastSecond = 0 @@ -150,18 +147,10 @@ class RenderWindow { camera.calculateViewMatrix(shader) - for (mesh in preparedChunks) { - mesh.draw() + for (mesh in meshesToDraw) { + mesh.draw(shader) } - // for ((key, value) in chunk.sections) { - // for ((key1) in value.blocks) { - // val model = Mat4().translate(Vec3(key1.x, key1.y + ProtocolDefinition.SECTION_HEIGHT_Y * key, key1.z)) - // shader.setMat4("model", model) - // glDrawArrays(GL11.GL_TRIANGLES, 0, 36) - // } - // } - glfwSwapBuffers(windowId) // swap the color buffers // Poll for window events. The key callback above will only be @@ -175,6 +164,10 @@ class RenderWindow { framesLastSecond = 0 } framesLastSecond++ + for (renderQueueElement in renderQueue) { + renderQueueElement.run() + renderQueue.remove(renderQueueElement) + } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt new file mode 100644 index 000000000..48f07de3c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt @@ -0,0 +1,50 @@ +package de.bixilon.minosoft.gui.rendering + +import de.bixilon.minosoft.data.entities.Location +import de.bixilon.minosoft.data.world.Chunk +import de.bixilon.minosoft.data.world.ChunkLocation +import de.bixilon.minosoft.data.world.ChunkSection +import de.bixilon.minosoft.gui.rendering.ChunkPreparer.prepareChunk +import de.bixilon.minosoft.protocol.network.Connection +import de.bixilon.minosoft.util.Util +import glm_.vec3.Vec3 +import org.lwjgl.Version +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class Renderer(private val connection: Connection) { + private val renderWindow: RenderWindow = RenderWindow(connection) + private val executor: ExecutorService = Executors.newFixedThreadPool(2, Util.getThreadFactory(String.format("Rendering#%d", connection.connectionId))) + + fun start() { + Thread({ + println("Hello LWJGL " + Version.getVersion() + "!") + renderWindow.init() + renderWindow.startLoop() + renderWindow.exit() + }, "Rendering").start() + } + + fun prepareChunk(chunkLocation: ChunkLocation, chunk: Chunk) { + for ((sectionHeight, section) in chunk.sections) { + prepareChunkSection(chunkLocation, sectionHeight, section) + } + } + + fun prepareChunkSection(chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection) { + executor.execute { + val data = prepareChunk(connection.player.world, chunkLocation, sectionHeight, section) + renderWindow.renderQueue.add { + renderWindow.meshesToDraw.add(Mesh(data, chunkLocation, sectionHeight)) + } + } + } + + fun teleport(position: Location) { + renderWindow.renderQueue.add { + renderWindow.camera.setPosition(Vec3(position.x, position.y, position.z)) + + } + } + +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Shader.java b/src/main/java/de/bixilon/minosoft/gui/rendering/Shader.java index f650372a4..bb78dd5d0 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Shader.java +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Shader.java @@ -2,6 +2,7 @@ package de.bixilon.minosoft.gui.rendering; import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; import glm_.mat4x4.Mat4; +import glm_.vec3.Vec3; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.ARBFragmentShader; import org.lwjgl.opengl.ARBShaderObjects; @@ -77,10 +78,14 @@ public class Shader { } public void set4f(String variableName, float[] floats) { - glUniformMatrix4fv(glGetUniformLocation(this.programId, variableName), false, floats); + glUniformMatrix4fv(getUniformLocation(variableName), false, floats); } public void setMat4(String variableName, Mat4 mat4) { - glUniformMatrix4fv(glGetUniformLocation(this.programId, variableName), false, mat4.to(BufferUtils.createFloatBuffer(16))); + glUniformMatrix4fv(getUniformLocation(variableName), false, mat4.to(BufferUtils.createFloatBuffer(16))); + } + + public void setVec3(String name, Vec3 vec3) { + glUniform3f(getUniformLocation(name), vec3.x, vec3.y, vec3.z); } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ChunkNotLoadedException.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ChunkNotLoadedException.kt new file mode 100644 index 000000000..fd81ed5a3 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ChunkNotLoadedException.kt @@ -0,0 +1,3 @@ +package de.bixilon.minosoft.gui.rendering.exceptions + +class ChunkNotLoadedException(message: String) : Exception(message) diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java index 53f8a9473..74e8deb03 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java +++ b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java @@ -22,6 +22,7 @@ import de.bixilon.minosoft.data.mappings.recipes.Recipes; import de.bixilon.minosoft.data.mappings.versions.Version; import de.bixilon.minosoft.data.mappings.versions.VersionMapping; import de.bixilon.minosoft.data.mappings.versions.Versions; +import de.bixilon.minosoft.gui.rendering.Renderer; import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.logging.LogLevels; import de.bixilon.minosoft.modding.event.EventInvoker; @@ -69,6 +70,7 @@ public class Connection { private CommandRootNode commandRootNode; private ConnectionPing connectionStatusPing; private ServerListPongEvent pong; + private final Renderer renderer = new Renderer(this); public Connection(int connectionId, String hostname, Player player) { this.connectionId = connectionId; @@ -360,7 +362,10 @@ public class Connection { } } case FAILED_NO_RETRY -> handlePingCallbacks(null); - case PLAY -> Minosoft.CONNECTIONS.put(getConnectionId(), this); + case PLAY -> { + Minosoft.CONNECTIONS.put(getConnectionId(), this); + this.renderer.start(); + } } // handle callbacks fireEvent(new ConnectionStateChangeEvent(this, previousState, state)); @@ -429,4 +434,8 @@ public class Connection { public String toString() { return String.format("id=%d, address=%s, account=\"%s\")", getConnectionId(), getAddress(), getPlayer().getAccount()); } + + public Renderer getRenderer() { + return this.renderer; + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java index 25c0b1931..9dcaeb251 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java @@ -118,6 +118,7 @@ public class PacketChunkData extends ClientboundPacket { connection.getPlayer().getWorld().setChunk(getLocation(), getChunk()); connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities()); + connection.getRenderer().prepareChunk(this.location, this.chunk); } @Override diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketPlayerPositionAndRotation.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketPlayerPositionAndRotation.java index 933fa0db9..9002cabc3 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketPlayerPositionAndRotation.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketPlayerPositionAndRotation.java @@ -58,6 +58,7 @@ public class PacketPlayerPositionAndRotation extends ClientboundPacket { } else { connection.sendPacket(new PacketPlayerPositionAndRotationSending(getLocation(), getRotation(), isOnGround())); } + connection.getRenderer().teleport(this.location); } @Override diff --git a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java index 182387208..f498d2a88 100644 --- a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java +++ b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java @@ -52,8 +52,8 @@ public final class ChunkUtil { // parse data int arrayPos = 0; - HashMap sectionMap = new HashMap<>(); - for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column + HashMap sectionMap = new HashMap<>(); + for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column if (BitByte.isBitSet(sectionBitMask, c)) { HashMap blockMap = new HashMap<>(); @@ -114,8 +114,8 @@ public final class ChunkUtil { } int arrayPos = 0; - HashMap sectionMap = new HashMap<>(); - for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column + HashMap sectionMap = new HashMap<>(); + for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column if (!BitByte.isBitSet(sectionBitMask, c)) { continue; } @@ -140,8 +140,8 @@ public final class ChunkUtil { return new Chunk(sectionMap); } // really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712 - HashMap sectionMap = new HashMap<>(); - for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column + HashMap sectionMap = new HashMap<>(); + for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column if (!BitByte.isBitSet(sectionBitMask, c)) { continue; } diff --git a/src/main/resources/assets/rendering/shader/vertex.glsl b/src/main/resources/assets/rendering/shader/vertex.glsl index 3c4fe46fa..c1819fa4b 100644 --- a/src/main/resources/assets/rendering/shader/vertex.glsl +++ b/src/main/resources/assets/rendering/shader/vertex.glsl @@ -7,8 +7,9 @@ out vec3 vertexColor; out vec3 passTextureCoordinates; -uniform mat4 view; -uniform mat4 projection; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 chunkPosition; vec2 textureIndexCoordinates[4] = vec2[4]( vec2(0.0f, 0.0f), @@ -18,6 +19,6 @@ vec2(0.0f, 1.0f) ); void main() { - gl_Position = projection * view * vec4(inPosition, 1.0f); + gl_Position = projectionMatrix * viewMatrix * vec4(inPosition + vec3(chunkPosition.x * 16u, chunkPosition.y * 16u, chunkPosition.z * 16u), 1.0f); passTextureCoordinates = vec3(textureIndexCoordinates[int(textureIndex)], textureLayer); }