Rendering of world, integrate in minosoft, minimal cleanup

This commit is contained in:
Bixilon 2021-02-03 20:13:33 +01:00
parent 98beee726d
commit 463896ae78
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
20 changed files with 349 additions and 150 deletions

View File

@ -21,9 +21,18 @@ public enum Directions {
WEST, WEST,
EAST; EAST;
private static final Directions[] DIRECTIONS = values(); public static final Directions[] DIRECTIONS = values();
public static Directions byId(int id) { public static Directions byId(int id) {
return DIRECTIONS[id]; return DIRECTIONS[id];
} }
public Directions inverse() {
var ordinal = ordinal();
if (ordinal % 2 == 0) {
return byId(ordinal + 1);
}
return byId(ordinal - 1);
}
} }

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.mappings.blocks; package de.bixilon.minosoft.data.mappings.blocks;
import de.bixilon.minosoft.data.Directions;
import de.bixilon.minosoft.data.mappings.ModIdentifier; import de.bixilon.minosoft.data.mappings.ModIdentifier;
import java.util.HashSet; import java.util.HashSet;
@ -104,4 +105,8 @@ public class Block extends ModIdentifier {
} }
return String.format("%s%s", getFullIdentifier(), out); return String.format("%s%s", getFullIdentifier(), out);
} }
public boolean forceDrawFace(Directions direction) {
return false; // ToDo
}
} }

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.data.world;
import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData; import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData;
import de.bixilon.minosoft.data.mappings.blocks.Block; import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import java.util.HashMap; import java.util.HashMap;
@ -22,9 +23,9 @@ import java.util.HashMap;
* Collection of 16 chunks sections * Collection of 16 chunks sections
*/ */
public class Chunk { public class Chunk {
private final HashMap<Byte, ChunkSection> sections; private final HashMap<Integer, ChunkSection> sections;
public Chunk(HashMap<Byte, ChunkSection> sections) { public Chunk(HashMap<Integer, ChunkSection> sections) {
this.sections = sections; this.sections = sections;
} }
@ -33,7 +34,7 @@ public class Chunk {
} }
public Block getBlock(int x, int y, int z) { public Block getBlock(int x, int y, int z) {
byte section = (byte) (y / 16); int section = (y / 16);
if (!this.sections.containsKey(section)) { if (!this.sections.containsKey(section)) {
return null; return null;
} }
@ -41,15 +42,15 @@ public class Chunk {
} }
public void setBlock(int x, int y, int z, Block block) { public void setBlock(int x, int y, int z, Block block) {
byte section = (byte) (y / 16); int section = y / 16;
createSection(section); createSection(section);
this.sections.get(section).setBlock(x, y % 16, z, block); this.sections.get(section).setBlock(x, y % 16, z, block);
} }
void createSection(byte section) { void createSection(int height) {
if (this.sections.get(section) == null) { if (this.sections.get(height) == null) {
// section was empty before, creating it // 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) { public void setBlock(InChunkLocation location, Block block) {
byte section = (byte) (location.getY() / 16); int section = (location.getY() / ProtocolDefinition.SECTION_HEIGHT_Y);
createSection(section); createSection(section);
this.sections.get(section).setBlock(location.getInChunkSectionLocation(), block); this.sections.get(section).setBlock(location.getInChunkSectionLocation(), block);
} }
public void setBlockEntityData(InChunkLocation position, BlockEntityMetaData data) { 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) { if (section == null) {
return; return;
} }
@ -72,7 +73,7 @@ public class Chunk {
} }
public BlockEntityMetaData getBlockEntityData(InChunkLocation position) { 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) { if (section == null) {
return null; return null;
} }
@ -83,7 +84,7 @@ public class Chunk {
blockEntities.forEach(this::setBlockEntityData); blockEntities.forEach(this::setBlockEntityData);
} }
public HashMap<Byte, ChunkSection> getSections() { public HashMap<Integer, ChunkSection> getSections() {
return this.sections; return this.sections;
} }
} }

View File

@ -12,6 +12,8 @@
*/ */
package de.bixilon.minosoft.data.world package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.Directions
/** /**
* Chunk X and Z location (block position / 16, rounded down) * 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 { override fun toString(): String {
return "($x $z)" 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")
}
}
} }

View File

@ -12,6 +12,8 @@
*/ */
package de.bixilon.minosoft.data.world package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.Directions
/** /**
* Chunk X, Y and Z location (max 16x16x16) * 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 { override fun toString(): String {
return "($x $y $z)" 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)
}
}
} }

View File

@ -61,7 +61,7 @@ class Camera(private var fov: Float, private val windowId: Long) {
} }
fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int, shader: Shader) { 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 { 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) { fun calculateViewMatrix(shader: Shader) {
shader.use().setMat4("view", calculateViewMatrix()) shader.use().setMat4("viewMatrix", calculateViewMatrix())
} }
private fun calculateViewMatrix(): Mat4 { private fun calculateViewMatrix(): Mat4 {
@ -80,6 +80,10 @@ class Camera(private var fov: Float, private val windowId: Long) {
this.fov = fov this.fov = fov
} }
fun setPosition(position: Vec3) {
cameraPosition = position
}
companion object { companion object {
private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f) private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f)
} }

View File

@ -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<Float> = 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
}
}
}

View File

@ -9,16 +9,19 @@ import java.util.HashMap;
public class DummyData { public class DummyData {
private static final Block BEDROCK = new Block("bedrock"); private static final Block BEDROCK = new Block("bedrock");
private static final Block DIRT = new Block("dirt"); private static final Block DIRT = new Block("dirt");
private static final Block STONE = new Block("stone");
public static Chunk getDummyChunk() { public static Chunk getDummyChunk() {
Chunk chunk = new Chunk(new HashMap<>()); 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 x = 0; x < ProtocolDefinition.SECTION_WIDTH_X; x++) {
for (int z = 0; z < ProtocolDefinition.SECTION_WIDTH_Z; z++) { for (int z = 0; z < ProtocolDefinition.SECTION_WIDTH_Z; z++) {
if (y == 0) { if (y == 0) {
chunk.setBlock(x, y, z, BEDROCK); chunk.setBlock(x, y, z, BEDROCK);
} else if (y < 8) { } else if (y < 8) {
chunk.setBlock(x, y, z, DIRT); chunk.setBlock(x, y, z, DIRT);
} else if (y < 60) {
chunk.setBlock(x, y, z, STONE);
} }
} }
} }

View File

@ -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();
}
}

View File

@ -1,8 +1,7 @@
package de.bixilon.minosoft.gui.rendering; package de.bixilon.minosoft.gui.rendering;
import glm_.mat4x4.Mat4; import de.bixilon.minosoft.data.world.ChunkLocation;
import glm_.vec3.Vec3; import glm_.vec3.Vec3;
import glm_.vec4.Vec4;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
@ -14,87 +13,21 @@ import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays; import static org.lwjgl.opengl.GL30.glGenVertexArrays;
public class Mesh { 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 vAO;
int vBO; int vBO;
Vec3 chunkPosition;
int trianglesCount;
public Mesh(int textureLayer, Vec3 position) { public Mesh(float[] data, ChunkLocation location, int sectionHeight) {
this.textureLayer = textureLayer; this.chunkPosition = new Vec3(location.getX(), sectionHeight, location.getZ());
float[] result = new float[VERTICIES.length * VERTICIES[0].length + (VERTICIES.length * VERTICIES[0].length / 2)]; this.trianglesCount = data.length / 5;
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;
}
}
this.vAO = glGenVertexArrays(); this.vAO = glGenVertexArrays();
this.vBO = glGenBuffers(); this.vBO = glGenBuffers();
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(this.vAO); glBindVertexArray(this.vAO);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vBO); 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.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 5 * Float.BYTES, 0L);
GL20.glEnableVertexAttribArray(0); GL20.glEnableVertexAttribArray(0);
GL20.glVertexAttribPointer(1, 1, GL11.GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES); 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); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
} }
public void draw() { public void draw(Shader chunkShader) {
chunkShader.setVec3("chunkPosition", this.chunkPosition);
glBindVertexArray(this.vAO); glBindVertexArray(this.vAO);
glDrawArrays(GL_TRIANGLES, 0, 36); glDrawArrays(GL_TRIANGLES, 0, this.trianglesCount);
} }
} }

View File

@ -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,
},
};
}

View File

@ -1,7 +1,6 @@
package de.bixilon.minosoft.gui.rendering package de.bixilon.minosoft.gui.rendering
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.network.Connection
import glm_.vec3.Vec3
import org.lwjgl.* import org.lwjgl.*
import org.lwjgl.glfw.Callbacks import org.lwjgl.glfw.Callbacks
import org.lwjgl.glfw.GLFW.* import org.lwjgl.glfw.GLFW.*
@ -11,8 +10,9 @@ import org.lwjgl.opengl.*
import org.lwjgl.opengl.GL11.glClear import org.lwjgl.opengl.GL11.glClear
import org.lwjgl.system.MemoryStack import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryUtil import org.lwjgl.system.MemoryUtil
import java.util.concurrent.ConcurrentLinkedQueue
class RenderWindow { class RenderWindow(private val connection: Connection) {
private var screenWidth = 800 private var screenWidth = 800
private var screenHeight = 600 private var screenHeight = 600
private var polygonEnabled = false private var polygonEnabled = false
@ -22,7 +22,11 @@ class RenderWindow {
private var deltaTime = 0.0 // time between current frame and last frame private var deltaTime = 0.0 // time between current frame and last frame
private var lastFrame = 0.0 private var lastFrame = 0.0
private lateinit var camera: Camera lateinit var camera: Camera
val meshesToDraw = ConcurrentLinkedQueue<Mesh>()
val renderQueue = ConcurrentLinkedQueue<Runnable>()
fun init() { fun init() {
// Setup an error callback. The default implementation // Setup an error callback. The default implementation
@ -104,9 +108,11 @@ class RenderWindow {
} }
fun startLoop() { 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() texture0.load()
shader = Shader("vertex.glsl", "fragment.glsl") shader = Shader("vertex.glsl", "fragment.glsl")
@ -117,21 +123,12 @@ class RenderWindow {
camera.calculateProjectionMatrix(screenWidth, screenHeight, shader) camera.calculateProjectionMatrix(screenWidth, screenHeight, shader)
camera.calculateViewMatrix(shader) camera.calculateViewMatrix(shader)
val preparedChunks = mutableListOf<Mesh>()
// for ((chunkLocation, chunk) in world.allChunks) {
// for ((sectionHeight, section) in chunk.sections) {
for ((sectionHeight, section) in chunk.sections) { // meshesToDraw.add(Mesh(ChunkPreparer.prepareChunk(world, chunkLocation, sectionHeight, section), chunkLocation, sectionHeight))
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
}
var framesLastSecond = 0 var framesLastSecond = 0
@ -150,18 +147,10 @@ class RenderWindow {
camera.calculateViewMatrix(shader) camera.calculateViewMatrix(shader)
for (mesh in preparedChunks) { for (mesh in meshesToDraw) {
mesh.draw() 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 glfwSwapBuffers(windowId) // swap the color buffers
// Poll for window events. The key callback above will only be // Poll for window events. The key callback above will only be
@ -175,6 +164,10 @@ class RenderWindow {
framesLastSecond = 0 framesLastSecond = 0
} }
framesLastSecond++ framesLastSecond++
for (renderQueueElement in renderQueue) {
renderQueueElement.run()
renderQueue.remove(renderQueueElement)
}
} }
} }

View File

@ -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))
}
}
}

View File

@ -2,6 +2,7 @@ package de.bixilon.minosoft.gui.rendering;
import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException;
import glm_.mat4x4.Mat4; import glm_.mat4x4.Mat4;
import glm_.vec3.Vec3;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBFragmentShader; import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.ARBShaderObjects; import org.lwjgl.opengl.ARBShaderObjects;
@ -77,10 +78,14 @@ public class Shader {
} }
public void set4f(String variableName, float[] floats) { 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) { 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);
} }
} }

View File

@ -0,0 +1,3 @@
package de.bixilon.minosoft.gui.rendering.exceptions
class ChunkNotLoadedException(message: String) : Exception(message)

View File

@ -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.Version;
import de.bixilon.minosoft.data.mappings.versions.VersionMapping; import de.bixilon.minosoft.data.mappings.versions.VersionMapping;
import de.bixilon.minosoft.data.mappings.versions.Versions; 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.Log;
import de.bixilon.minosoft.logging.LogLevels; import de.bixilon.minosoft.logging.LogLevels;
import de.bixilon.minosoft.modding.event.EventInvoker; import de.bixilon.minosoft.modding.event.EventInvoker;
@ -69,6 +70,7 @@ public class Connection {
private CommandRootNode commandRootNode; private CommandRootNode commandRootNode;
private ConnectionPing connectionStatusPing; private ConnectionPing connectionStatusPing;
private ServerListPongEvent pong; private ServerListPongEvent pong;
private final Renderer renderer = new Renderer(this);
public Connection(int connectionId, String hostname, Player player) { public Connection(int connectionId, String hostname, Player player) {
this.connectionId = connectionId; this.connectionId = connectionId;
@ -360,7 +362,10 @@ public class Connection {
} }
} }
case FAILED_NO_RETRY -> handlePingCallbacks(null); 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 // handle callbacks
fireEvent(new ConnectionStateChangeEvent(this, previousState, state)); fireEvent(new ConnectionStateChangeEvent(this, previousState, state));
@ -429,4 +434,8 @@ public class Connection {
public String toString() { public String toString() {
return String.format("id=%d, address=%s, account=\"%s\")", getConnectionId(), getAddress(), getPlayer().getAccount()); return String.format("id=%d, address=%s, account=\"%s\")", getConnectionId(), getAddress(), getPlayer().getAccount());
} }
public Renderer getRenderer() {
return this.renderer;
}
} }

View File

@ -118,6 +118,7 @@ public class PacketChunkData extends ClientboundPacket {
connection.getPlayer().getWorld().setChunk(getLocation(), getChunk()); connection.getPlayer().getWorld().setChunk(getLocation(), getChunk());
connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities()); connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities());
connection.getRenderer().prepareChunk(this.location, this.chunk);
} }
@Override @Override

View File

@ -58,6 +58,7 @@ public class PacketPlayerPositionAndRotation extends ClientboundPacket {
} else { } else {
connection.sendPacket(new PacketPlayerPositionAndRotationSending(getLocation(), getRotation(), isOnGround())); connection.sendPacket(new PacketPlayerPositionAndRotationSending(getLocation(), getRotation(), isOnGround()));
} }
connection.getRenderer().teleport(this.location);
} }
@Override @Override

View File

@ -52,8 +52,8 @@ public final class ChunkUtil {
// parse data // parse data
int arrayPos = 0; int arrayPos = 0;
HashMap<Byte, ChunkSection> sectionMap = new HashMap<>(); HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(sectionBitMask, c)) { if (BitByte.isBitSet(sectionBitMask, c)) {
HashMap<InChunkSectionLocation, Block> blockMap = new HashMap<>(); HashMap<InChunkSectionLocation, Block> blockMap = new HashMap<>();
@ -114,8 +114,8 @@ public final class ChunkUtil {
} }
int arrayPos = 0; int arrayPos = 0;
HashMap<Byte, ChunkSection> sectionMap = new HashMap<>(); HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
if (!BitByte.isBitSet(sectionBitMask, c)) { if (!BitByte.isBitSet(sectionBitMask, c)) {
continue; continue;
} }
@ -140,8 +140,8 @@ public final class ChunkUtil {
return new Chunk(sectionMap); return new Chunk(sectionMap);
} }
// really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712 // really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712
HashMap<Byte, ChunkSection> sectionMap = new HashMap<>(); HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
if (!BitByte.isBitSet(sectionBitMask, c)) { if (!BitByte.isBitSet(sectionBitMask, c)) {
continue; continue;
} }

View File

@ -7,8 +7,9 @@ out vec3 vertexColor;
out vec3 passTextureCoordinates; out vec3 passTextureCoordinates;
uniform mat4 view; uniform mat4 viewMatrix;
uniform mat4 projection; uniform mat4 projectionMatrix;
uniform vec3 chunkPosition;
vec2 textureIndexCoordinates[4] = vec2[4]( vec2 textureIndexCoordinates[4] = vec2[4](
vec2(0.0f, 0.0f), vec2(0.0f, 0.0f),
@ -18,6 +19,6 @@ vec2(0.0f, 1.0f)
); );
void main() { 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); passTextureCoordinates = vec3(textureIndexCoordinates[int(textureIndex)], textureLayer);
} }