mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -04:00
rendering: chunk culling behind the player
This commit is contained in:
commit
7fe1a40fc5
@ -20,7 +20,7 @@ import java.io.File;
|
|||||||
|
|
||||||
public class StaticConfiguration {
|
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 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 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 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
|
public static boolean SKIP_MOJANG_AUTHENTICATION; // disables all connections to mojang
|
||||||
|
@ -20,12 +20,16 @@ import com.google.gson.JsonParser;
|
|||||||
import de.bixilon.minosoft.Minosoft;
|
import de.bixilon.minosoft.Minosoft;
|
||||||
import de.bixilon.minosoft.config.StaticConfiguration;
|
import de.bixilon.minosoft.config.StaticConfiguration;
|
||||||
import de.bixilon.minosoft.data.mappings.ResourceLocation;
|
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.protocol.protocol.ProtocolDefinition;
|
||||||
import de.bixilon.minosoft.util.CountUpAndDownLatch;
|
import de.bixilon.minosoft.util.CountUpAndDownLatch;
|
||||||
import de.bixilon.minosoft.util.Util;
|
import de.bixilon.minosoft.util.Util;
|
||||||
import de.bixilon.minosoft.util.logging.Log;
|
import de.bixilon.minosoft.util.logging.Log;
|
||||||
|
import de.matthiasmann.twl.utils.PNGDecoder;
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -337,4 +341,18 @@ public class AssetsManager {
|
|||||||
public AssetVersion getAssetVersion() {
|
public AssetVersion getAssetVersion() {
|
||||||
return this.assetVersion;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ data class Dimension(
|
|||||||
ambientLight = data.get("ambient_light")?.asFloat ?: 0f,
|
ambientLight = data.get("ambient_light")?.asFloat ?: 0f,
|
||||||
infiniBurn = ResourceLocation(data.get("ambient_light")?.asString ?: "infiniburn_overworld"),
|
infiniBurn = ResourceLocation(data.get("ambient_light")?.asString ?: "infiniburn_overworld"),
|
||||||
respawnAnchorWorks = data.get("respawn_anchor_works")?.asBoolean == true,
|
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,
|
bedWorks = data.get("bed_works")?.asBoolean == true,
|
||||||
effects = ResourceLocation(data.get("effects")?.asString ?: "overworld"),
|
effects = ResourceLocation(data.get("effects")?.asString ?: "overworld"),
|
||||||
hasRaids = data.get("has_raids")?.asBoolean == true,
|
hasRaids = data.get("has_raids")?.asBoolean == true,
|
||||||
|
@ -31,8 +31,8 @@ open class Registry<T : RegistryItem>(
|
|||||||
return resourceLocationMap[resourceLocation] ?: parentRegistry?.get(resourceLocation)
|
return resourceLocationMap[resourceLocation] ?: parentRegistry?.get(resourceLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun get(id: Int): T? {
|
open fun get(id: Int): T {
|
||||||
return idMap[id] ?: parentRegistry?.get(id)
|
return idMap[id] ?: parentRegistry?.get(id)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getId(value: T): Int {
|
open fun getId(value: T): Int {
|
||||||
|
@ -18,6 +18,10 @@ import de.bixilon.minosoft.data.mappings.ResourceLocation
|
|||||||
import de.bixilon.minosoft.data.mappings.ResourceLocationDeserializer
|
import de.bixilon.minosoft.data.mappings.ResourceLocationDeserializer
|
||||||
import de.bixilon.minosoft.data.mappings.versions.VersionMapping
|
import de.bixilon.minosoft.data.mappings.versions.VersionMapping
|
||||||
import de.bixilon.minosoft.data.text.RGBColor
|
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(
|
data class Biome(
|
||||||
val resourceLocation: ResourceLocation,
|
val resourceLocation: ResourceLocation,
|
||||||
@ -25,19 +29,32 @@ data class Biome(
|
|||||||
val scale: Float,
|
val scale: Float,
|
||||||
val temperature: Float,
|
val temperature: Float,
|
||||||
val downfall: Float,
|
val downfall: Float,
|
||||||
val water_color: RGBColor,
|
val waterColor: RGBColor?,
|
||||||
val water_fog_color: RGBColor,
|
val waterFogColor: RGBColor?,
|
||||||
val category: BiomeCategory,
|
val category: BiomeCategory,
|
||||||
val precipation: BiomePrecipation,
|
val precipation: BiomePrecipation,
|
||||||
val skyColor: RGBColor,
|
val skyColor: RGBColor,
|
||||||
val foliageColor: RGBColor?,
|
val foliageColor: RGBColor?,
|
||||||
|
val grassColor: RGBColor?,
|
||||||
val descriptionId: String?,
|
val descriptionId: String?,
|
||||||
|
val grassColorModifier: GrassColorModifiers = GrassColorModifiers.NONE,
|
||||||
) : RegistryItem {
|
) : RegistryItem {
|
||||||
|
|
||||||
|
val temperatureColorMapCoordinate = getColorMapCoordinate(temperature)
|
||||||
|
val downfallColorMapCoordinate = getColorMapCoordinate(downfall * temperature)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return resourceLocation.toString()
|
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<Biome> {
|
companion object : ResourceLocationDeserializer<Biome> {
|
||||||
override fun deserialize(mappings: VersionMapping, resourceLocation: ResourceLocation, data: JsonObject): Biome {
|
override fun deserialize(mappings: VersionMapping, resourceLocation: ResourceLocation, data: JsonObject): Biome {
|
||||||
return Biome(
|
return Biome(
|
||||||
@ -46,13 +63,20 @@ data class Biome(
|
|||||||
scale = data["scale"]?.asFloat ?: 0f,
|
scale = data["scale"]?.asFloat ?: 0f,
|
||||||
temperature = data["temperature"]?.asFloat ?: 0f,
|
temperature = data["temperature"]?.asFloat ?: 0f,
|
||||||
downfall = data["downfall"]?.asFloat ?: 0f,
|
downfall = data["downfall"]?.asFloat ?: 0f,
|
||||||
water_color = RGBColor(data["water_color"]?.asInt ?: 0),
|
waterColor = TintColorCalculator.getJsonColor(data["water_color"]?.asInt ?: 0),
|
||||||
water_fog_color = RGBColor(data["water_fog_color"]?.asInt ?: 0),
|
waterFogColor = TintColorCalculator.getJsonColor(data["water_fog_color"]?.asInt ?: 0),
|
||||||
category = mappings.biomeCategoryRegistry.get(data["category"]?.asInt ?: -1) ?: DEFAULT_CATEGORY,
|
category = mappings.biomeCategoryRegistry.get(data["category"]?.asInt ?: -1) ?: DEFAULT_CATEGORY,
|
||||||
precipation = mappings.biomePrecipationRegistry.get(data["precipitation"]?.asInt ?: -1) ?: DEFAULT_PRECIPATION,
|
precipation = mappings.biomePrecipationRegistry.get(data["precipitation"]?.asInt ?: -1) ?: DEFAULT_PRECIPATION,
|
||||||
skyColor = RGBColor(data["sky_color"]?.asInt ?: 0),
|
skyColor = data["sky_color"]?.asInt?.let { RGBColor.noAlpha(it) } ?: RenderConstants.GRASS_FAILOVER_COLOR,
|
||||||
foliageColor = RGBColor(data["foliage_color"]?.asInt ?: 0),
|
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,
|
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")
|
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")
|
||||||
|
}),
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,13 @@ data class Block(
|
|||||||
val hasDynamicShape: Boolean = false,
|
val hasDynamicShape: Boolean = false,
|
||||||
val tintColor: RGBColor? = null,
|
val tintColor: RGBColor? = null,
|
||||||
private val itemId: Int = 0,
|
private val itemId: Int = 0,
|
||||||
|
val tint: ResourceLocation? = null,
|
||||||
) : RegistryItem {
|
) : RegistryItem {
|
||||||
lateinit var item: Item
|
lateinit var item: Item
|
||||||
val states: MutableSet<BlockState> = mutableSetOf()
|
val states: MutableSet<BlockState> = mutableSetOf()
|
||||||
|
|
||||||
override fun postInit(versionMapping: VersionMapping) {
|
override fun postInit(versionMapping: VersionMapping) {
|
||||||
item = versionMapping.itemRegistry.get(itemId)!!
|
item = versionMapping.itemRegistry.get(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : ResourceLocationDeserializer<Block> {
|
companion object : ResourceLocationDeserializer<Block> {
|
||||||
@ -44,8 +45,9 @@ data class Block(
|
|||||||
explosionResistance = data["explosion_resistance"]?.asFloat ?: 0.0f,
|
explosionResistance = data["explosion_resistance"]?.asFloat ?: 0.0f,
|
||||||
hasCollision = data["has_collision"]?.asBoolean ?: false,
|
hasCollision = data["has_collision"]?.asBoolean ?: false,
|
||||||
hasDynamicShape = data["has_dynamic_shape"]?.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,
|
itemId = data["item"]?.asInt ?: 0,
|
||||||
|
tint = data["tint"]?.asString?.let { ResourceLocation(it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
// block states
|
// block states
|
||||||
|
@ -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(
|
return BlockState(
|
||||||
|
@ -26,7 +26,7 @@ class ItemRegistry(
|
|||||||
) : Registry<Item>(parentRegistry = parentRegistry, initialSize = initialSize) {
|
) : Registry<Item>(parentRegistry = parentRegistry, initialSize = initialSize) {
|
||||||
private var flattened = false
|
private var flattened = false
|
||||||
|
|
||||||
override fun get(id: Int): Item? {
|
override fun get(id: Int): Item {
|
||||||
return if (!flattened) {
|
return if (!flattened) {
|
||||||
val itemId = id ushr 16
|
val itemId = id ushr 16
|
||||||
val itemMeta = id and 0xFFFF
|
val itemMeta = id and 0xFFFF
|
||||||
@ -35,7 +35,11 @@ class ItemRegistry(
|
|||||||
if (itemMeta > 0 && itemMeta < Short.MAX_VALUE) {
|
if (itemMeta > 0 && itemMeta < Short.MAX_VALUE) {
|
||||||
versionItemId = versionItemId or itemMeta
|
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 {
|
} else {
|
||||||
super.get(id)
|
super.get(id)
|
||||||
}
|
}
|
||||||
|
@ -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.Font;
|
||||||
import de.bixilon.minosoft.gui.rendering.font.FontBindings;
|
import de.bixilon.minosoft.gui.rendering.font.FontBindings;
|
||||||
import de.bixilon.minosoft.gui.rendering.hud.HUDScale;
|
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.modding.event.events.annotations.Unsafe;
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||||
import de.bixilon.minosoft.util.hash.BetterHashSet;
|
import de.bixilon.minosoft.util.hash.BetterHashSet;
|
||||||
@ -33,7 +34,6 @@ import javax.annotation.Nullable;
|
|||||||
import java.text.CharacterIterator;
|
import java.text.CharacterIterator;
|
||||||
import java.text.StringCharacterIterator;
|
import java.text.StringCharacterIterator;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BaseComponent extends ChatComponent {
|
public class BaseComponent extends ChatComponent {
|
||||||
private static final String LEGACY_RESET_SUFFIX = String.valueOf(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR) + PostChatFormattingCodes.RESET.getChar();
|
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
|
@Override
|
||||||
public void addVerticies(Vec2 startPosition, Vec2 offset, Mat4 perspectiveMatrix, FontBindings binding, Font font, HUDScale hudScale, List<Float> 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) {
|
if (binding == FontBindings.RIGHT_DOWN || binding == FontBindings.RIGHT_UP) {
|
||||||
for (int i = this.parts.size() - 1; i >= 0; i--) {
|
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 {
|
} else {
|
||||||
for (var chatPart : this.parts) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.Font;
|
||||||
import de.bixilon.minosoft.gui.rendering.font.FontBindings;
|
import de.bixilon.minosoft.gui.rendering.font.FontBindings;
|
||||||
import de.bixilon.minosoft.gui.rendering.hud.HUDScale;
|
import de.bixilon.minosoft.gui.rendering.hud.HUDScale;
|
||||||
|
import de.bixilon.minosoft.gui.rendering.hud.elements.text.HUDFontMesh;
|
||||||
import glm_.mat4x4.Mat4;
|
import glm_.mat4x4.Mat4;
|
||||||
import glm_.vec2.Vec2;
|
import glm_.vec2.Vec2;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
@ -28,7 +29,6 @@ import javafx.collections.ObservableList;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class ChatComponent {
|
public abstract class ChatComponent {
|
||||||
public static ChatComponent valueOf(Object raw) {
|
public static ChatComponent valueOf(Object raw) {
|
||||||
@ -96,5 +96,5 @@ public abstract class ChatComponent {
|
|||||||
/**
|
/**
|
||||||
* @return Adds all verticies to the array (used in opengl)
|
* @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<Float> meshData, Vec2 maxSize);
|
public abstract void addVerticies(Vec2 startPosition, Vec2 offset, Mat4 perspectiveMatrix, FontBindings binding, Font font, HUDScale hudScale, HUDFontMesh mesh, Vec2 maxSize);
|
||||||
}
|
}
|
||||||
|
@ -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.FontBindings
|
||||||
import de.bixilon.minosoft.gui.rendering.font.FontChar
|
import de.bixilon.minosoft.gui.rendering.font.FontChar
|
||||||
import de.bixilon.minosoft.gui.rendering.hud.HUDScale
|
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.protocol.protocol.ProtocolDefinition
|
||||||
import de.bixilon.minosoft.util.Util
|
import de.bixilon.minosoft.util.Util
|
||||||
import de.bixilon.minosoft.util.hash.BetterHashSet
|
import de.bixilon.minosoft.util.hash.BetterHashSet
|
||||||
import glm_.mat4x4.Mat4
|
import glm_.mat4x4.Mat4
|
||||||
import glm_.vec2.Vec2
|
import glm_.vec2.Vec2
|
||||||
|
import glm_.vec3.Vec3
|
||||||
import glm_.vec4.Vec4
|
import glm_.vec4.Vec4
|
||||||
import javafx.animation.Animation
|
import javafx.animation.Animation
|
||||||
import javafx.animation.KeyFrame
|
import javafx.animation.KeyFrame
|
||||||
@ -201,25 +203,20 @@ open class TextComponent : ChatComponent {
|
|||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addVerticies(startPosition: Vec2, offset: Vec2, perspectiveMatrix: Mat4, binding: FontBindings, font: Font, hudScale: HUDScale, meshData: MutableList<Float>, maxSize: Vec2) {
|
override fun addVerticies(startPosition: Vec2, offset: Vec2, perspectiveMatrix: Mat4, binding: FontBindings, font: Font, hudScale: HUDScale, mesh: HUDFontMesh, maxSize: Vec2) {
|
||||||
fun drawLetterVertex(position: Vec2, uv: Vec2, atlasPage: Int, color: RGBColor, meshData: MutableList<Float>) {
|
|
||||||
|
fun drawLetterVertex(position: Vec3, uv: Vec2, atlasPage: Int) {
|
||||||
val matrixPosition = perspectiveMatrix * Vec4(position.x, position.y, 0f, 1f)
|
val matrixPosition = perspectiveMatrix * Vec4(position.x, position.y, 0f, 1f)
|
||||||
meshData.add(matrixPosition.x)
|
mesh.addVertex(Vec3(matrixPosition.x, matrixPosition.y, position.z), uv, atlasPage, color)
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun drawLetter(position: Vec2, scaledWidth: Float, scaledHeight: Float, fontChar: FontChar, color: RGBColor, meshData: MutableList<Float>) {
|
fun drawLetter(position: Vec2, scaledWidth: Float, scaledHeight: Float, fontChar: FontChar) {
|
||||||
drawLetterVertex(Vec2(position.x, position.y), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][0]], fontChar.atlasTextureIndex, color, meshData)
|
drawLetterVertex(Vec3(position.x, position.y, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][0]], fontChar.atlasTextureIndex)
|
||||||
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex, color, meshData)
|
drawLetterVertex(Vec3(position.x, position.y + scaledHeight, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex)
|
||||||
drawLetterVertex(Vec2(position.x + scaledWidth, position.y), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex, color, meshData)
|
drawLetterVertex(Vec3(position.x + scaledWidth, position.y, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex)
|
||||||
drawLetterVertex(Vec2(position.x + scaledWidth, position.y), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex, color, meshData)
|
drawLetterVertex(Vec3(position.x + scaledWidth, position.y, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][1]], fontChar.atlasTextureIndex)
|
||||||
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex, color, meshData)
|
drawLetterVertex(Vec3(position.x, position.y + scaledHeight, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][3]], fontChar.atlasTextureIndex)
|
||||||
drawLetterVertex(Vec2(position.x + scaledWidth, position.y + scaledHeight), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][2]], fontChar.atlasTextureIndex, color, meshData)
|
drawLetterVertex(Vec3(position.x + scaledWidth, position.y + scaledHeight, HUD_Z_COORDINATE), fontChar.texturePosition[texturePositionCoordinates[binding.ordinal][2]], fontChar.atlasTextureIndex)
|
||||||
}
|
}
|
||||||
// reverse text if right bound
|
// reverse text if right bound
|
||||||
val charArray = when (binding) {
|
val charArray = when (binding) {
|
||||||
@ -260,7 +257,7 @@ open class TextComponent : ChatComponent {
|
|||||||
}
|
}
|
||||||
val fontChar = font.getChar(char)
|
val fontChar = font.getChar(char)
|
||||||
val scaledX = fontChar.width * (font.charHeight / fontChar.height.toFloat()) * hudScale.scale
|
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)
|
offset += Vec2(scaledX + (hudScale.scale / 2), 0f)
|
||||||
if (offset.x >= maxSize.x) {
|
if (offset.x >= maxSize.x) {
|
||||||
maxSize.x += scaledX + (hudScale.scale / 2)
|
maxSize.x += scaledX + (hudScale.scale / 2)
|
||||||
@ -280,5 +277,7 @@ open class TextComponent : ChatComponent {
|
|||||||
listOf(2, 3, 0, 1),
|
listOf(2, 3, 0, 1),
|
||||||
listOf(3, 2, 1, 0),
|
listOf(3, 2, 1, 0),
|
||||||
) // matches FontBindings::ordinal
|
) // matches FontBindings::ordinal
|
||||||
|
|
||||||
|
const val HUD_Z_COORDINATE = -0.997f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,5 +18,5 @@ import de.bixilon.minosoft.data.world.BlockPosition
|
|||||||
|
|
||||||
interface BiomeAccessor {
|
interface BiomeAccessor {
|
||||||
|
|
||||||
fun getBiome(position: BlockPosition): Biome
|
fun getBiome(position: BlockPosition): Biome?
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,20 @@ package de.bixilon.minosoft.data.world.biome
|
|||||||
|
|
||||||
import de.bixilon.minosoft.data.mappings.biomes.Biome
|
import de.bixilon.minosoft.data.mappings.biomes.Biome
|
||||||
import de.bixilon.minosoft.data.world.BlockPosition
|
import de.bixilon.minosoft.data.world.BlockPosition
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
|
|
||||||
class NoiseBiomeAccessor(
|
class NoiseBiomeAccessor(
|
||||||
private val biomes: Array<Biome>,
|
private val biomes: Array<Biome>,
|
||||||
) : BiomeAccessor {
|
) : BiomeAccessor {
|
||||||
|
|
||||||
override fun getBiome(position: BlockPosition): Biome {
|
override fun getBiome(position: BlockPosition): Biome? {
|
||||||
val inChunk = position.getInChunkSectionLocation()
|
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).
|
// ToDo: This value is pseudo randomly generated. It depends on the seed of the world (received in join game).
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ import de.bixilon.minosoft.config.key.KeyAction
|
|||||||
import de.bixilon.minosoft.config.key.KeyCodes
|
import de.bixilon.minosoft.config.key.KeyCodes
|
||||||
import de.bixilon.minosoft.data.entities.EntityRotation
|
import de.bixilon.minosoft.data.entities.EntityRotation
|
||||||
import de.bixilon.minosoft.data.entities.Location
|
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.chunk.Frustum
|
||||||
import de.bixilon.minosoft.gui.rendering.shader.Shader
|
import de.bixilon.minosoft.gui.rendering.shader.Shader
|
||||||
import de.bixilon.minosoft.protocol.network.Connection
|
import de.bixilon.minosoft.protocol.network.Connection
|
||||||
@ -35,7 +39,6 @@ class Camera(
|
|||||||
var fov: Float,
|
var fov: Float,
|
||||||
private val renderWindow: RenderWindow,
|
private val renderWindow: RenderWindow,
|
||||||
) {
|
) {
|
||||||
lateinit var viewProjectionMatrix: Mat4
|
|
||||||
private var mouseSensitivity = Minosoft.getConfig().config.game.camera.moseSensitivity
|
private var mouseSensitivity = Minosoft.getConfig().config.game.camera.moseSensitivity
|
||||||
private var movementSpeed = 7
|
private var movementSpeed = 7
|
||||||
var cameraPosition = Vec3(0.0f, 0.0f, 0.0f)
|
var cameraPosition = Vec3(0.0f, 0.0f, 0.0f)
|
||||||
@ -52,6 +55,21 @@ class Camera(
|
|||||||
var cameraRight = Vec3(0.0f, 0.0f, -1.0f)
|
var cameraRight = Vec3(0.0f, 0.0f, -1.0f)
|
||||||
private var cameraUp = Vec3(0.0f, 1.0f, 0.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 screenHeight = 0
|
||||||
private var screenWidth = 0
|
private var screenWidth = 0
|
||||||
private val shaders: MutableSet<Shader> = mutableSetOf()
|
private val shaders: MutableSet<Shader> = mutableSetOf()
|
||||||
@ -176,10 +194,30 @@ class Camera(
|
|||||||
for (shader in shaders) {
|
for (shader in shaders) {
|
||||||
shader.use().setMat4("viewProjectionMatrix", matrix)
|
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
|
// recalculate sky color for current biome
|
||||||
val blockPosition = Location(cameraPosition).toBlockPosition()
|
val blockPosition = Location(cameraPosition).toBlockPosition()
|
||||||
renderWindow.setSkyColor(connection.player.world.getChunk(blockPosition.getChunkLocation())?.biomeAccessor?.getBiome(blockPosition)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR)
|
renderWindow.setSkyColor(connection.player.world.getChunk(blockPosition.getChunkLocation())?.biomeAccessor?.getBiome(blockPosition)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR)
|
||||||
connection.renderer.renderWindow.worldRenderer.recalculateFrustum(Frustum(this))
|
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 {
|
private fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int): Mat4 {
|
||||||
@ -216,7 +254,7 @@ class Camera(
|
|||||||
private fun sendPositionToServer() {
|
private fun sendPositionToServer() {
|
||||||
if (System.currentTimeMillis() - lastPositionChange > ProtocolDefinition.TICK_TIME) {
|
if (System.currentTimeMillis() - lastPositionChange > ProtocolDefinition.TICK_TIME) {
|
||||||
// ToDo: Replace this with proper movement and only send it, when our position changed
|
// 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()
|
lastPositionChange = System.currentTimeMillis()
|
||||||
currentPositionSent = true
|
currentPositionSent = true
|
||||||
return
|
return
|
||||||
@ -224,7 +262,14 @@ class Camera(
|
|||||||
currentPositionSent = false
|
currentPositionSent = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setPosition(location: Location) {
|
||||||
|
feetLocation = location
|
||||||
|
headLocation = Location(location.x, location.y + PLAYER_HEIGHT, location.z)
|
||||||
|
cameraPosition = headLocation.toVec3()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f)
|
private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,15 @@ import de.bixilon.minosoft.data.text.RGBColor
|
|||||||
|
|
||||||
object RenderConstants {
|
object RenderConstants {
|
||||||
val DEFAULT_SKY_COLOR = RGBColor("#ecff89")
|
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
|
||||||
}
|
}
|
||||||
|
@ -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 deltaTime = 0.0 // time between current frame and last frame
|
||||||
|
|
||||||
private var lastFrame = 0.0
|
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 val latch = CountUpAndDownLatch(1)
|
||||||
|
|
||||||
private var renderingStatus = RenderingStates.RUNNING
|
private var renderingStatus = RenderingStates.RUNNING
|
||||||
@ -59,6 +59,8 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
private var polygonEnabled = false
|
private var polygonEnabled = false
|
||||||
private var mouseCatch = !StaticConfiguration.DEBUG_MODE
|
private var mouseCatch = !StaticConfiguration.DEBUG_MODE
|
||||||
|
|
||||||
|
val tintColorCalculator = TintColorCalculator()
|
||||||
|
|
||||||
// all renderers
|
// all renderers
|
||||||
val worldRenderer: WorldRenderer = WorldRenderer(connection, connection.player.world, this)
|
val worldRenderer: WorldRenderer = WorldRenderer(connection, connection.player.world, this)
|
||||||
val hudRenderer: HUDRenderer = HUDRenderer(connection, this)
|
val hudRenderer: HUDRenderer = HUDRenderer(connection, this)
|
||||||
@ -82,7 +84,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
renderQueue.add {
|
renderQueue.add {
|
||||||
camera.cameraPosition = packet.location.toVec3()
|
camera.setPosition(packet.location)
|
||||||
camera.setRotation(packet.rotation.yaw.toDouble(), packet.rotation.pitch.toDouble())
|
camera.setRotation(packet.rotation.yaw.toDouble(), packet.rotation.pitch.toDouble())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -111,9 +113,10 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
glfwTerminate()
|
glfwTerminate()
|
||||||
throw RuntimeException("Failed to create the GLFW window")
|
throw RuntimeException("Failed to create the GLFW window")
|
||||||
}
|
}
|
||||||
camera = Camera(connection, Minosoft.getConfig().config.game.camera.fov, this)
|
|
||||||
camera.init(this)
|
camera.init(this)
|
||||||
|
|
||||||
|
tintColorCalculator.init(connection.version.assetsManager)
|
||||||
|
|
||||||
|
|
||||||
glfwSetKeyCallback(this.windowId) { _: Long, key: Int, _: Int, action: Int, _: Int ->
|
glfwSetKeyCallback(this.windowId) { _: Long, key: Int, _: Int, action: Int, _: Int ->
|
||||||
val keyCode = KeyCodes.KEY_CODE_GLFW_ID_MAP[key] ?: KeyCodes.KEY_UNKNOWN
|
val keyCode = KeyCodes.KEY_CODE_GLFW_ID_MAP[key] ?: KeyCodes.KEY_UNKNOWN
|
||||||
|
@ -13,13 +13,51 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering
|
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.text.RGBColor
|
||||||
|
import de.bixilon.minosoft.data.world.BlockPosition
|
||||||
|
|
||||||
object TintColorCalculator {
|
class TintColorCalculator {
|
||||||
fun getColor(color: Int): RGBColor? {
|
private lateinit var grassColorMap: Array<RGBColor>
|
||||||
if (color == 0) {
|
private lateinit var foliageColorMap: Array<RGBColor>
|
||||||
return null
|
|
||||||
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,22 +13,51 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.chunk
|
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_.BYTES
|
||||||
|
import glm_.vec2.Vec2
|
||||||
|
import glm_.vec3.Vec3
|
||||||
import org.lwjgl.opengl.GL11.GL_FLOAT
|
import org.lwjgl.opengl.GL11.GL_FLOAT
|
||||||
import org.lwjgl.opengl.GL20.glEnableVertexAttribArray
|
import org.lwjgl.opengl.GL20.glEnableVertexAttribArray
|
||||||
import org.lwjgl.opengl.GL20.glVertexAttribPointer
|
import org.lwjgl.opengl.GL20.glVertexAttribPointer
|
||||||
import org.lwjgl.opengl.GL30.*
|
import org.lwjgl.opengl.GL30.*
|
||||||
|
|
||||||
class WorldMesh(data: FloatArray) {
|
class ChunkMesh {
|
||||||
var vAO: Int = glGenVertexArrays()
|
private val data: MutableList<Float> = mutableListOf()
|
||||||
var vBO: Int = glGenBuffers()
|
private var vao: Int = 0
|
||||||
var trianglesCount: Int = data.size / FLOATS_PER_VERTEX
|
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).
|
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
|
||||||
glBindVertexArray(vAO)
|
glBindVertexArray(vao)
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vBO)
|
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
||||||
glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW)
|
glBufferData(GL_ARRAY_BUFFER, data.toFloatArray(), GL_STATIC_DRAW)
|
||||||
var index = 0
|
var index = 0
|
||||||
glVertexAttribPointer(index, 3, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, 0L)
|
glVertexAttribPointer(index, 3, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, 0L)
|
||||||
glEnableVertexAttribArray(index++)
|
glEnableVertexAttribArray(index++)
|
||||||
@ -53,13 +82,13 @@ class WorldMesh(data: FloatArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun draw() {
|
fun draw() {
|
||||||
glBindVertexArray(vAO)
|
glBindVertexArray(vao)
|
||||||
glDrawArrays(GL_TRIANGLES, 0, trianglesCount)
|
glDrawArrays(GL_TRIANGLES, 0, trianglesCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unload() {
|
fun unload() {
|
||||||
glDeleteVertexArrays(vAO)
|
glDeleteVertexArrays(vao)
|
||||||
glDeleteBuffers(vBO)
|
glDeleteBuffers(vbo)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
@ -14,8 +14,10 @@
|
|||||||
package de.bixilon.minosoft.gui.rendering.chunk
|
package de.bixilon.minosoft.gui.rendering.chunk
|
||||||
|
|
||||||
import de.bixilon.minosoft.Minosoft
|
import de.bixilon.minosoft.Minosoft
|
||||||
|
import de.bixilon.minosoft.config.StaticConfiguration
|
||||||
import de.bixilon.minosoft.data.Directions
|
import de.bixilon.minosoft.data.Directions
|
||||||
import de.bixilon.minosoft.data.mappings.blocks.BlockState
|
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.data.world.*
|
||||||
import de.bixilon.minosoft.gui.rendering.RenderWindow
|
import de.bixilon.minosoft.gui.rendering.RenderWindow
|
||||||
import de.bixilon.minosoft.gui.rendering.Renderer
|
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 {
|
class WorldRenderer(private val connection: Connection, private val world: World, val renderWindow: RenderWindow) : Renderer {
|
||||||
private lateinit var minecraftTextures: TextureArray
|
private lateinit var minecraftTextures: TextureArray
|
||||||
lateinit var chunkShader: Shader
|
lateinit var chunkShader: Shader
|
||||||
private val chunkSectionsToDraw = ConcurrentHashMap<ChunkLocation, ConcurrentHashMap<Int, WorldMesh>>()
|
private val chunkSectionsToDraw = ConcurrentHashMap<ChunkLocation, ConcurrentHashMap<Int, ChunkMesh>>()
|
||||||
private val visibleChunks: MutableSet<ChunkLocation> = mutableSetOf()
|
private val visibleChunks: MutableSet<ChunkLocation> = mutableSetOf()
|
||||||
private lateinit var frustum: Frustum
|
private lateinit var frustum: Frustum
|
||||||
private var currentTick = 0 // for animation usage
|
private var currentTick = 0 // for animation usage
|
||||||
private var lastTickIncrementTime = 0L
|
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)) {
|
if (frustum.containsChunk(chunkLocation, connection)) {
|
||||||
visibleChunks.add(chunkLocation)
|
visibleChunks.add(chunkLocation)
|
||||||
}
|
}
|
||||||
val data: MutableList<Float> = mutableListOf()
|
val mesh = ChunkMesh()
|
||||||
|
|
||||||
val below = world.allChunks[chunkLocation]?.sections?.get(sectionHeight - 1)
|
val below = world.allChunks[chunkLocation]?.sections?.get(sectionHeight - 1)
|
||||||
val above = 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("")
|
Log.debug("")
|
||||||
}
|
}
|
||||||
val biome = chunk.biomeAccessor.getBiome(blockPosition)
|
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() {
|
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) {
|
fun prepareChunkSection(chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection, chunk: Chunk) {
|
||||||
renderWindow.rendering.executor.execute {
|
renderWindow.rendering.executor.execute {
|
||||||
val data = prepareChunk(chunkLocation, sectionHeight, section, chunk)
|
val mesh = prepareChunk(chunkLocation, sectionHeight, section, chunk)
|
||||||
|
|
||||||
var sectionMap = chunkSectionsToDraw[chunkLocation]
|
var sectionMap = chunkSectionsToDraw[chunkLocation]
|
||||||
if (sectionMap == null) {
|
if (sectionMap == null) {
|
||||||
@ -168,9 +186,9 @@ class WorldRenderer(private val connection: Connection, private val world: World
|
|||||||
chunkSectionsToDraw[chunkLocation] = sectionMap
|
chunkSectionsToDraw[chunkLocation] = sectionMap
|
||||||
}
|
}
|
||||||
renderWindow.renderQueue.add {
|
renderWindow.renderQueue.add {
|
||||||
val newMesh = WorldMesh(data)
|
mesh.load()
|
||||||
sectionMap[sectionHeight]?.unload()
|
sectionMap[sectionHeight]?.unload()
|
||||||
sectionMap[sectionHeight] = newMesh
|
sectionMap[sectionHeight] = mesh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import java.util.*
|
|||||||
class BlockModelFace(data: JsonObject, from: Vec3, to: Vec3, direction: Directions) {
|
class BlockModelFace(data: JsonObject, from: Vec3, to: Vec3, direction: Directions) {
|
||||||
val textureName: String = data.get("texture").asString.removePrefix("#")
|
val textureName: String = data.get("texture").asString.removePrefix("#")
|
||||||
val cullFace: Directions?
|
val cullFace: Directions?
|
||||||
|
val tint: Boolean = data.has("tintindex")
|
||||||
private var positions: MutableList<Vec2>
|
private var positions: MutableList<Vec2>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -56,8 +57,8 @@ class BlockModelFace(data: JsonObject, from: Vec3, to: Vec3, direction: Directio
|
|||||||
positions = mutableListOf(
|
positions = mutableListOf(
|
||||||
uvToFloat(Vec2(textureStart.x, textureStart.y)),
|
uvToFloat(Vec2(textureStart.x, textureStart.y)),
|
||||||
uvToFloat(Vec2(textureStart.x, textureEnd.y)),
|
uvToFloat(Vec2(textureStart.x, textureEnd.y)),
|
||||||
uvToFloat(Vec2(textureEnd.x, textureEnd.y)),
|
uvToFloat(Vec2(textureEnd.x, textureEnd.y)),
|
||||||
uvToFloat(Vec2(textureEnd.x, textureStart.y)),
|
uvToFloat(Vec2(textureEnd.x, textureStart.y)),
|
||||||
)
|
)
|
||||||
val rotation = data["rotation"]?.asInt?.div(90) ?: 0
|
val rotation = data["rotation"]?.asInt?.div(90) ?: 0
|
||||||
Collections.rotate(positions, rotation)
|
Collections.rotate(positions, rotation)
|
||||||
|
@ -14,11 +14,10 @@
|
|||||||
package de.bixilon.minosoft.gui.rendering.chunk.models.renderable
|
package de.bixilon.minosoft.gui.rendering.chunk.models.renderable
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import de.bixilon.minosoft.config.StaticConfiguration
|
|
||||||
import de.bixilon.minosoft.data.Directions
|
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.text.RGBColor
|
||||||
import de.bixilon.minosoft.data.world.BlockInfo
|
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.chunk.models.loading.BlockModel
|
||||||
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||||
import glm_.mat4x4.Mat4
|
import glm_.mat4x4.Mat4
|
||||||
@ -71,7 +70,7 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(blockInfo: BlockInfo, biome: Biome, position: Vec3, data: MutableList<Float>, neighbourBlocks: Array<BlockInfo?>) {
|
fun render(blockInfo: BlockInfo, tintColor: RGBColor?, position: Vec3, mesh: ChunkMesh, neighbourBlocks: Array<BlockInfo?>) {
|
||||||
val modelMatrix = Mat4().translate(position)
|
val modelMatrix = Mat4().translate(position)
|
||||||
|
|
||||||
for (direction in Directions.DIRECTIONS) {
|
for (direction in Directions.DIRECTIONS) {
|
||||||
@ -92,12 +91,7 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var tintColor: RGBColor? = blockInfo.block.tintColor
|
element.render(tintColor, textureMapping, modelMatrix, direction, mesh)
|
||||||
|
|
||||||
if (StaticConfiguration.BIOME_DEBUG_MODE) {
|
|
||||||
tintColor = RGBColor(biome.hashCode())
|
|
||||||
}
|
|
||||||
element.render(tintColor, textureMapping, modelMatrix, direction, data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import com.google.gson.JsonObject
|
|||||||
import de.bixilon.minosoft.data.Axes
|
import de.bixilon.minosoft.data.Axes
|
||||||
import de.bixilon.minosoft.data.Directions
|
import de.bixilon.minosoft.data.Directions
|
||||||
import de.bixilon.minosoft.data.text.RGBColor
|
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.BlockModel
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace
|
||||||
@ -28,7 +29,7 @@ import glm_.vec2.Vec2
|
|||||||
import glm_.vec3.Vec3
|
import glm_.vec3.Vec3
|
||||||
import glm_.vec4.Vec4
|
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<Directions> = mutableSetOf()
|
private val fullFaceDirections: MutableSet<Directions> = mutableSetOf()
|
||||||
private val faces: MutableMap<Directions, BlockModelFace> = element.faces.toMutableMap()
|
private val faces: MutableMap<Directions, BlockModelFace> = element.faces.toMutableMap()
|
||||||
private var positions: Array<Vec3> = element.positions.clone()
|
private var positions: Array<Vec3> = element.positions.clone()
|
||||||
@ -49,7 +50,7 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolea
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun render(tintColor: RGBColor?, textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, data: MutableList<Float>) {
|
fun render(tintColor: RGBColor?, textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, mesh: ChunkMesh) {
|
||||||
val realDirection = directionMapping[direction]!!
|
val realDirection = directionMapping[direction]!!
|
||||||
val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal]
|
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]])
|
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<Vec3>, texturePositions: Array<Vec2?>) {
|
fun createQuad(drawPositions: Array<Vec3>, texturePositions: Array<Vec2?>) {
|
||||||
for (vertex in drawOrder) {
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,10 +17,6 @@ import de.bixilon.minosoft.config.StaticConfiguration
|
|||||||
import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
|
import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
|
||||||
import de.bixilon.minosoft.config.key.KeyAction
|
import de.bixilon.minosoft.config.key.KeyAction
|
||||||
import de.bixilon.minosoft.config.key.KeyCodes
|
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.gui.rendering.font.FontBindings
|
||||||
import de.bixilon.minosoft.modding.loading.ModLoader
|
import de.bixilon.minosoft.modding.loading.ModLoader
|
||||||
import de.bixilon.minosoft.util.GitInfo
|
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 systemMemoryText: String = formatBytes(systemInfoHardwareAbstractionLayer.memory.total)
|
||||||
private val osText: String = "${System.getProperty("os.name")}: ${systemInfo.operatingSystem.family} ${systemInfo.operatingSystem.bitness}bit"
|
private val osText: String = "${System.getProperty("os.name")}: ${systemInfo.operatingSystem.family} ${systemInfo.operatingSystem.bitness}bit"
|
||||||
|
|
||||||
// only calculate once per update values
|
private val camera = hudTextElement.renderWindow.camera
|
||||||
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 var debugScreenEnabled = StaticConfiguration.DEBUG_MODE
|
private var debugScreenEnabled = StaticConfiguration.DEBUG_MODE
|
||||||
|
|
||||||
@ -54,7 +45,6 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
|
|||||||
if (!debugScreenEnabled) {
|
if (!debugScreenEnabled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
calculateDynamicValues()
|
|
||||||
|
|
||||||
chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf(
|
chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf(
|
||||||
"FPS: ${getFPS()}",
|
"FPS: ${getFPS()}",
|
||||||
@ -66,6 +56,7 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
|
|||||||
"Chunk ${getChunkLocation()}",
|
"Chunk ${getChunkLocation()}",
|
||||||
"Facing ${getFacing()}",
|
"Facing ${getFacing()}",
|
||||||
"Dimension ${hudTextElement.connection.player.world.dimension}",
|
"Dimension ${hudTextElement.connection.player.world.dimension}",
|
||||||
|
"Biome ${camera.currentBiome}",
|
||||||
))
|
))
|
||||||
chatComponents[FontBindings.RIGHT_UP]!!.addAll(listOf(
|
chatComponents[FontBindings.RIGHT_UP]!!.addAll(listOf(
|
||||||
"Java: ${Runtime.version()} ${System.getProperty("sun.arch.data.model")}bit",
|
"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 {
|
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 {
|
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 {
|
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 {
|
private fun getFacing(): String {
|
||||||
@ -170,14 +161,6 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
|
|||||||
return "todo (${formatRotation(yaw)} / ${formatRotation(pitch)})"
|
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 {
|
companion object {
|
||||||
private val UNITS = listOf("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB")
|
private val UNITS = listOf("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB")
|
||||||
|
@ -13,46 +13,67 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.hud.elements.text
|
package de.bixilon.minosoft.gui.rendering.hud.elements.text
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.data.text.RGBColor
|
||||||
import glm_.BYTES
|
import glm_.BYTES
|
||||||
|
import glm_.vec2.Vec2
|
||||||
|
import glm_.vec3.Vec3
|
||||||
import org.lwjgl.opengl.GL11.GL_FLOAT
|
import org.lwjgl.opengl.GL11.GL_FLOAT
|
||||||
import org.lwjgl.opengl.GL20.glEnableVertexAttribArray
|
import org.lwjgl.opengl.GL20.glEnableVertexAttribArray
|
||||||
import org.lwjgl.opengl.GL20.glVertexAttribPointer
|
import org.lwjgl.opengl.GL20.glVertexAttribPointer
|
||||||
import org.lwjgl.opengl.GL30.*
|
import org.lwjgl.opengl.GL30.*
|
||||||
|
|
||||||
class HUDFontMesh(data: FloatArray) {
|
class HUDFontMesh {
|
||||||
var vAO: Int = glGenVertexArrays()
|
private val data: MutableList<Float> = mutableListOf()
|
||||||
var vBO: Int = glGenBuffers()
|
private var vao: Int = 0
|
||||||
var trianglesCount: Int = data.size / FLOAT_PER_VERTEX
|
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() {
|
fun draw() {
|
||||||
glBindVertexArray(vAO)
|
glBindVertexArray(vao)
|
||||||
glDrawArrays(GL_TRIANGLES, 0, trianglesCount)
|
glDrawArrays(GL_TRIANGLES, 0, trianglesCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unload() {
|
fun unload() {
|
||||||
glDeleteVertexArrays(vAO)
|
glDeleteVertexArrays(vao)
|
||||||
glDeleteBuffers(vBO)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val FLOAT_PER_VERTEX = 7
|
private const val FLOATS_PER_VERTEX = 7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import de.bixilon.minosoft.protocol.network.Connection
|
|||||||
import glm_.glm
|
import glm_.glm
|
||||||
import glm_.mat4x4.Mat4
|
import glm_.mat4x4.Mat4
|
||||||
import glm_.vec2.Vec2
|
import glm_.vec2.Vec2
|
||||||
|
import glm_.vec3.Vec3
|
||||||
import glm_.vec4.Vec4
|
import glm_.vec4.Vec4
|
||||||
|
|
||||||
class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, val renderWindow: RenderWindow) : HUDElement {
|
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()
|
prepare()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawTextBackground(start: Vec2, end: Vec2, perspectiveMatrix: Mat4, meshData: MutableList<Float>) {
|
private fun drawTextBackground(start: Vec2, end: Vec2, perspectiveMatrix: Mat4, mesh: HUDFontMesh) {
|
||||||
fun drawLetterVertex(position: Vec2) {
|
fun drawLetterVertex(position: Vec2) {
|
||||||
val matrixPosition = perspectiveMatrix * Vec4(position.x, position.y, 0f, 1f)
|
val matrixPosition = perspectiveMatrix * Vec4(position.x, position.y, 0f, 1f)
|
||||||
meshData.add(matrixPosition.x)
|
mesh.addVertex(
|
||||||
meshData.add(matrixPosition.y)
|
position = Vec3(matrixPosition.x, matrixPosition.y, TEXT_BACKGROUND_Z),
|
||||||
meshData.add(-0.995f)
|
textureCoordinates = TEXT_BACKGROUND_ATLAS_COORDINATES,
|
||||||
|
atlasPage = TEXT_BACKGROUND_ATLAS_INDEX,
|
||||||
meshData.add(0f)
|
color = TEXT_BACKGROUND_COLOR,
|
||||||
meshData.add(0f)
|
)
|
||||||
meshData.add(Float.fromBits(0))
|
|
||||||
|
|
||||||
meshData.add(Float.fromBits(RGBColor(0, 0, 0, 76).color))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawLetterVertex(start)
|
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<Float>, maxSize: Vec2) {
|
fun drawChatComponent(position: Vec2, binding: FontBindings, text: ChatComponent, mesh: HUDFontMesh, maxSize: Vec2) {
|
||||||
if (text.message.isBlank()) {
|
if (text.message.isBlank()) {
|
||||||
maxSize += Vec2(0, font.charHeight * hudRenderer.hudScale.scale)
|
maxSize += Vec2(0, font.charHeight * hudRenderer.hudScale.scale)
|
||||||
return
|
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() {
|
override fun prepare() {
|
||||||
@ -105,7 +103,7 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v
|
|||||||
hudTextElement.update()
|
hudTextElement.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
val meshData: MutableList<Float> = mutableListOf()
|
val mesh = HUDFontMesh()
|
||||||
|
|
||||||
for ((binding, components) in componentsBindingMap) {
|
for ((binding, components) in componentsBindingMap) {
|
||||||
val offset = Vec2(3, 3)
|
val offset = Vec2(3, 3)
|
||||||
@ -116,12 +114,13 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v
|
|||||||
|
|
||||||
for ((_, component) in components.withIndex()) {
|
for ((_, component) in components.withIndex()) {
|
||||||
val currentOffset = Vec2()
|
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)
|
offset += Vec2(0, currentOffset.y + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mesh.load()
|
||||||
hudMeshHUD.unload()
|
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 = font.createAtlasTexture()
|
||||||
fontAtlasTexture.load()
|
fontAtlasTexture.load()
|
||||||
|
|
||||||
hudMeshHUD = HUDFontMesh(floatArrayOf())
|
hudMeshHUD = HUDFontMesh()
|
||||||
|
hudMeshHUD.load()
|
||||||
|
|
||||||
fontShader = Shader("font_vertex.glsl", "font_fragment.glsl")
|
fontShader = Shader("font_vertex.glsl", "font_fragment.glsl")
|
||||||
fontShader.load()
|
fontShader.load()
|
||||||
@ -157,4 +157,13 @@ class HUDTextElement(val connection: Connection, val hudRenderer: HUDRenderer, v
|
|||||||
}
|
}
|
||||||
hudMeshHUD.draw()
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class PacketJoinGame : ClientboundPacket() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.versionId < ProtocolVersions.V_1_9_1) {
|
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())
|
difficulty = Difficulties.byId(buffer.readUnsignedByte().toInt())
|
||||||
maxPlayers = buffer.readByte().toInt()
|
maxPlayers = buffer.readByte().toInt()
|
||||||
if (buffer.versionId >= ProtocolVersions.V_13W42B) {
|
if (buffer.versionId >= ProtocolVersions.V_13W42B) {
|
||||||
@ -80,7 +80,7 @@ class PacketJoinGame : ClientboundPacket() {
|
|||||||
buffer.readStringArray() // world
|
buffer.readStringArray() // world
|
||||||
}
|
}
|
||||||
if (buffer.versionId < ProtocolVersions.V_20W21A) {
|
if (buffer.versionId < ProtocolVersions.V_20W21A) {
|
||||||
dimension = buffer.connection.mapping.dimensionRegistry.get(buffer.readInt())!!
|
dimension = buffer.connection.mapping.dimensionRegistry.get(buffer.readInt())
|
||||||
} else {
|
} else {
|
||||||
val dimensionCodec = buffer.readNBT()
|
val dimensionCodec = buffer.readNBT()
|
||||||
dimensions = parseDimensionCodec(dimensionCodec, buffer.versionId)
|
dimensions = parseDimensionCodec(dimensionCodec, buffer.versionId)
|
||||||
|
@ -88,6 +88,10 @@ public final class ProtocolDefinition {
|
|||||||
public static final int TICKS_PER_SECOND = 20;
|
public static final int TICKS_PER_SECOND = 20;
|
||||||
public static final int TICK_TIME = 1000 / TICKS_PER_SECOND;
|
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 DEFAULT_DIMENSION = new ResourceLocation("overworld");
|
||||||
public static final ResourceLocation NETHER_DIMENSION = new ResourceLocation("nether");
|
public static final ResourceLocation NETHER_DIMENSION = new ResourceLocation("nether");
|
||||||
|
36
src/main/java/de/bixilon/minosoft/util/MMath.kt
Normal file
36
src/main/java/de/bixilon/minosoft/util/MMath.kt
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user