rendering: wip (2): load block models, show more complex models

This commit is contained in:
Bixilon 2021-02-05 19:32:08 +01:00
parent fe3d6ff503
commit 15498d0ffc
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
15 changed files with 215 additions and 150 deletions

View File

@ -10,10 +10,9 @@
* *
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.data
package de.bixilon.minosoft.data; enum class Directions {
public enum Directions {
DOWN, DOWN,
UP, UP,
NORTH, NORTH,
@ -21,18 +20,21 @@ public enum Directions {
WEST, WEST,
EAST; EAST;
public static final Directions[] DIRECTIONS = values(); fun inverse(): Directions {
val ordinal = ordinal
public static Directions byId(int id) { return if (ordinal % 2 == 0) {
return DIRECTIONS[id]; byId(ordinal + 1)
} else {
byId(ordinal - 1)
}
} }
public Directions inverse() { companion object {
var ordinal = ordinal(); val DIRECTIONS = values()
if (ordinal % 2 == 0) {
return byId(ordinal + 1);
}
return byId(ordinal - 1);
@JvmStatic
fun byId(id: Int): Directions {
return DIRECTIONS[id]
}
} }
} }

View File

@ -20,7 +20,6 @@ public enum Mappings {
BLOCK_MODELS; BLOCK_MODELS;
public static final Mappings[] VALUES = values(); public static final Mappings[] VALUES = values();
public String getFilename() { public String getFilename() {
return name().toLowerCase(); return name().toLowerCase();
} }

View File

@ -299,7 +299,11 @@ public class AssetsManager {
} }
public InputStreamReader readAsset(String name) throws IOException { public InputStreamReader readAsset(String name) throws IOException {
return readAssetByHash(this.assetsMap.get(name)); String hash = this.assetsMap.get(name);
if (hash == null) {
throw new FileNotFoundException(String.format("Can not find asset with name: %s", name));
}
return readAssetByHash(hash);
} }
public String readStringAsset(String name) throws IOException { public String readStringAsset(String name) throws IOException {
@ -307,7 +311,11 @@ public class AssetsManager {
} }
public InputStream readAssetAsStream(String name) throws IOException { public InputStream readAssetAsStream(String name) throws IOException {
return readAssetAsStreamByHash(this.assetsMap.get(name)); String hash = this.assetsMap.get(name);
if (hash == null) {
throw new FileNotFoundException(String.format("Can not find asset with name: %s", name));
}
return readAssetAsStreamByHash(hash);
} }
public JsonElement readJsonAsset(String name) throws IOException { public JsonElement readJsonAsset(String name) throws IOException {

View File

@ -31,7 +31,6 @@ import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.mappings.particle.Particle; import de.bixilon.minosoft.data.mappings.particle.Particle;
import de.bixilon.minosoft.data.mappings.statistics.Statistic; import de.bixilon.minosoft.data.mappings.statistics.Statistic;
import de.bixilon.minosoft.gui.rendering.models.BlockModel; import de.bixilon.minosoft.gui.rendering.models.BlockModel;
import de.bixilon.minosoft.gui.rendering.models.ConditionalModel;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.logging.Log; import de.bixilon.minosoft.util.logging.Log;
import javafx.util.Pair; import javafx.util.Pair;
@ -453,12 +452,11 @@ public class VersionMapping {
} }
if (data.has("conditional")) { if (data.has("conditional")) {
model = new ConditionalModel(parent); // model = new ConditionalModel(parent);
} else { } else {
model = new BlockModel(parent); // model = new BlockModel(this.textureIndices, data);
} }
model = new BlockModel(parent, this.textureIndices, data);
model.deserialize(this.textureIndices, data);
this.blockModels.put(identifier, model); this.blockModels.put(identifier, model);

View File

@ -71,12 +71,16 @@ object ChunkPreparer {
} }
} }
} }
if (blockToCheck == null) { if (blockToCheck == null) {
blockToCheck = section.getBlock(position.getLocationByDirection(direction)) blockToCheck = section.getBlock(position.getLocationByDirection(direction))
} }
if (blockToCheck != null) { if (blockToCheck != null) {
if (blockToCheck!!.blockModel.full && block.blockModel.full) {
continue
}
// if (block.forceDrawFace(direction.inverse())) { // if (block.forceDrawFace(direction.inverse())) {
continue //continue
// } // }
} }
data.addAll(block.blockModel.render(position, direction)) data.addAll(block.blockModel.render(position, direction))

View File

@ -21,7 +21,7 @@ public class Mesh {
public Mesh(float[] data, ChunkLocation location, int sectionHeight) { public Mesh(float[] data, ChunkLocation location, int sectionHeight) {
this.chunkPosition = new Vec3(location.getX(), sectionHeight, location.getZ()); this.chunkPosition = new Vec3(location.getX(), sectionHeight, location.getZ());
this.trianglesCount = data.length / 5; // <- bytes per vertex this.trianglesCount = data.length / 6; // <- bytes per vertex
this.vAO = glGenVertexArrays(); this.vAO = glGenVertexArrays();
this.vBO = glGenBuffers(); this.vBO = glGenBuffers();
@ -29,11 +29,11 @@ public class Mesh {
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, data, 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, 6 * 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, 2, GL11.GL_FLOAT, false, 6 * Float.BYTES, 3 * Float.BYTES);
GL20.glEnableVertexAttribArray(1); GL20.glEnableVertexAttribArray(1);
GL20.glVertexAttribPointer(2, 1, GL11.GL_FLOAT, false, 5 * Float.BYTES, 4 * Float.BYTES); GL20.glVertexAttribPointer(2, 1, GL11.GL_FLOAT, false, 6 * Float.BYTES, 5 * Float.BYTES);
GL20.glEnableVertexAttribArray(2); GL20.glEnableVertexAttribArray(2);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind

View File

@ -1,60 +0,0 @@
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

@ -3,49 +3,71 @@ package de.bixilon.minosoft.gui.rendering.models
import com.google.gson.JsonObject import com.google.gson.JsonObject
import de.bixilon.minosoft.data.Directions import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.InChunkSectionLocation import de.bixilon.minosoft.data.world.InChunkSectionLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import glm_.mat4x4.Mat4
import glm_.vec3.Vec3
import glm_.vec4.Vec4
open class BlockModel(val parent: BlockModel? = null) { open class BlockModel(val parent: BlockModel? = null, textureIndices: MutableMap<String, Int>, json: JsonObject) {
protected val textures: MutableMap<String, Int> = mutableMapOf() private val textures: MutableMap<String, String> = parent?.textures?.toMutableMap() ?: mutableMapOf()
private val textureIndexMap: MutableMap<String, Int> = parent?.textureIndexMap?.toMutableMap() ?: mutableMapOf()
private var elements: MutableList<BlockModelElement> = parent?.elements?.toMutableList() ?: mutableListOf(BlockModelElement(JsonObject()))
var full = true
init {
json["textures"]?.asJsonObject?.let {
for ((type, value) in it.entrySet()) {
textures[type] = value.asString
}
}
for ((type, texture) in textures) {
getTextureByType(texture).let {
textures[type] = it
if (!it.startsWith("#")) {
var index: Int? = textureIndices[it]
if (index == null) {
index = textureIndices.size
textureIndices[it] = index
}
textureIndexMap[type] = index
}
}
}
json["elements"]?.let { it ->
elements.clear()
for (element in it.asJsonArray) {
elements.add(BlockModelElement(element.asJsonObject))
}
}
}
open fun render(position: InChunkSectionLocation, direction: Directions): List<Float> { open fun render(position: InChunkSectionLocation, direction: Directions): List<Float> {
val data: MutableList<Float> = mutableListOf() val data: MutableList<Float> = mutableListOf()
val model = Mat4().translate(Vec3(position.x, position.y, position.z))
val vertexArray = RenderConstants.VERTICIES[direction.ordinal] for (element in elements) {
var vertex = 0 data.addAll(element.render(textureIndexMap, position, direction))
val texture = textures["all"]?.toFloat() ?: -1.0f
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(texture) // ToDo: Compact this
} }
return data return data
} }
open fun deserialize(textureIndices: MutableMap<String, Int>, json: JsonObject) { private fun getTextureByType(type: String): String {
json["textures"]?.asJsonObject?.let { var currentValue: String = type
for ((type, value) in it.entrySet()) {
if (value.asString.startsWith("#")) { while (currentValue.startsWith("#")) {
// ToDo textures[currentValue.removePrefix("#")].let {
} else { if (it == null) {
var index: Int? = textureIndices[value.asString] return currentValue
if (index == null) {
index = textureIndices.size
textureIndices[value.asString] = index
}
textures[type] = index
} }
currentValue = it
} }
} }
return currentValue
}
companion object {
fun positionToFloat(uv: Float): Float {
return (uv - 8f) / 16f
}
} }
} }

View File

@ -0,0 +1,79 @@
package de.bixilon.minosoft.gui.rendering.models
import com.google.gson.JsonObject
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.InChunkSectionLocation
import glm_.mat4x4.Mat4
import glm_.vec2.Vec2
import glm_.vec3.Vec3
import glm_.vec4.Vec4
open class BlockModelElement(data: JsonObject) {
private var from: Vec3 = Vec3(0, 0, 0)
private var to: Vec3 = Vec3(16, 16, 16)
private val faces: MutableMap<Directions, BlockModelFace> = mutableMapOf()
init {
data["from"]?.let {
val array = it.asJsonArray
from = Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
}
data["to"]?.let {
val array = it.asJsonArray
to = Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
}
data["faces"]?.let {
for ((direction, json) in it.asJsonObject.entrySet()) {
faces[Directions.valueOf(direction.toUpperCase())] = BlockModelFace(json.asJsonObject)
}
}
}
private val positionUpLeftFront = Vec3(BlockModel.positionToFloat(from.x), BlockModel.positionToFloat(to.y), BlockModel.positionToFloat(from.z))
private val positionUpLeftBack = Vec3(BlockModel.positionToFloat(from.x), BlockModel.positionToFloat(to.y), BlockModel.positionToFloat(to.z))
private val positionUpRightFront = Vec3(BlockModel.positionToFloat(to.x), BlockModel.positionToFloat(to.y), BlockModel.positionToFloat(from.z))
private val positionUpRightBack = Vec3(BlockModel.positionToFloat(to.x), BlockModel.positionToFloat(to.y), BlockModel.positionToFloat(to.z))
private val positionDownLeftFront = Vec3(BlockModel.positionToFloat(from.x), BlockModel.positionToFloat(from.y), BlockModel.positionToFloat(from.z))
private val positionDownLeftBack = Vec3(BlockModel.positionToFloat(from.x), BlockModel.positionToFloat(from.y), BlockModel.positionToFloat(to.z))
private val positionDownRightFront = Vec3(BlockModel.positionToFloat(to.x), BlockModel.positionToFloat(from.y), BlockModel.positionToFloat(from.z))
private val positionDownRightBack = Vec3(BlockModel.positionToFloat(to.x), BlockModel.positionToFloat(from.y), BlockModel.positionToFloat(to.z))
open fun render(textureIndexMap: Map<String, Int>, position: InChunkSectionLocation, direction: Directions): List<Float> {
val face = faces[direction] ?: return emptyList()
val data: MutableList<Float> = mutableListOf()
val model = Mat4().translate(Vec3(position.x, position.y, position.z))
val texture = textureIndexMap[face.texture.removePrefix("#")]?.toFloat() ?: 0f
fun addToData(vec3: Vec3, textureCoordinates: Vec2) {
val input = Vec4(vec3, 1.0f)
val output = model * input
data.add(output.x)
data.add(output.y)
data.add(output.z)
data.add(textureCoordinates.x)
data.add(textureCoordinates.y)
data.add(texture) // ToDo: Compact this
}
fun createQuad(first: Vec3, second: Vec3, third: Vec3, fourth: Vec3) {
addToData(first, face.texturRightDown)
addToData(fourth, face.texturRightUp)
addToData(third, face.texturLeftUp)
addToData(third, face.texturLeftUp)
addToData(second, face.texturLeftDown)
addToData(first, face.texturRightDown)
}
when (direction) {
Directions.DOWN -> createQuad(positionDownLeftFront, positionDownLeftBack, positionDownRightBack, positionDownRightFront)
Directions.UP -> createQuad(positionUpLeftFront, positionUpLeftBack, positionUpRightBack, positionUpRightFront)
Directions.NORTH -> createQuad(positionDownLeftFront, positionUpLeftFront, positionUpRightFront, positionDownRightFront)
Directions.SOUTH -> createQuad(positionDownLeftBack, positionUpLeftBack, positionUpRightBack, positionDownRightBack)
Directions.WEST -> createQuad(positionUpLeftBack, positionDownLeftBack, positionDownLeftFront, positionUpLeftFront)
Directions.EAST -> createQuad(positionUpRightBack, positionDownRightBack, positionDownRightFront, positionUpRightFront)
}
return data
}
}

View File

@ -0,0 +1,29 @@
package de.bixilon.minosoft.gui.rendering.models
import com.google.gson.JsonObject
import glm_.vec2.Vec2
class BlockModelFace(data: JsonObject) {
val texture: String = data.get("texture").asString
var textureStart = Vec2(0, 0)
var textureEnd = Vec2(16, 16)
init {
data["uv"]?.asJsonArray?.let {
textureStart = Vec2(it[0].asFloat, it[1].asFloat)
textureEnd = Vec2(it[2].asFloat, it[3].asFloat)
}
}
val texturLeftDown = Vec2(uvToFloat(textureStart.x), uvToFloat(textureStart.y))
val texturLeftUp = Vec2(uvToFloat(textureStart.x), uvToFloat(textureEnd.y))
val texturRightUp = Vec2(uvToFloat(textureEnd.x), uvToFloat(textureEnd.y))
val texturRightDown = Vec2(uvToFloat(textureEnd.x), uvToFloat(textureStart.y))
companion object {
fun uvToFloat(uv: Float): Float {
return (uv) / 16f
}
}
}

View File

@ -1,5 +0,0 @@
package de.bixilon.minosoft.gui.rendering.models
open class ConditionalModel(parent: BlockModel? = null) : BlockModel(parent) {
}

View File

@ -1,5 +0,0 @@
package de.bixilon.minosoft.gui.rendering.models
open class StateModel(parent: BlockModel? = null) : BlockModel(parent) {
}

View File

@ -32,7 +32,7 @@ public class TextureArray {
// load and generate the texture // load and generate the texture
var textures = TextureLoader.Companion.loadTextureArray(this.assetsManager, this.texturePaths); var textures = TextureLoader.INSTANCE.loadTextureArray(this.assetsManager, this.texturePaths);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, textures.size(), 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, textures.size(), 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null);

View File

@ -5,19 +5,20 @@ import de.matthiasmann.twl.utils.PNGDecoder
import org.lwjgl.BufferUtils import org.lwjgl.BufferUtils
import java.nio.ByteBuffer import java.nio.ByteBuffer
class TextureLoader { object TextureLoader {
fun loadTextureArray(assetsManager: AssetsManager, textures: Array<String>): Map<String, ByteBuffer> {
companion object { val result: MutableMap<String, ByteBuffer> = mutableMapOf()
fun loadTextureArray(assetsManager: AssetsManager, textures: Array<String>): Map<String, ByteBuffer> { for (texture in textures) {
val result: MutableMap<String, ByteBuffer> = mutableMapOf() result[texture] = loadTexture(assetsManager, texture)
for (texture in textures) {
val decoder = PNGDecoder(assetsManager.readAssetAsStream("minecraft/textures/${texture}.png"))
val buffer = BufferUtils.createByteBuffer(decoder.width * decoder.height * 4)
decoder.decode(buffer, decoder.width * 4, PNGDecoder.Format.RGBA)
buffer.flip()
result[texture] = buffer
}
return result.toMap()
} }
return result.toMap()
}
private fun loadTexture(assetsManager: AssetsManager, texture: String): ByteBuffer {
val decoder = PNGDecoder(assetsManager.readAssetAsStream("minecraft/textures/${texture}.png"))
val buffer = BufferUtils.createByteBuffer(decoder.width * decoder.height * 4)
decoder.decode(buffer, decoder.width * 4, PNGDecoder.Format.RGBA)
buffer.flip()
return buffer
} }
} }

View File

@ -1,6 +1,6 @@
#version 330 core #version 330 core
layout (location = 0) in vec3 inPosition; layout (location = 0) in vec3 inPosition;
layout (location = 1) in float textureIndex; layout (location = 1) in vec2 textureIndex;
layout (location = 2) in float textureLayer; layout (location = 2) in float textureLayer;
out vec3 vertexColor; out vec3 vertexColor;
@ -11,14 +11,7 @@ uniform mat4 viewMatrix;
uniform mat4 projectionMatrix; uniform mat4 projectionMatrix;
uniform vec3 chunkPosition; uniform vec3 chunkPosition;
vec2 textureIndexCoordinates[4] = vec2[4](
vec2(0.0f, 0.0f),
vec2(1.0f, 0.0f),
vec2(1.0f, 1.0f),
vec2(0.0f, 1.0f)
);
void main() { void main() {
gl_Position = projectionMatrix * viewMatrix * vec4(inPosition + vec3(chunkPosition.x * 16u, chunkPosition.y * 16u, chunkPosition.z * 16u), 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(textureIndex, textureLayer);
} }