diff --git a/src/main/java/de/bixilon/minosoft/config/StaticConfiguration.java b/src/main/java/de/bixilon/minosoft/config/StaticConfiguration.java index be9d26926..e184d4052 100644 --- a/src/main/java/de/bixilon/minosoft/config/StaticConfiguration.java +++ b/src/main/java/de/bixilon/minosoft/config/StaticConfiguration.java @@ -20,7 +20,7 @@ import java.io.File; public class StaticConfiguration { public static final boolean DEBUG_MODE = true; // if true, additional checks will be made to validate data, ... Decreases performance - public static final boolean BIOME_DEBUG_MODE = true; // colors all biomes according to the biome hashCode + public static final boolean BIOME_DEBUG_MODE = false; // colors all biomes according to the biome hashCode public static final boolean DEBUG_SLOW_LOADING = false; // if true, many Thread.sleep will be executed and the start will be delayed (by a lot) public static String CONFIG_FILENAME = "minosoft.json"; // Filename of minosoft's base configuration (located in AppData/Minosoft/config) public static boolean SKIP_MOJANG_AUTHENTICATION; // disables all connections to mojang diff --git a/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java b/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java index ceee6ac98..cab8b25c6 100644 --- a/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java +++ b/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java @@ -20,12 +20,16 @@ import com.google.gson.JsonParser; import de.bixilon.minosoft.Minosoft; import de.bixilon.minosoft.config.StaticConfiguration; import de.bixilon.minosoft.data.mappings.ResourceLocation; +import de.bixilon.minosoft.data.text.RGBColor; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import de.bixilon.minosoft.util.CountUpAndDownLatch; import de.bixilon.minosoft.util.Util; import de.bixilon.minosoft.util.logging.Log; +import de.matthiasmann.twl.utils.PNGDecoder; +import org.lwjgl.BufferUtils; import java.io.*; +import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; @@ -337,4 +341,18 @@ public class AssetsManager { public AssetVersion getAssetVersion() { return this.assetVersion; } + + public RGBColor[] readPixelArray(String name) throws IOException { + PNGDecoder decoder = new PNGDecoder(readAssetAsStream(name)); + + ByteBuffer buffer = BufferUtils.createByteBuffer(decoder.getWidth() * decoder.getHeight() * PNGDecoder.Format.RGBA.getNumComponents()); + decoder.decode(buffer, decoder.getWidth() * PNGDecoder.Format.RGBA.getNumComponents(), PNGDecoder.Format.RGBA); + buffer.rewind(); + + RGBColor[] colors = new RGBColor[decoder.getWidth() * decoder.getHeight()]; + for (int pixel = 0; pixel < buffer.limit(); pixel += PNGDecoder.Format.RGBA.getNumComponents()) { + colors[pixel / 4] = new RGBColor(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + return colors; + } } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/Dimension.kt b/src/main/java/de/bixilon/minosoft/data/mappings/Dimension.kt index 5fcb47839..a39187831 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/Dimension.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/Dimension.kt @@ -93,7 +93,7 @@ data class Dimension( ambientLight = data.get("ambient_light")?.asFloat ?: 0f, infiniBurn = ResourceLocation(data.get("ambient_light")?.asString ?: "infiniburn_overworld"), respawnAnchorWorks = data.get("respawn_anchor_works")?.asBoolean == true, - hasSkyLight = data.get("has_skylight")?.asBoolean == true, + hasSkyLight = data.get("has_sky_light")?.asBoolean == true, bedWorks = data.get("bed_works")?.asBoolean == true, effects = ResourceLocation(data.get("effects")?.asString ?: "overworld"), hasRaids = data.get("has_raids")?.asBoolean == true, diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/Registry.kt b/src/main/java/de/bixilon/minosoft/data/mappings/Registry.kt index 824c2c82d..d12941191 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/Registry.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/Registry.kt @@ -31,8 +31,8 @@ open class Registry( return resourceLocationMap[resourceLocation] ?: parentRegistry?.get(resourceLocation) } - open fun get(id: Int): T? { - return idMap[id] ?: parentRegistry?.get(id) + open fun get(id: Int): T { + return idMap[id] ?: parentRegistry?.get(id)!! } open fun getId(value: T): Int { diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/biomes/Biome.kt b/src/main/java/de/bixilon/minosoft/data/mappings/biomes/Biome.kt index 4212e5db9..d6a3ec8fe 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/biomes/Biome.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/biomes/Biome.kt @@ -18,6 +18,10 @@ import de.bixilon.minosoft.data.mappings.ResourceLocation import de.bixilon.minosoft.data.mappings.ResourceLocationDeserializer import de.bixilon.minosoft.data.mappings.versions.VersionMapping import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.gui.rendering.RenderConstants +import de.bixilon.minosoft.gui.rendering.TintColorCalculator +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import de.bixilon.minosoft.util.MMath data class Biome( val resourceLocation: ResourceLocation, @@ -25,19 +29,32 @@ data class Biome( val scale: Float, val temperature: Float, val downfall: Float, - val water_color: RGBColor, - val water_fog_color: RGBColor, + val waterColor: RGBColor?, + val waterFogColor: RGBColor?, val category: BiomeCategory, val precipation: BiomePrecipation, val skyColor: RGBColor, val foliageColor: RGBColor?, + val grassColor: RGBColor?, val descriptionId: String?, + val grassColorModifier: GrassColorModifiers = GrassColorModifiers.NONE, ) : RegistryItem { + val temperatureColorMapCoordinate = getColorMapCoordinate(temperature) + val downfallColorMapCoordinate = getColorMapCoordinate(downfall * temperature) + override fun toString(): String { return resourceLocation.toString() } + private fun getColorMapCoordinate(value: Float): Int { + return ((1.0 - MMath.clamp(value, 0.0f, 1.0f)) * RenderConstants.COLORMAP_SIZE).toInt() + } + + fun getClampedTemperature(height: Int): Int { + return getColorMapCoordinate(MMath.clamp(temperature + (MMath.clamp(height - ProtocolDefinition.SEA_LEVEL_HEIGHT, 1, Int.MAX_VALUE) * ProtocolDefinition.HEIGHT_SEA_LEVEL_MODIFIER), 0f, 1f)) + } + companion object : ResourceLocationDeserializer { override fun deserialize(mappings: VersionMapping, resourceLocation: ResourceLocation, data: JsonObject): Biome { return Biome( @@ -46,13 +63,20 @@ data class Biome( scale = data["scale"]?.asFloat ?: 0f, temperature = data["temperature"]?.asFloat ?: 0f, downfall = data["downfall"]?.asFloat ?: 0f, - water_color = RGBColor(data["water_color"]?.asInt ?: 0), - water_fog_color = RGBColor(data["water_fog_color"]?.asInt ?: 0), + waterColor = TintColorCalculator.getJsonColor(data["water_color"]?.asInt ?: 0), + waterFogColor = TintColorCalculator.getJsonColor(data["water_fog_color"]?.asInt ?: 0), category = mappings.biomeCategoryRegistry.get(data["category"]?.asInt ?: -1) ?: DEFAULT_CATEGORY, precipation = mappings.biomePrecipationRegistry.get(data["precipitation"]?.asInt ?: -1) ?: DEFAULT_PRECIPATION, - skyColor = RGBColor(data["sky_color"]?.asInt ?: 0), - foliageColor = RGBColor(data["foliage_color"]?.asInt ?: 0), + skyColor = data["sky_color"]?.asInt?.let { RGBColor.noAlpha(it) } ?: RenderConstants.GRASS_FAILOVER_COLOR, + foliageColor = TintColorCalculator.getJsonColor(data["foliage_color_override"]?.asInt ?: data["foliage_color"]?.asInt ?: 0), + grassColor = TintColorCalculator.getJsonColor(data["grass_color_override"]?.asInt ?: 0), descriptionId = data["water_fog_color"]?.asString, + grassColorModifier = data["grass_color_modifier"]?.asString?.toUpperCase()?.let { GrassColorModifiers.valueOf(it) } ?: when (resourceLocation) { + ResourceLocation("minecraft:swamp"), ResourceLocation("minecraft:swamp_hills") -> GrassColorModifiers.SWAMP + ResourceLocation("minecraft:dark_forest"), ResourceLocation("minecraft:dark_forest_hills") -> GrassColorModifiers.DARK_FORREST + + else -> GrassColorModifiers.NONE + } ) } @@ -60,4 +84,14 @@ data class Biome( private val DEFAULT_CATEGORY = BiomeCategory("NONE") } + + enum class GrassColorModifiers(val modifier: (color: RGBColor) -> RGBColor) { + NONE({ color: RGBColor -> color }), + DARK_FORREST({ color: RGBColor -> RGBColor(color.color + 2634762 shl 8) }), + SWAMP({ + // ToDo: Minecraft uses PerlinSimplexNoise here + RGBColor("#6A7039") + }), + + } } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.kt b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.kt index e73409081..4dac4e174 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/Block.kt @@ -28,12 +28,13 @@ data class Block( val hasDynamicShape: Boolean = false, val tintColor: RGBColor? = null, private val itemId: Int = 0, + val tint: ResourceLocation? = null, ) : RegistryItem { lateinit var item: Item val states: MutableSet = mutableSetOf() override fun postInit(versionMapping: VersionMapping) { - item = versionMapping.itemRegistry.get(itemId)!! + item = versionMapping.itemRegistry.get(itemId) } companion object : ResourceLocationDeserializer { @@ -44,8 +45,9 @@ data class Block( explosionResistance = data["explosion_resistance"]?.asFloat ?: 0.0f, hasCollision = data["has_collision"]?.asBoolean ?: false, hasDynamicShape = data["has_dynamic_shape"]?.asBoolean ?: false, - tintColor = data["tint_color"]?.asInt?.let { TintColorCalculator.getColor(it) }, + tintColor = data["tint_color"]?.asInt?.let { TintColorCalculator.getJsonColor(it) }, itemId = data["item"]?.asInt ?: 0, + tint = data["tint"]?.asString?.let { ResourceLocation(it) } ) // block states diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockState.kt b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockState.kt index b842ce34c..e7b62132d 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockState.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockState.kt @@ -168,7 +168,7 @@ data class BlockState( } } - val tintColor: RGBColor? = data["tint_color"]?.asInt?.let { TintColorCalculator.getColor(it) } ?: owner.tintColor + val tintColor: RGBColor? = data["tint_color"]?.asInt?.let { TintColorCalculator.getJsonColor(it) } ?: owner.tintColor return BlockState( diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/items/ItemRegistry.kt b/src/main/java/de/bixilon/minosoft/data/mappings/items/ItemRegistry.kt index 4bcdef1af..d8bd0e86b 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/items/ItemRegistry.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/items/ItemRegistry.kt @@ -26,7 +26,7 @@ class ItemRegistry( ) : Registry(parentRegistry = parentRegistry, initialSize = initialSize) { private var flattened = false - override fun get(id: Int): Item? { + override fun get(id: Int): Item { return if (!flattened) { val itemId = id ushr 16 val itemMeta = id and 0xFFFF @@ -35,7 +35,11 @@ class ItemRegistry( if (itemMeta > 0 && itemMeta < Short.MAX_VALUE) { versionItemId = versionItemId or itemMeta } - return super.get(versionItemId) ?: super.get(itemId shl 16) // ignore meta data ? + return try { + super.get(versionItemId) + } catch (exception: NullPointerException) { + super.get(itemId shl 16) // ignore meta data ? + } } else { super.get(id) } diff --git a/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java b/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java index 1204a43df..522bd8e21 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java +++ b/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java @@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.locale.minecraft.MinecraftLocaleManager; import de.bixilon.minosoft.gui.rendering.font.Font; import de.bixilon.minosoft.gui.rendering.font.FontBindings; import de.bixilon.minosoft.gui.rendering.hud.HUDScale; +import de.bixilon.minosoft.gui.rendering.hud.elements.text.HUDFontMesh; import de.bixilon.minosoft.modding.event.events.annotations.Unsafe; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import de.bixilon.minosoft.util.hash.BetterHashSet; @@ -33,7 +34,6 @@ import javax.annotation.Nullable; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.ArrayList; -import java.util.List; public class BaseComponent extends ChatComponent { private static final String LEGACY_RESET_SUFFIX = String.valueOf(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR) + PostChatFormattingCodes.RESET.getChar(); @@ -215,14 +215,14 @@ public class BaseComponent extends ChatComponent { } @Override - public void addVerticies(Vec2 startPosition, Vec2 offset, Mat4 perspectiveMatrix, FontBindings binding, Font font, HUDScale hudScale, List meshData, Vec2 maxSize) { + public void addVerticies(Vec2 startPosition, Vec2 offset, Mat4 perspectiveMatrix, FontBindings binding, Font font, HUDScale hudScale, HUDFontMesh mesh, Vec2 maxSize) { if (binding == FontBindings.RIGHT_DOWN || binding == FontBindings.RIGHT_UP) { for (int i = this.parts.size() - 1; i >= 0; i--) { - this.parts.get(i).addVerticies(startPosition, offset, perspectiveMatrix, binding, font, hudScale, meshData, maxSize); + this.parts.get(i).addVerticies(startPosition, offset, perspectiveMatrix, binding, font, hudScale, mesh, maxSize); } } else { for (var chatPart : this.parts) { - chatPart.addVerticies(startPosition, offset, perspectiveMatrix, binding, font, hudScale, meshData, maxSize); + chatPart.addVerticies(startPosition, offset, perspectiveMatrix, binding, font, hudScale, mesh, maxSize); } } } diff --git a/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java b/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java index 88396a808..8775a797b 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java +++ b/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java @@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.locale.minecraft.MinecraftLocaleManager; import de.bixilon.minosoft.gui.rendering.font.Font; import de.bixilon.minosoft.gui.rendering.font.FontBindings; import de.bixilon.minosoft.gui.rendering.hud.HUDScale; +import de.bixilon.minosoft.gui.rendering.hud.elements.text.HUDFontMesh; import glm_.mat4x4.Mat4; import glm_.vec2.Vec2; import javafx.collections.FXCollections; @@ -28,7 +29,6 @@ import javafx.collections.ObservableList; import javafx.scene.Node; import javax.annotation.Nullable; -import java.util.List; public abstract class ChatComponent { public static ChatComponent valueOf(Object raw) { @@ -96,5 +96,5 @@ public abstract class ChatComponent { /** * @return Adds all verticies to the array (used in opengl) */ - public abstract void addVerticies(Vec2 startPosition, Vec2 offset, Mat4 perspectiveMatrix, FontBindings binding, Font font, HUDScale hudScale, List meshData, Vec2 maxSize); + public abstract void addVerticies(Vec2 startPosition, Vec2 offset, Mat4 perspectiveMatrix, FontBindings binding, Font font, HUDScale hudScale, HUDFontMesh mesh, Vec2 maxSize); } diff --git a/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt b/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt index c581a9eee..a020bcf8f 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt @@ -17,11 +17,13 @@ import de.bixilon.minosoft.gui.rendering.font.Font import de.bixilon.minosoft.gui.rendering.font.FontBindings import de.bixilon.minosoft.gui.rendering.font.FontChar import de.bixilon.minosoft.gui.rendering.hud.HUDScale +import de.bixilon.minosoft.gui.rendering.hud.elements.text.HUDFontMesh import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.util.Util import de.bixilon.minosoft.util.hash.BetterHashSet import glm_.mat4x4.Mat4 import glm_.vec2.Vec2 +import glm_.vec3.Vec3 import glm_.vec4.Vec4 import javafx.animation.Animation import javafx.animation.KeyFrame @@ -201,25 +203,20 @@ open class TextComponent : ChatComponent { return nodes } - override fun addVerticies(startPosition: Vec2, offset: Vec2, perspectiveMatrix: Mat4, binding: FontBindings, font: Font, hudScale: HUDScale, meshData: MutableList, maxSize: Vec2) { - fun drawLetterVertex(position: Vec2, uv: Vec2, atlasPage: Int, color: RGBColor, meshData: MutableList) { + override fun addVerticies(startPosition: Vec2, offset: Vec2, perspectiveMatrix: Mat4, binding: FontBindings, font: Font, hudScale: HUDScale, mesh: HUDFontMesh, maxSize: Vec2) { + + fun drawLetterVertex(position: Vec3, uv: Vec2, atlasPage: Int) { val matrixPosition = perspectiveMatrix * Vec4(position.x, position.y, 0f, 1f) - meshData.add(matrixPosition.x) - meshData.add(matrixPosition.y) - meshData.add(-0.997f) - meshData.add(uv.x) - meshData.add(uv.y) - meshData.add(Float.fromBits(atlasPage)) - meshData.add(Float.fromBits(color.color)) + mesh.addVertex(Vec3(matrixPosition.x, matrixPosition.y, position.z), uv, atlasPage, color) } - fun drawLetter(position: Vec2, scaledWidth: Float, scaledHeight: Float, fontChar: FontChar, color: RGBColor, meshData: MutableList) { - drawLetterVertex(Vec2(position.x, position.y), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][0]], fontChar.atlasTextureIndex, color, meshData) - drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex, color, meshData) - drawLetterVertex(Vec2(position.x + scaledWidth, position.y), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex, color, meshData) - drawLetterVertex(Vec2(position.x + scaledWidth, position.y), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex, color, meshData) - drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex, color, meshData) - drawLetterVertex(Vec2(position.x + scaledWidth, position.y + scaledHeight), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][2]], fontChar.atlasTextureIndex, color, meshData) + fun drawLetter(position: Vec2, scaledWidth: Float, scaledHeight: Float, fontChar: FontChar) { + drawLetterVertex(Vec3(position.x, position.y, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][0]], fontChar.atlasTextureIndex) + drawLetterVertex(Vec3(position.x, position.y + scaledHeight, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex) + drawLetterVertex(Vec3(position.x + scaledWidth, position.y, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex) + drawLetterVertex(Vec3(position.x + scaledWidth, position.y, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex) + drawLetterVertex(Vec3(position.x, position.y + scaledHeight, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex) + drawLetterVertex(Vec3(position.x + scaledWidth, position.y + scaledHeight, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][2]], fontChar.atlasTextureIndex) } // reverse text if right bound val charArray = when (binding) { @@ -260,7 +257,7 @@ open class TextComponent : ChatComponent { } val fontChar = font.getChar(char) val scaledX = fontChar.width * (font.charHeight / fontChar.height.toFloat()) * hudScale.scale - drawLetter(startPosition + offset, scaledX, scaledHeight, fontChar, color, meshData) + drawLetter(startPosition + offset, scaledX, scaledHeight, fontChar) offset += Vec2(scaledX + (hudScale.scale / 2), 0f) if (offset.x >= maxSize.x) { maxSize.x += scaledX + (hudScale.scale / 2) @@ -280,5 +277,7 @@ open class TextComponent : ChatComponent { listOf(2, 3, 0, 1), listOf(3, 2, 1, 0), ) // matches FontBindings::ordinal + + const val HUD_Z_COORDINATE = -0.997f } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/BiomeAccessor.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/BiomeAccessor.kt index 16e5479eb..8fe57d957 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/biome/BiomeAccessor.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/BiomeAccessor.kt @@ -18,5 +18,5 @@ import de.bixilon.minosoft.data.world.BlockPosition interface BiomeAccessor { - fun getBiome(position: BlockPosition): Biome + fun getBiome(position: BlockPosition): Biome? } diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/NoiseBiomeAccessor.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/NoiseBiomeAccessor.kt index 65a29725c..2066341aa 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/biome/NoiseBiomeAccessor.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/NoiseBiomeAccessor.kt @@ -15,14 +15,20 @@ package de.bixilon.minosoft.data.world.biome import de.bixilon.minosoft.data.mappings.biomes.Biome import de.bixilon.minosoft.data.world.BlockPosition +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition class NoiseBiomeAccessor( private val biomes: Array, ) : BiomeAccessor { - override fun getBiome(position: BlockPosition): Biome { + override fun getBiome(position: BlockPosition): Biome? { val inChunk = position.getInChunkSectionLocation() - return biomes[(inChunk.y * 16 + ((inChunk.z / 4) * 4 + (inChunk.x / 4)))] + val index = inChunk.y * ProtocolDefinition.SECTION_HEIGHT_Y + ((inChunk.z / 4) * 4 + (inChunk.x / 4)) + if (index < 0 || index > biomes.size) { + return null + } + + return biomes[index] // ToDo: This value is pseudo randomly generated. It depends on the seed of the world (received in join game). } 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 9219da994..059126ddc 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt @@ -19,6 +19,10 @@ import de.bixilon.minosoft.config.key.KeyAction import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.data.entities.EntityRotation import de.bixilon.minosoft.data.entities.Location +import de.bixilon.minosoft.data.mappings.biomes.Biome +import de.bixilon.minosoft.data.world.BlockPosition +import de.bixilon.minosoft.data.world.ChunkLocation +import de.bixilon.minosoft.data.world.InChunkSectionLocation import de.bixilon.minosoft.gui.rendering.chunk.Frustum import de.bixilon.minosoft.gui.rendering.shader.Shader import de.bixilon.minosoft.protocol.network.Connection @@ -35,7 +39,6 @@ class Camera( var fov: Float, private val renderWindow: RenderWindow, ) { - lateinit var viewProjectionMatrix: Mat4 private var mouseSensitivity = Minosoft.getConfig().config.game.camera.moseSensitivity private var movementSpeed = 7 var cameraPosition = Vec3(0.0f, 0.0f, 0.0f) @@ -52,6 +55,21 @@ class Camera( var cameraRight = Vec3(0.0f, 0.0f, -1.0f) private var cameraUp = Vec3(0.0f, 1.0f, 0.0f) + var feetLocation: Location = Location(0.0, 0.0, 0.0) + private set + var headLocation: Location = Location(0.0, 0.0, 0.0) + private set + var blockPosition: BlockPosition = BlockPosition(0, 0, 0) + private set + var currentBiome: Biome? = null + private set + var chunkLocation: ChunkLocation = ChunkLocation(0, 0) + private set + var sectionHeight: Int = 0 + private set + var inChunkSectionLocation: InChunkSectionLocation = InChunkSectionLocation(0, 0, 0) + private set + private var screenHeight = 0 private var screenWidth = 0 private val shaders: MutableSet = mutableSetOf() @@ -176,10 +194,30 @@ class Camera( for (shader in shaders) { shader.use().setMat4("viewProjectionMatrix", matrix) } + + positionChangeCallback() + } + + private fun positionChangeCallback() { + headLocation = Location(cameraPosition) + feetLocation = Location(headLocation.x, headLocation.y - PLAYER_HEIGHT, headLocation.z) + blockPosition = feetLocation.toBlockPosition() + currentBiome = connection.player.world.getChunk(blockPosition.getChunkLocation())?.biomeAccessor?.getBiome(blockPosition) + chunkLocation = blockPosition.getChunkLocation() + sectionHeight = blockPosition.getSectionHeight() + inChunkSectionLocation = blockPosition.getInChunkSectionLocation() + // recalculate sky color for current biome val blockPosition = Location(cameraPosition).toBlockPosition() renderWindow.setSkyColor(connection.player.world.getChunk(blockPosition.getChunkLocation())?.biomeAccessor?.getBiome(blockPosition)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR) connection.renderer.renderWindow.worldRenderer.recalculateFrustum(Frustum(this)) + connection.player.world.dimension?.hasSkyLight?.let { + if (it) { + renderWindow.setSkyColor(currentBiome?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR) + } else { + renderWindow.setSkyColor(RenderConstants.BLACK_COLOR) + } + } ?: renderWindow.setSkyColor(RenderConstants.DEFAULT_SKY_COLOR) } private fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int): Mat4 { @@ -216,7 +254,7 @@ class Camera( private fun sendPositionToServer() { if (System.currentTimeMillis() - lastPositionChange > ProtocolDefinition.TICK_TIME) { // ToDo: Replace this with proper movement and only send it, when our position changed - connection.sendPacket(PacketPlayerPositionAndRotationSending(Location(cameraPosition), EntityRotation(yaw, pitch), false)) + connection.sendPacket(PacketPlayerPositionAndRotationSending(feetLocation, EntityRotation(yaw, pitch), false)) lastPositionChange = System.currentTimeMillis() currentPositionSent = true return @@ -224,7 +262,14 @@ class Camera( currentPositionSent = false } + fun setPosition(location: Location) { + feetLocation = location + headLocation = Location(location.x, location.y + PLAYER_HEIGHT, location.z) + cameraPosition = headLocation.toVec3() + } + companion object { private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f) + private const val PLAYER_HEIGHT = 1.3 // player is 1.8 blocks high, the camera is normally at 0.5. 1.8 - 0.5 = 1.13 } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt index 35f6a700d..dbc00a6f9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt @@ -17,4 +17,15 @@ import de.bixilon.minosoft.data.text.RGBColor object RenderConstants { val DEFAULT_SKY_COLOR = RGBColor("#ecff89") + val WHITE_COLOR = RGBColor("#ffffff") + val BLACK_COLOR = RGBColor("#000000") + + val GRASS_FAILOVER_COLOR = RGBColor("#48B518") + + val GRASS_OUT_OF_BOUNDS_COLOR = RGBColor(-65281) + + val LILY_PAD_INVENTORY_COLOR = RGBColor("#71C35C") + val LILY_PAD_BLOCK_COLOR = RGBColor("#208030") + + const val COLORMAP_SIZE = 255 } 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 53bee5f28..343efe0b6 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -51,7 +51,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) private var deltaTime = 0.0 // time between current frame and last frame private var lastFrame = 0.0 - lateinit var camera: Camera + val camera: Camera = Camera(connection, Minosoft.getConfig().config.game.camera.fov, this) private val latch = CountUpAndDownLatch(1) private var renderingStatus = RenderingStates.RUNNING @@ -59,6 +59,8 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) private var polygonEnabled = false private var mouseCatch = !StaticConfiguration.DEBUG_MODE + val tintColorCalculator = TintColorCalculator() + // all renderers val worldRenderer: WorldRenderer = WorldRenderer(connection, connection.player.world, this) val hudRenderer: HUDRenderer = HUDRenderer(connection, this) @@ -82,7 +84,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) latch.countDown() } renderQueue.add { - camera.cameraPosition = packet.location.toVec3() + camera.setPosition(packet.location) camera.setRotation(packet.rotation.yaw.toDouble(), packet.rotation.pitch.toDouble()) } }) @@ -111,9 +113,10 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering) glfwTerminate() throw RuntimeException("Failed to create the GLFW window") } - camera = Camera(connection, Minosoft.getConfig().config.game.camera.fov, this) camera.init(this) + tintColorCalculator.init(connection.version.assetsManager) + glfwSetKeyCallback(this.windowId) { _: Long, key: Int, _: Int, action: Int, _: Int -> val keyCode = KeyCodes.KEY_CODE_GLFW_ID_MAP[key] ?: KeyCodes.KEY_UNKNOWN diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/TintColorCalculator.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/TintColorCalculator.kt index 8348d75b5..2634939cb 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/TintColorCalculator.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/TintColorCalculator.kt @@ -13,13 +13,51 @@ package de.bixilon.minosoft.gui.rendering +import de.bixilon.minosoft.data.assets.AssetsManager +import de.bixilon.minosoft.data.mappings.ResourceLocation +import de.bixilon.minosoft.data.mappings.biomes.Biome import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.data.world.BlockPosition -object TintColorCalculator { - fun getColor(color: Int): RGBColor? { - if (color == 0) { - return null +class TintColorCalculator { + private lateinit var grassColorMap: Array + private lateinit var foliageColorMap: Array + + fun init(assetsManager: AssetsManager) { + grassColorMap = assetsManager.readPixelArray("minecraft/textures/colormap/grass.png") + foliageColorMap = assetsManager.readPixelArray("minecraft/textures/colormap/foliage.png") + } + + fun calculateTint(tint: ResourceLocation, biome: Biome, position: BlockPosition): RGBColor? { + return when (tint) { + ResourceLocation("water_tint") -> biome.waterColor + ResourceLocation("grass_tint"), ResourceLocation("sugar_cane_tint"), ResourceLocation("shearing_double_plant_tint") -> { + val colorMapPixelIndex = biome.downfallColorMapCoordinate shl 8 or biome.temperatureColorMapCoordinate + var color = if (colorMapPixelIndex > grassColorMap.size) { + RenderConstants.GRASS_OUT_OF_BOUNDS_COLOR + } else { + grassColorMap[colorMapPixelIndex] + } + if (color == RenderConstants.WHITE_COLOR) { + color = RenderConstants.GRASS_FAILOVER_COLOR + } + biome.grassColorModifier.modifier.invoke(color) + } + ResourceLocation("foliage_tint") -> { + foliageColorMap[biome.downfallColorMapCoordinate shl 8 or biome.getClampedTemperature(position.y)] // ToDo: hardcoded color values + } + ResourceLocation("lily_pad_tint") -> RenderConstants.LILY_PAD_BLOCK_COLOR + else -> null + } + } + + + companion object { + fun getJsonColor(color: Int): RGBColor? { + if (color == 0) { + return null + } + return RGBColor.noAlpha(color) } - return RGBColor.noAlpha(color) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMesh.kt similarity index 63% rename from src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldMesh.kt rename to src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMesh.kt index 9344b8bff..dfafedf5b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMesh.kt @@ -13,22 +13,51 @@ package de.bixilon.minosoft.gui.rendering.chunk +import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.gui.rendering.textures.Texture import glm_.BYTES +import glm_.vec2.Vec2 +import glm_.vec3.Vec3 import org.lwjgl.opengl.GL11.GL_FLOAT import org.lwjgl.opengl.GL20.glEnableVertexAttribArray import org.lwjgl.opengl.GL20.glVertexAttribPointer import org.lwjgl.opengl.GL30.* -class WorldMesh(data: FloatArray) { - var vAO: Int = glGenVertexArrays() - var vBO: Int = glGenBuffers() - var trianglesCount: Int = data.size / FLOATS_PER_VERTEX +class ChunkMesh { + private val data: MutableList = mutableListOf() + private var vao: Int = 0 + private var vbo: Int = 0 + private var trianglesCount: Int = 0 + + fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?) { + data.add(position.x) + data.add(position.y) + data.add(position.z) + data.add(textureCoordinates.x * texture.widthFactor) + data.add(textureCoordinates.y * texture.heightFactor) + data.add(Float.fromBits(texture.id)) // ToDo: Compact this + + // ToDo: Send this only once per texture + data.add(texture.animationFrameTime.toFloat()) + data.add(texture.animations.toFloat()) + data.add(texture.heightFactor) + + if (tintColor == null) { + data.add(0f) + } else { + data.add(Float.fromBits(tintColor.color)) + } + } + + fun load() { + trianglesCount = data.size / FLOATS_PER_VERTEX + vao = glGenVertexArrays() + vbo = glGenBuffers() - init { // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). - glBindVertexArray(vAO) - glBindBuffer(GL_ARRAY_BUFFER, vBO) - glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW) + glBindVertexArray(vao) + glBindBuffer(GL_ARRAY_BUFFER, vbo) + glBufferData(GL_ARRAY_BUFFER, data.toFloatArray(), GL_STATIC_DRAW) var index = 0 glVertexAttribPointer(index, 3, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, 0L) glEnableVertexAttribArray(index++) @@ -53,13 +82,13 @@ class WorldMesh(data: FloatArray) { } fun draw() { - glBindVertexArray(vAO) + glBindVertexArray(vao) glDrawArrays(GL_TRIANGLES, 0, trianglesCount) } fun unload() { - glDeleteVertexArrays(vAO) - glDeleteBuffers(vBO) + glDeleteVertexArrays(vao) + glDeleteBuffers(vbo) } companion object { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt index 63493c586..3b70bbdae 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt @@ -14,8 +14,10 @@ package de.bixilon.minosoft.gui.rendering.chunk import de.bixilon.minosoft.Minosoft +import de.bixilon.minosoft.config.StaticConfiguration import de.bixilon.minosoft.data.Directions import de.bixilon.minosoft.data.mappings.blocks.BlockState +import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.* import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.Renderer @@ -34,17 +36,17 @@ import java.util.concurrent.ConcurrentHashMap class WorldRenderer(private val connection: Connection, private val world: World, val renderWindow: RenderWindow) : Renderer { private lateinit var minecraftTextures: TextureArray lateinit var chunkShader: Shader - private val chunkSectionsToDraw = ConcurrentHashMap>() + private val chunkSectionsToDraw = ConcurrentHashMap>() private val visibleChunks: MutableSet = mutableSetOf() private lateinit var frustum: Frustum private var currentTick = 0 // for animation usage private var lastTickIncrementTime = 0L - private fun prepareChunk(chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection, chunk: Chunk): FloatArray { + private fun prepareChunk(chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection, chunk: Chunk): ChunkMesh { if (frustum.containsChunk(chunkLocation, connection)) { visibleChunks.add(chunkLocation) } - val data: MutableList = mutableListOf() + val mesh = ChunkMesh() val below = world.allChunks[chunkLocation]?.sections?.get(sectionHeight - 1) val above = world.allChunks[chunkLocation]?.sections?.get(sectionHeight + 1) @@ -95,9 +97,25 @@ class WorldRenderer(private val connection: Connection, private val world: World Log.debug("") } val biome = chunk.biomeAccessor.getBiome(blockPosition) - blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, biome, worldPosition, data, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast)) + + var tintColor: RGBColor? = null + if (StaticConfiguration.BIOME_DEBUG_MODE) { + tintColor = RGBColor(biome.hashCode()) + } else { + biome?.let { + biome.foliageColor?.let { tintColor = it } + + blockInfo.block.owner.tint?.let { tint -> + tintColor = renderWindow.tintColorCalculator.calculateTint(tint, biome, blockPosition) + } + } + + blockInfo.block.tintColor?.let { tintColor = it } + } + + blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, tintColor, worldPosition, mesh, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast)) } - return data.toFloatArray() + return mesh } override fun init() { @@ -160,7 +178,7 @@ class WorldRenderer(private val connection: Connection, private val world: World fun prepareChunkSection(chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection, chunk: Chunk) { renderWindow.rendering.executor.execute { - val data = prepareChunk(chunkLocation, sectionHeight, section, chunk) + val mesh = prepareChunk(chunkLocation, sectionHeight, section, chunk) var sectionMap = chunkSectionsToDraw[chunkLocation] if (sectionMap == null) { @@ -168,9 +186,9 @@ class WorldRenderer(private val connection: Connection, private val world: World chunkSectionsToDraw[chunkLocation] = sectionMap } renderWindow.renderQueue.add { - val newMesh = WorldMesh(data) + mesh.load() sectionMap[sectionHeight]?.unload() - sectionMap[sectionHeight] = newMesh + sectionMap[sectionHeight] = mesh } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/loading/BlockModelFace.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/loading/BlockModelFace.kt index aca6fd9c4..173a3f84c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/loading/BlockModelFace.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/loading/BlockModelFace.kt @@ -23,6 +23,7 @@ import java.util.* class BlockModelFace(data: JsonObject, from: Vec3, to: Vec3, direction: Directions) { val textureName: String = data.get("texture").asString.removePrefix("#") val cullFace: Directions? + val tint: Boolean = data.has("tintindex") private var positions: MutableList init { @@ -56,8 +57,8 @@ class BlockModelFace(data: JsonObject, from: Vec3, to: Vec3, direction: Directio positions = mutableListOf( uvToFloat(Vec2(textureStart.x, textureStart.y)), uvToFloat(Vec2(textureStart.x, textureEnd.y)), - uvToFloat(Vec2(textureEnd.x, textureEnd.y)), - uvToFloat(Vec2(textureEnd.x, textureStart.y)), + uvToFloat(Vec2(textureEnd.x, textureEnd.y)), + uvToFloat(Vec2(textureEnd.x, textureStart.y)), ) val rotation = data["rotation"]?.asInt?.div(90) ?: 0 Collections.rotate(positions, rotation) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt index e0a7b840f..574acef44 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt @@ -14,11 +14,10 @@ package de.bixilon.minosoft.gui.rendering.chunk.models.renderable import com.google.gson.JsonObject -import de.bixilon.minosoft.config.StaticConfiguration import de.bixilon.minosoft.data.Directions -import de.bixilon.minosoft.data.mappings.biomes.Biome import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockInfo +import de.bixilon.minosoft.gui.rendering.chunk.ChunkMesh import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel import de.bixilon.minosoft.gui.rendering.textures.Texture import glm_.mat4x4.Mat4 @@ -71,7 +70,7 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) { } } - fun render(blockInfo: BlockInfo, biome: Biome, position: Vec3, data: MutableList, neighbourBlocks: Array) { + fun render(blockInfo: BlockInfo, tintColor: RGBColor?, position: Vec3, mesh: ChunkMesh, neighbourBlocks: Array) { val modelMatrix = Mat4().translate(position) for (direction in Directions.DIRECTIONS) { @@ -92,12 +91,7 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) { continue } - var tintColor: RGBColor? = blockInfo.block.tintColor - - if (StaticConfiguration.BIOME_DEBUG_MODE) { - tintColor = RGBColor(biome.hashCode()) - } - element.render(tintColor, textureMapping, modelMatrix, direction, data) + element.render(tintColor, textureMapping, modelMatrix, direction, mesh) } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt index 107c8aff2..2cd839b8b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt @@ -17,6 +17,7 @@ import com.google.gson.JsonObject import de.bixilon.minosoft.data.Axes import de.bixilon.minosoft.data.Directions import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.gui.rendering.chunk.ChunkMesh import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace @@ -28,7 +29,7 @@ import glm_.vec2.Vec2 import glm_.vec3.Vec3 import glm_.vec4.Vec4 -class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolean, rescale: Boolean) { +class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolean, rescale: Boolean) { private val fullFaceDirections: MutableSet = mutableSetOf() private val faces: MutableMap = element.faces.toMutableMap() private var positions: Array = element.positions.clone() @@ -49,7 +50,7 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolea } - fun render(tintColor: RGBColor?, textureMapping: MutableMap, modelMatrix: Mat4, direction: Directions, data: MutableList) { + fun render(tintColor: RGBColor?, textureMapping: MutableMap, modelMatrix: Mat4, direction: Directions, mesh: ChunkMesh) { val realDirection = directionMapping[direction]!! val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal] @@ -61,31 +62,21 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolea val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]]) - fun addToData(vec3: Vec3, textureCoordinates: Vec2) { - val input = Vec4(vec3, 1.0f) - val output = modelMatrix * input - data.add(output.x) - data.add(output.y) - data.add(output.z) - data.add(textureCoordinates.x * texture.widthFactor) - data.add(textureCoordinates.y * texture.heightFactor) - data.add(Float.fromBits(texture.id)) // ToDo: Compact this - - // ToDo: Send this only once per texture - data.add(texture.animationFrameTime.toFloat()) - data.add(texture.animations.toFloat()) - data.add(texture.heightFactor) - - if (tintColor == null) { - data.add(0f) - } else { - data.add(Float.fromBits(tintColor.color)) - } - } fun createQuad(drawPositions: Array, texturePositions: Array) { for (vertex in drawOrder) { - addToData(drawPositions[vertex.first], texturePositions[vertex.second]!!) + val input = Vec4(drawPositions[vertex.first], 1.0f) + val output = modelMatrix * input + mesh.addVertex( + position = output.toVec3(), + textureCoordinates = texturePositions[vertex.second]!!, + texture = texture, + tintColor = if (face.tint) { + tintColor + } else { + null + } + ) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt index e07bb0697..a08174db1 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDDebugScreenElement.kt @@ -17,10 +17,6 @@ import de.bixilon.minosoft.config.StaticConfiguration import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames import de.bixilon.minosoft.config.key.KeyAction import de.bixilon.minosoft.config.key.KeyCodes -import de.bixilon.minosoft.data.entities.Location -import de.bixilon.minosoft.data.world.BlockPosition -import de.bixilon.minosoft.data.world.ChunkLocation -import de.bixilon.minosoft.data.world.InChunkSectionLocation import de.bixilon.minosoft.gui.rendering.font.FontBindings import de.bixilon.minosoft.modding.loading.ModLoader import de.bixilon.minosoft.util.GitInfo @@ -41,12 +37,7 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex private val systemMemoryText: String = formatBytes(systemInfoHardwareAbstractionLayer.memory.total) private val osText: String = "${System.getProperty("os.name")}: ${systemInfo.operatingSystem.family} ${systemInfo.operatingSystem.bitness}bit" - // only calculate once per update values - private lateinit var location: Location - private lateinit var blockPosition: BlockPosition - private lateinit var chunkLocation: ChunkLocation - private var sectionHeight: Int = 0 - private lateinit var inChunkSectionLocation: InChunkSectionLocation + private val camera = hudTextElement.renderWindow.camera private var debugScreenEnabled = StaticConfiguration.DEBUG_MODE @@ -54,7 +45,6 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex if (!debugScreenEnabled) { return } - calculateDynamicValues() chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf( "FPS: ${getFPS()}", @@ -66,6 +56,7 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex "Chunk ${getChunkLocation()}", "Facing ${getFacing()}", "Dimension ${hudTextElement.connection.player.world.dimension}", + "Biome ${camera.currentBiome}", )) chatComponents[FontBindings.RIGHT_UP]!!.addAll(listOf( "Java: ${Runtime.version()} ${System.getProperty("sun.arch.data.model")}bit", @@ -153,15 +144,15 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex } private fun getLocation(): String { - return "${formatCoordinate(location.x)} / ${formatCoordinate(location.y)} / ${formatCoordinate(location.z)}" + return "${formatCoordinate(camera.feetLocation.x)} / ${formatCoordinate(camera.feetLocation.y)} / ${formatCoordinate(camera.feetLocation.z)}" } private fun getBlockPosition(): String { - return "${blockPosition.x} / ${blockPosition.y} / ${blockPosition.z}" + return "${camera.blockPosition.x} / ${camera.blockPosition.y} / ${camera.blockPosition.z}" } private fun getChunkLocation(): String { - return "${inChunkSectionLocation.x} ${inChunkSectionLocation.y} ${inChunkSectionLocation.z} in ${chunkLocation.x} $sectionHeight ${chunkLocation.z}" + return "${camera.inChunkSectionLocation.x} ${camera.inChunkSectionLocation.y} ${camera.inChunkSectionLocation.z} in ${camera.chunkLocation.x} ${camera.sectionHeight} ${camera.chunkLocation.z}" } private fun getFacing(): String { @@ -170,14 +161,6 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex return "todo (${formatRotation(yaw)} / ${formatRotation(pitch)})" } - private fun calculateDynamicValues() { - location = Location(hudTextElement.renderWindow.camera.cameraPosition) - blockPosition = location.toBlockPosition() - chunkLocation = blockPosition.getChunkLocation() - sectionHeight = blockPosition.getSectionHeight() - inChunkSectionLocation = blockPosition.getInChunkSectionLocation() - } - companion object { private val UNITS = listOf("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB") diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDFontMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDFontMesh.kt index 3ad74f51d..5fb2a936a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDFontMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDFontMesh.kt @@ -13,46 +13,67 @@ package de.bixilon.minosoft.gui.rendering.hud.elements.text +import de.bixilon.minosoft.data.text.RGBColor import glm_.BYTES +import glm_.vec2.Vec2 +import glm_.vec3.Vec3 import org.lwjgl.opengl.GL11.GL_FLOAT import org.lwjgl.opengl.GL20.glEnableVertexAttribArray import org.lwjgl.opengl.GL20.glVertexAttribPointer import org.lwjgl.opengl.GL30.* -class HUDFontMesh(data: FloatArray) { - var vAO: Int = glGenVertexArrays() - var vBO: Int = glGenBuffers() - var trianglesCount: Int = data.size / FLOAT_PER_VERTEX +class HUDFontMesh { + private val data: MutableList = mutableListOf() + private var vao: Int = 0 + private var vbo: Int = 0 + private var trianglesCount: Int = 0 + + + fun load() { + trianglesCount = data.size / FLOATS_PER_VERTEX + vao = glGenVertexArrays() + vbo = glGenBuffers() + + // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). + glBindVertexArray(vao) + glBindBuffer(GL_ARRAY_BUFFER, vbo) + glBufferData(GL_ARRAY_BUFFER, data.toFloatArray(), GL_STATIC_DRAW) + var index = 0 + glVertexAttribPointer(index, 3, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, 0L) + glEnableVertexAttribArray(index++) + glVertexAttribPointer(index, 2, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (3 * Float.BYTES).toLong()) + glEnableVertexAttribArray(index++) + glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (5 * Float.BYTES).toLong()) + glEnableVertexAttribArray(index++) + glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (6 * Float.BYTES).toLong()) + glEnableVertexAttribArray(index++) + + // 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 + glBindBuffer(GL_ARRAY_BUFFER, 0) + } + + + fun addVertex(position: Vec3, textureCoordinates: Vec2, atlasPage: Int, color: RGBColor) { + data.add(position.x) + data.add(position.y) + data.add(position.z) + data.add(textureCoordinates.x) + data.add(textureCoordinates.y) + data.add(Float.fromBits(atlasPage)) + data.add(Float.fromBits(color.color)) + } fun draw() { - glBindVertexArray(vAO) + glBindVertexArray(vao) glDrawArrays(GL_TRIANGLES, 0, trianglesCount) } fun unload() { - glDeleteVertexArrays(vAO) - glDeleteBuffers(vBO) - } - - init { - // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). - glBindVertexArray(vAO) - glBindBuffer(GL_ARRAY_BUFFER, vBO) - glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW) - glVertexAttribPointer(0, 3, GL_FLOAT, false, FLOAT_PER_VERTEX * Float.BYTES, 0L) - glEnableVertexAttribArray(0) - glVertexAttribPointer(1, 2, GL_FLOAT, false, FLOAT_PER_VERTEX * Float.BYTES, (3 * Float.BYTES).toLong()) - glEnableVertexAttribArray(1) - glVertexAttribPointer(2, 1, GL_FLOAT, false, FLOAT_PER_VERTEX * Float.BYTES, (5 * Float.BYTES).toLong()) - glEnableVertexAttribArray(2) - glVertexAttribPointer(3, 1, GL_FLOAT, false, FLOAT_PER_VERTEX * Float.BYTES, (6 * Float.BYTES).toLong()) - glEnableVertexAttribArray(3) - - // 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 - glBindBuffer(GL_ARRAY_BUFFER, 0) + glDeleteVertexArrays(vao) + glDeleteBuffers(vbo) } companion object { - private const val FLOAT_PER_VERTEX = 7 + private const val FLOATS_PER_VERTEX = 7 } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDTextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDTextElement.kt index 346968b27..dee32e297 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDTextElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/text/HUDTextElement.kt @@ -27,6 +27,7 @@ import de.bixilon.minosoft.protocol.network.Connection import glm_.glm import glm_.mat4x4.Mat4 import glm_.vec2.Vec2 +import glm_.vec3.Vec3 import glm_.vec4.Vec4 class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, val renderWindow: RenderWindow) : HUDElement { @@ -53,18 +54,15 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v prepare() } - private fun drawTextBackground(start: Vec2, end: Vec2, perspectiveMatrix: Mat4, meshData: MutableList) { + private fun drawTextBackground(start: Vec2, end: Vec2, perspectiveMatrix: Mat4, mesh: HUDFontMesh) { fun drawLetterVertex(position: Vec2) { val matrixPosition = perspectiveMatrix * Vec4(position.x, position.y, 0f, 1f) - meshData.add(matrixPosition.x) - meshData.add(matrixPosition.y) - meshData.add(-0.995f) - - meshData.add(0f) - meshData.add(0f) - meshData.add(Float.fromBits(0)) - - meshData.add(Float.fromBits(RGBColor(0, 0, 0, 76).color)) + mesh.addVertex( + position = Vec3(matrixPosition.x, matrixPosition.y, TEXT_BACKGROUND_Z), + textureCoordinates = TEXT_BACKGROUND_ATLAS_COORDINATES, + atlasPage = TEXT_BACKGROUND_ATLAS_INDEX, + color = TEXT_BACKGROUND_COLOR, + ) } drawLetterVertex(start) @@ -76,14 +74,14 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v } - fun drawChatComponent(position: Vec2, binding: FontBindings, text: ChatComponent, meshData: MutableList, maxSize: Vec2) { + fun drawChatComponent(position: Vec2, binding: FontBindings, text: ChatComponent, mesh: HUDFontMesh, maxSize: Vec2) { if (text.message.isBlank()) { maxSize += Vec2(0, font.charHeight * hudRenderer.hudScale.scale) return } - text.addVerticies(position, Vec2(0), fontBindingPerspectiveMatrices[binding.ordinal], binding, font, hudRenderer.hudScale, meshData, maxSize) + text.addVerticies(position, Vec2(0), fontBindingPerspectiveMatrices[binding.ordinal], binding, font, hudRenderer.hudScale, mesh, maxSize) - drawTextBackground(position - 1, (position + maxSize) + 1, fontBindingPerspectiveMatrices[binding.ordinal], meshData) + drawTextBackground(position - 1, (position + maxSize) + 1, fontBindingPerspectiveMatrices[binding.ordinal], mesh) } override fun prepare() { @@ -105,7 +103,7 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v hudTextElement.update() } - val meshData: MutableList = mutableListOf() + val mesh = HUDFontMesh() for ((binding, components) in componentsBindingMap) { val offset = Vec2(3, 3) @@ -116,12 +114,13 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v for ((_, component) in components.withIndex()) { val currentOffset = Vec2() - drawChatComponent(offset, binding, ChatComponent.valueOf(component), meshData, currentOffset) + drawChatComponent(offset, binding, ChatComponent.valueOf(component), mesh, currentOffset) offset += Vec2(0, currentOffset.y + 1) } } + mesh.load() hudMeshHUD.unload() - hudMeshHUD = HUDFontMesh(meshData.toFloatArray()) + hudMeshHUD = mesh } @@ -131,7 +130,8 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v fontAtlasTexture = font.createAtlasTexture() fontAtlasTexture.load() - hudMeshHUD = HUDFontMesh(floatArrayOf()) + hudMeshHUD = HUDFontMesh() + hudMeshHUD.load() fontShader = Shader("font_vertex.glsl", "font_fragment.glsl") fontShader.load() @@ -157,4 +157,13 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v } hudMeshHUD.draw() } + + companion object { + + private const val TEXT_BACKGROUND_Z = -0.995f + private val TEXT_BACKGROUND_ATLAS_COORDINATES = Vec2() + private const val TEXT_BACKGROUND_ATLAS_INDEX = 0 + private val TEXT_BACKGROUND_COLOR = RGBColor(0, 0, 0, 80) + + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketJoinGame.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketJoinGame.kt index a5e427b48..b263d1226 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketJoinGame.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketJoinGame.kt @@ -61,7 +61,7 @@ class PacketJoinGame : ClientboundPacket() { } if (buffer.versionId < ProtocolVersions.V_1_9_1) { - dimension = buffer.connection.mapping.dimensionRegistry.get(buffer.readByte().toInt())!! + dimension = buffer.connection.mapping.dimensionRegistry.get(buffer.readByte().toInt()) difficulty = Difficulties.byId(buffer.readUnsignedByte().toInt()) maxPlayers = buffer.readByte().toInt() if (buffer.versionId >= ProtocolVersions.V_13W42B) { @@ -80,7 +80,7 @@ class PacketJoinGame : ClientboundPacket() { buffer.readStringArray() // world } if (buffer.versionId < ProtocolVersions.V_20W21A) { - dimension = buffer.connection.mapping.dimensionRegistry.get(buffer.readInt())!! + dimension = buffer.connection.mapping.dimensionRegistry.get(buffer.readInt()) } else { val dimensionCodec = buffer.readNBT() dimensions = parseDimensionCodec(dimensionCodec, buffer.versionId) diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java index 8ceb395c8..2a80c24bb 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java @@ -88,6 +88,10 @@ public final class ProtocolDefinition { public static final int TICKS_PER_SECOND = 20; public static final int TICK_TIME = 1000 / TICKS_PER_SECOND; + public static final int SEA_LEVEL_HEIGHT = 62; + + public static final float HEIGHT_SEA_LEVEL_MODIFIER = 0.00166667f; + public static final ResourceLocation DEFAULT_DIMENSION = new ResourceLocation("overworld"); public static final ResourceLocation NETHER_DIMENSION = new ResourceLocation("nether"); diff --git a/src/main/java/de/bixilon/minosoft/util/MMath.kt b/src/main/java/de/bixilon/minosoft/util/MMath.kt new file mode 100644 index 000000000..1bc5aae46 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/MMath.kt @@ -0,0 +1,36 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program.If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.util + +object MMath { + fun clamp(value: Int, min: Int, max: Int): Int { + if (value < min) { + return min + } + if (value > max) { + return max + } + return value + } + + fun clamp(value: Float, min: Float, max: Float): Float { + if (value < min) { + return min + } + if (value > max) { + return max + } + return value + } +}