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

View File

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

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.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<Byte, ChunkSection> sections;
private final HashMap<Integer, ChunkSection> sections;
public Chunk(HashMap<Byte, ChunkSection> sections) {
public Chunk(HashMap<Integer, ChunkSection> 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<Byte, ChunkSection> getSections() {
public HashMap<Integer, ChunkSection> getSections() {
return this.sections;
}
}

View File

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

View File

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

View File

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

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 {
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);
}
}
}

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

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
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<Mesh>()
val renderQueue = ConcurrentLinkedQueue<Runnable>()
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<Mesh>()
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)
}
}
}

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

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.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;
}
}

View File

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

View File

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

View File

@ -52,8 +52,8 @@ public final class ChunkUtil {
// parse data
int arrayPos = 0;
HashMap<Byte, ChunkSection> sectionMap = new HashMap<>();
for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
HashMap<Integer, ChunkSection> 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<InChunkSectionLocation, Block> blockMap = new HashMap<>();
@ -114,8 +114,8 @@ public final class ChunkUtil {
}
int arrayPos = 0;
HashMap<Byte, ChunkSection> sectionMap = new HashMap<>();
for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
HashMap<Integer, ChunkSection> 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<Byte, ChunkSection> sectionMap = new HashMap<>();
for (byte c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
HashMap<Integer, ChunkSection> 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;
}

View File

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