From 7bfc18ebdb94bae38533a25daf45dfa4db4a31f9 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 13 Mar 2021 22:12:42 +0100 Subject: [PATCH] rendering: add support for liquids --- .../de/bixilon/minosoft/data/Directions.kt | 8 + .../data/mappings/blocks/BlockState.kt | 22 +- .../data/mappings/versions/VersionMapping.kt | 1 - .../gui/rendering/chunk/WorldRenderer.kt | 2 +- .../chunk/models/loading/BlockModelFace.kt | 62 +++-- .../models/renderable/BlockRenderInterface.kt | 37 +++ .../chunk/models/renderable/BlockRenderer.kt | 26 +-- .../models/renderable/ElementRenderer.kt | 23 +- .../chunk/models/renderable/FluidRenderer.kt | 220 ++++++++++++++++++ .../minosoft/gui/rendering/util/VecUtil.kt | 4 + 10 files changed, 345 insertions(+), 60 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt diff --git a/src/main/java/de/bixilon/minosoft/data/Directions.kt b/src/main/java/de/bixilon/minosoft/data/Directions.kt index 8c5d46522..6217c5649 100644 --- a/src/main/java/de/bixilon/minosoft/data/Directions.kt +++ b/src/main/java/de/bixilon/minosoft/data/Directions.kt @@ -31,6 +31,14 @@ enum class Directions(direction: Vec3) { } } + fun sidesNextTo(direction: Directions): Set { + return when (direction) { + NORTH, SOUTH -> setOf(EAST, WEST) + EAST, WEST -> setOf(NORTH, SOUTH) + else -> emptySet() + } + } + val directionVector: Vec3 = direction companion object { 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 391ec3ef3..f1bb36eed 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 @@ -22,7 +22,9 @@ import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.gui.rendering.TintColorCalculator import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel +import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.BlockRenderInterface import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.BlockRenderer +import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.FluidRenderer import java.util.* import kotlin.random.Random @@ -30,7 +32,7 @@ data class BlockState( val owner: Block, val properties: Set = setOf(), val rotation: BlockRotations = BlockRotations.NONE, - val renders: Set = setOf(), + val renders: Set = setOf(), val tintColor: RGBColor? = null, ) { @@ -104,7 +106,7 @@ data class BlockState( return String.format("%s%s", owner.resourceLocation, out) } - fun getBlockRenderer(position: BlockPosition): BlockRenderer { + fun getBlockRenderer(position: BlockPosition): BlockRenderInterface { if (Minosoft.getConfig().config.game.other.antiMoirePattern) { // ToDo: Support weight attribute return renders.random(Random(position.hashCode())) @@ -116,11 +118,16 @@ data class BlockState( companion object { val ROTATION_PROPERTIES = setOf("facing", "rotation", "orientation", "axis") + val SPECIAL_RENDERERS = mutableMapOf( + Pair("water", FluidRenderer("block/water_still", "block/water_flow", "water")), + Pair("lava", FluidRenderer("block/lava_still", "block/lava_flow", "lava")), + ) + fun deserialize(owner: Block, data: JsonObject, models: HashBiMap): BlockState { val (rotation, properties) = data["properties"]?.asJsonObject?.let { getProperties(it) } ?: Pair(BlockRotations.NONE, mutableSetOf()) - val renders: MutableSet = mutableSetOf() + val renders: MutableSet = mutableSetOf() data["render"]?.let { when (it) { @@ -149,6 +156,13 @@ data class BlockState( val tintColor: RGBColor? = data["tint_color"]?.asInt?.let { TintColorCalculator.getJsonColor(it) } ?: owner.tintColor + for ((regex, renderer) in SPECIAL_RENDERERS) { + if (owner.resourceLocation.full.contains(regex)) { + renders.clear() + renders.add(renderer) + } + } + return BlockState( owner = owner, properties = properties.toSet(), @@ -192,7 +206,7 @@ data class BlockState( return Pair(rotation, properties) } - private fun addBlockModel(data: JsonObject, renders: MutableSet, models: HashBiMap) { + private fun addBlockModel(data: JsonObject, renders: MutableSet, models: HashBiMap) { val model = models[ResourceLocation(data["model"].asString)] ?: error("Can not find block model ${data["model"]}") renders.add(BlockRenderer(data, model)) } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/versions/VersionMapping.kt b/src/main/java/de/bixilon/minosoft/data/mappings/versions/VersionMapping.kt index de3ef678a..b14a088d1 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/versions/VersionMapping.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/versions/VersionMapping.kt @@ -154,7 +154,6 @@ class VersionMapping(var version: Version?) { } } - private fun loadBlockModels(data: Map) { for ((resourceLocation, model) in data) { if (models.containsKey(resourceLocation)) { 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 c22c308e7..f164ead2f 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 @@ -87,7 +87,7 @@ class WorldRenderer( blockInfo.block.tintColor?.let { tintColor = it } } - blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, world.worldLightAccessor, tintColor, blockPosition, mesh, neighborBlocks) + blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, world.worldLightAccessor, tintColor, blockPosition, mesh, neighborBlocks, world) } } return 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 2cb4e01b2..e01fd359e 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 @@ -13,6 +13,7 @@ package de.bixilon.minosoft.gui.rendering.chunk.models.loading +import com.google.gson.JsonArray import com.google.gson.JsonObject import de.bixilon.minosoft.data.Directions import de.bixilon.minosoft.gui.rendering.util.VecUtil @@ -22,7 +23,7 @@ import glm_.vec3.Vec3 import java.util.* class BlockModelFace { - val textureName: String + val textureName: String? val cullFace: Directions? val tint: Boolean private val positions: MutableList @@ -30,26 +31,6 @@ class BlockModelFace { constructor(data: JsonObject, from: Vec3, to: Vec3, direction: Directions) { tint = data.has("tintindex") textureName = data.get("texture").asString.removePrefix("#") - var textureTopLeft = Vec2(0, 16) - var textureBottomRight = Vec2(16, 0) - when (direction) { - Directions.EAST, Directions.WEST -> run { - textureTopLeft = Vec2(from.z.toInt(), to.y.toInt()) - textureBottomRight = Vec2(to.z.toInt(), from.y.toInt()) - } - Directions.UP, Directions.DOWN -> { - textureTopLeft = Vec2(from.x.toInt(), to.z.toInt()) - textureBottomRight = Vec2(to.x.toInt(), from.z.toInt()) - } - Directions.NORTH, Directions.SOUTH -> { - textureTopLeft = Vec2(from.x.toInt(), to.y.toInt()) - textureBottomRight = Vec2(to.x.toInt(), from.y.toInt()) - } - } - data["uv"]?.asJsonArray?.let { - textureTopLeft = Vec2(it[0].asFloat, it[3].asFloat) - textureBottomRight = Vec2(it[2].asFloat, it[1].asFloat) - } cullFace = data["cullface"]?.asString?.let { return@let if (it == "bottom") { Directions.DOWN @@ -57,14 +38,33 @@ class BlockModelFace { Directions.valueOf(it.toUpperCase()) } } - positions = mutableListOf( + positions = calculateTexturePositions(data, from, to, direction) + val rotation = data["rotation"]?.asInt?.div(90) ?: 0 + Collections.rotate(positions, rotation) + } + + private fun calculateTexturePositions(data: JsonObject?, from: Vec3, to: Vec3, direction: Directions): MutableList { + val (textureTopLeft: Vec2, textureBottomRight: Vec2) = data?.get("uv")?.asJsonArray?.let { + readUV(it) + } ?: getTexturePositionsFromRegion(from, to, direction) + return mutableListOf( uvToFloat(Vec2(textureTopLeft.x, textureTopLeft.y)), uvToFloat(Vec2(textureTopLeft.x, textureBottomRight.y)), uvToFloat(Vec2(textureBottomRight.x, textureBottomRight.y)), uvToFloat(Vec2(textureBottomRight.x, textureTopLeft.y)), ) - val rotation = data["rotation"]?.asInt?.div(90) ?: 0 - Collections.rotate(positions, rotation) + } + + private fun readUV(data: JsonArray): Pair { + return Pair(Vec2(data[0].asFloat, data[3].asFloat), Vec2(data[2].asFloat, data[1].asFloat)) + } + + private fun getTexturePositionsFromRegion(from: Vec3, to: Vec3, direction: Directions): Pair { + return when (direction) { + Directions.EAST, Directions.WEST -> Pair(Vec2(from.z.toInt(), to.y.toInt()), Vec2(to.z.toInt(), from.y.toInt())) + Directions.UP, Directions.DOWN -> Pair(Vec2(from.x.toInt(), to.z.toInt()), Vec2(to.x.toInt(), from.z.toInt())) + Directions.NORTH, Directions.SOUTH -> Pair(Vec2(from.x.toInt(), to.y.toInt()), Vec2(to.x.toInt(), from.y.toInt())) + } } constructor(parent: BlockModelFace) { @@ -77,6 +77,20 @@ class BlockModelFace { } } + constructor() { + textureName = null + cullFace = null + tint = false + positions = calculateTexturePositions(null, VecUtil.EMPTY_VECTOR, VecUtil.BLOCK_SIZE_VECTOR, Directions.EAST) + } + + constructor(from: Vec3, to: Vec3, direction: Directions) { + textureName = null + cullFace = null + tint = false + positions = calculateTexturePositions(null, from, to, direction) + } + fun getTexturePositionArray(direction: Directions): Array { val template = textureTemplate[direction.ordinal] val result = arrayOfNulls(template.size) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt new file mode 100644 index 000000000..486ab9c08 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt @@ -0,0 +1,37 @@ +package de.bixilon.minosoft.gui.rendering.chunk.models.renderable + +import de.bixilon.minosoft.data.Directions +import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.data.world.BlockInfo +import de.bixilon.minosoft.data.world.BlockPosition +import de.bixilon.minosoft.data.world.World +import de.bixilon.minosoft.data.world.light.LightAccessor +import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh +import de.bixilon.minosoft.gui.rendering.textures.Texture + +interface BlockRenderInterface { + val fullFaceDirections: MutableSet + val transparentFaces: MutableSet + + fun render(blockInfo: BlockInfo, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array, world: World) + + fun resolveTextures(indexed: MutableList, textureMap: MutableMap) + + fun postInit() {} + + companion object { + fun resolveTexture(indexed: MutableList, textureMap: MutableMap, textureName: String): Texture? { + var texture: Texture? = null + val index: Int? = textureMap[textureName]?.let { + texture = it + indexed.indexOf(it) + } + if (index == null) { + texture = Texture(Texture.getResourceTextureIdentifier(textureName = textureName)) + textureMap[textureName] = texture!! + indexed.add(texture!!) + } + return texture + } + } +} 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 d748db85b..f23c272a1 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 @@ -20,6 +20,7 @@ import de.bixilon.minosoft.data.mappings.ResourceLocation import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockInfo import de.bixilon.minosoft.data.world.BlockPosition +import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.light.LightAccessor import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel @@ -27,13 +28,13 @@ import de.bixilon.minosoft.gui.rendering.textures.Texture import de.bixilon.minosoft.gui.rendering.textures.TextureTransparencies import glm_.mat4x4.Mat4 -class BlockRenderer { - private val transparentFaces: MutableSet = mutableSetOf() +class BlockRenderer: BlockRenderInterface { private val cullFaces: MutableSet = mutableSetOf() val textures: MutableMap = mutableMapOf() - private val fullFaceDirections: MutableSet = mutableSetOf() private val elements: MutableSet = mutableSetOf() private val textureMapping: MutableMap = mutableMapOf() + override val fullFaceDirections: MutableSet = mutableSetOf() + override val transparentFaces: MutableSet = mutableSetOf() constructor(data: JsonObject, parent: BlockModel) { val newElements = ElementRenderer.createElements(data, parent) @@ -51,26 +52,15 @@ class BlockRenderer { } } - - fun resolveTextures(indexed: MutableList, textureMap: MutableMap) { + override fun resolveTextures(indexed: MutableList, textureMap: MutableMap) { for ((key, textureName) in textures) { if (!textureName.startsWith("#")) { - var texture: Texture? = null - val index: Int? = textureMap[textureName]?.let { - texture = it - indexed.indexOf(it) - } - if (index == null) { - texture = Texture(Texture.getResourceTextureIdentifier(textureName = textureName)) - textureMap[textureName] = texture!! - indexed.add(texture!!) - } - textureMapping[key] = texture!! + textureMapping[key] = BlockRenderInterface.resolveTexture(indexed, textureMap, textureName = textureName)!! } } } - fun postInit() { + override fun postInit() { for (direction in Directions.DIRECTIONS) { var directionIsCullface: Boolean? = null var directionIsNotTransparent: Boolean? = null @@ -103,7 +93,7 @@ class BlockRenderer { } } - fun render(blockInfo: BlockInfo, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array) { + override fun render(blockInfo: BlockInfo, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array, world: World) { val modelMatrix = Mat4().translate(position.toVec3()) for (direction in Directions.DIRECTIONS) { 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 905c08e1b..877282341 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,8 @@ import com.google.common.collect.HashBiMap import com.google.gson.JsonObject import de.bixilon.minosoft.data.Axes import de.bixilon.minosoft.data.Directions +import de.bixilon.minosoft.data.mappings.ResourceLocation +import de.bixilon.minosoft.data.mappings.versions.VersionMapping import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.light.LightAccessor @@ -67,7 +69,6 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo val realDirection = directionMapping.inverse()[direction]!! val face = faces[realDirection] ?: return // Not our face - val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal] val texture = textureMapping[face.textureName] ?: TODO() @@ -110,8 +111,6 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo } companion object { - private val EMPTY_VECTOR = Vec3() - val DRAW_ODER = arrayOf( Pair(0, 1), Pair(3, 2), @@ -151,7 +150,7 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo } fun getRotatedDirection(rotation: Vec3, direction: Directions): Directions { - if (rotation == EMPTY_VECTOR) { + if (rotation == VecUtil.EMPTY_VECTOR) { return direction } var rotatedDirectionVector = VecUtil.rotateVector(direction.directionVector, rotation.x, Axes.X) @@ -160,18 +159,18 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo } fun rotatePositionsAxes(positions: Array, angles: Vec3, rescale: Boolean) { - if (angles == EMPTY_VECTOR) { + if (angles == VecUtil.EMPTY_VECTOR) { return } - BlockModelElement.rotatePositions(positions, Axes.X, angles.x, EMPTY_VECTOR, rescale) - BlockModelElement.rotatePositions(positions, Axes.Y, angles.y, EMPTY_VECTOR, rescale) - BlockModelElement.rotatePositions(positions, Axes.Z, angles.z, EMPTY_VECTOR, rescale) + BlockModelElement.rotatePositions(positions, Axes.X, angles.x, VecUtil.EMPTY_VECTOR, rescale) + BlockModelElement.rotatePositions(positions, Axes.Y, angles.y, VecUtil.EMPTY_VECTOR, rescale) + BlockModelElement.rotatePositions(positions, Axes.Z, angles.z, VecUtil.EMPTY_VECTOR, rescale) } - private val POSITION_1 = Vec3(-0.5f, -0.5f, -0.5f) - private val POSITION_2 = Vec3(+0.5f, -0.5f, -0.5f) - private val POSITION_3 = Vec3(-0.5f, -0.5f, +0.5f) - private val POSITION_4 = Vec3(+0.5f, -0.5f, +0.5f) + val POSITION_1 = Vec3(-0.5f, -0.5f, -0.5f) + val POSITION_2 = Vec3(+0.5f, -0.5f, -0.5f) + val POSITION_3 = Vec3(-0.5f, -0.5f, +0.5f) + val POSITION_4 = Vec3(+0.5f, -0.5f, +0.5f) private val POSITION_5 = Vec3(-0.5f, +0.5f, -0.5f) private val POSITION_6 = Vec3(+0.5f, +0.5f, +0.5f) private val POSITION_7 = Vec3(-0.5f, +0.5f, +0.5f) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt new file mode 100644 index 000000000..40d610d66 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt @@ -0,0 +1,220 @@ +package de.bixilon.minosoft.gui.rendering.chunk.models.renderable + +import de.bixilon.minosoft.data.Directions +import de.bixilon.minosoft.data.mappings.blocks.BlockProperties +import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.data.world.BlockInfo +import de.bixilon.minosoft.data.world.BlockPosition +import de.bixilon.minosoft.data.world.World +import de.bixilon.minosoft.data.world.light.LightAccessor +import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh +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.textures.Texture +import de.bixilon.minosoft.gui.rendering.util.VecUtil +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.toInt +import glm_.vec2.Vec2 +import glm_.vec3.Vec3 +import glm_.vec4.Vec4 + +class FluidRenderer(private val stillTextureName: String, private val flowingTextureName: String, val regex: String): BlockRenderInterface { + override val fullFaceDirections: MutableSet = mutableSetOf() + override val transparentFaces: MutableSet = Directions.DIRECTIONS.toMutableSet() + private var still: Texture? = null + private var flowing: Texture? = null + + override fun render(blockInfo: BlockInfo, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array, world: World) { + val modelMatrix = Mat4().translate(position.toVec3()) + val lightLevel = lightAccessor.getLightLevel(position) + val heights = calculateHeights(neighbourBlocks, blockInfo, world, position) + val (texture, angle) = if (isLiquidFlowing(heights)) { + Pair(flowing, getRotationAngle(heights)) + } else { + Pair(still, 0f) + } + val positions = calculatePositions(heights) + for (direction in Directions.DIRECTIONS) { + if (isBlockSameFluid(neighbourBlocks[direction.ordinal]) || neighbourBlocks[direction.ordinal]?.block?.getBlockRenderer(position + direction)?.fullFaceDirections?.contains(direction.inverse()) == true && direction != Directions.UP) { + continue + } + val face = BlockModelFace(VecUtil.EMPTY_VECTOR, Vec3(VecUtil.BLOCK_SIZE_VECTOR.x, positions[7].y * 8, VecUtil.BLOCK_SIZE_VECTOR.z), direction) + face.rotate(angle) + val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[direction.ordinal] + val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]]) + createQuad(drawPositions, face.getTexturePositionArray(direction), texture!!, modelMatrix, mesh, tintColor, lightLevel) + } + } + + private fun getRotationAngle(heights: FloatArray): Float { + val maxHeight = heights.maxOrNull() + for (direction in Directions.SIDES) { + val positions = getPositionsForDirection(direction) + val currentHeights = mutableListOf() + for (position in positions) { + currentHeights.add(heights[position]) + } + val allCurrentHeightsAreEqual = currentHeights.toSet().size == 1 + if (allCurrentHeightsAreEqual) { + if (maxHeight == currentHeights[0]) { + return getRotationAngle(direction) + } + } + } + val minHeight = heights.minOrNull() + val position = heights.indexOfFirst { it == minHeight } + val directions = HEIGHT_POSITIONS_REVERSED[position] + var angle = 0f + for (direction in directions!!) { + angle += getRotationAngle(direction) + } + return angle / directions.size + } + + private fun getRotationAngle(direction: Directions): Float { + return when (direction) { + Directions.SOUTH -> glm.PI.toFloat() + Directions.NORTH -> 0f + Directions.WEST -> glm.PI.toFloat() * 0.5f + Directions.EAST -> glm.PI.toFloat() * 1.5f + else -> error("Unexpected value: $direction") + } + } + + private fun isLiquidFlowing(heights: FloatArray): Boolean { + return heights.toSet().size != 1 // liquid is flowing, if not all of the heights are the same + } + + private fun createQuad(drawPositions: Array, texturePositions: Array, texture: Texture, modelMatrix: Mat4, mesh: SectionArrayMesh, tintColor: RGBColor?, lightLevel: Int) { + for (vertex in ElementRenderer.DRAW_ODER) { + val input = Vec4(drawPositions[vertex.first], 1.0f) + val output = modelMatrix * input + mesh.addVertex( + position = output.toVec3(), + textureCoordinates = texturePositions[vertex.second]!!, + texture = texture, + tintColor = tintColor, + lightLevel = lightLevel, + ) + } + } + + private fun calculatePositions(heights: FloatArray): List { + val positions = mutableListOf() + positions.addAll(DEFAULT_POSITIONS) + for ((i, defaultPosition) in DEFAULT_POSITIONS.withIndex()) { + val position = Vec3(defaultPosition) + position.y += heights[i] + positions.add(position) + } + return positions + } + + private fun calculateHeights(neighbourBlocks: Array, blockInfo: BlockInfo, world: World, position: BlockPosition): FloatArray { + val height = getLevel(blockInfo) + val heights = floatArrayOf(height, height, height, height) + for (direction in Directions.SIDES) { + val positions = getPositionsForDirection(direction) + handleUpperBlocks(world, position, direction, positions, heights) + handleDirectNeighbours(neighbourBlocks, direction, world, position, positions, heights) + } + return heights + } + + private fun handleDirectNeighbours(neighbourBlocks: Array, direction: Directions, world: World, position: BlockPosition, positions: MutableSet, heights: FloatArray) { + if (isBlockSameFluid(neighbourBlocks[direction.ordinal])) { + val neighbourLevel = getLevel(neighbourBlocks[direction.ordinal]!!) + for (heightPosition in positions) { + heights[heightPosition] = glm.max(heights[heightPosition], neighbourLevel) + } + } + for (altDirection in direction.sidesNextTo(direction)) { + val bothDirections = setOf(direction, altDirection) + if (isBlockSameFluid(world.getBlockInfo(position + direction + altDirection))) { + val neighbourLevel = getLevel(world.getBlockInfo(position + direction + altDirection)!!) + for (heightPosition in HEIGHT_POSITIONS) { + if (heightPosition.key.containsAll(bothDirections)) { + heights[heightPosition.value] = glm.max(heights[heightPosition.value], neighbourLevel) + } + } + } + } + } + + private fun handleUpperBlocks(world: World, position: BlockPosition, direction: Directions, positions: MutableSet, heights: FloatArray) { + if (isBlockSameFluid(world.getBlockInfo(position + Directions.UP + direction))) { + for (heightPosition in positions) { + heights[heightPosition] = 1.0f + } + } + for (altDirection in direction.sidesNextTo(direction)) { + val bothDirections = setOf(direction, altDirection) + if (isBlockSameFluid(world.getBlockInfo(position + Directions.UP + direction + altDirection))) { + for (heightPosition in HEIGHT_POSITIONS) { + if (heightPosition.key.containsAll(bothDirections)) { + heights[heightPosition.value] = 1.0f + } + } + } + } + } + + private fun getPositionsForDirection(direction: Directions): MutableSet { + val positions = mutableSetOf() + for (heightPosition in HEIGHT_POSITIONS) { + if (heightPosition.key.contains(direction)) { + positions.add(heightPosition.value) + } + } + return positions + } + + private fun getLevel(blockInfo: BlockInfo): Float { + for (property in blockInfo.block.properties) { + if (property.group == "level") { + return (8 - property.value!!.toInt) * (1f / 8f) - 0.125f + } + } + return 0.8125f + } + + private fun isBlockSameFluid(blockInfo: BlockInfo?): Boolean { + if (blockInfo == null) { + return false + } + if (blockInfo.block.owner.resourceLocation.full!!.contains(regex)) { + return true + } + if (blockInfo.block.properties.contains(BlockProperties.GENERAL_WATERLOGGED_YES)) { + return true + } + return false + } + + override fun resolveTextures(indexed: MutableList, textureMap: MutableMap) { + if (still != null) { + return + } + still = BlockRenderInterface.resolveTexture(indexed, textureMap, stillTextureName) + flowing = BlockRenderInterface.resolveTexture(indexed, textureMap, flowingTextureName) + } + + companion object { + val DEFAULT_POSITIONS = arrayOf( + ElementRenderer.POSITION_1, + ElementRenderer.POSITION_2, + ElementRenderer.POSITION_3, + ElementRenderer.POSITION_4 + ) + + val HEIGHT_POSITIONS = mapOf( + Pair(setOf(Directions.NORTH, Directions.WEST), 0), + Pair(setOf(Directions.NORTH, Directions.EAST), 1), + Pair(setOf(Directions.SOUTH, Directions.WEST), 2), + Pair(setOf(Directions.SOUTH, Directions.EAST), 3), + ) + + val HEIGHT_POSITIONS_REVERSED = HEIGHT_POSITIONS.entries.associate{(k,v)-> v to k} + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt index b34f0e4de..6e19579df 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt @@ -17,11 +17,15 @@ import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import de.bixilon.minosoft.data.Axes +import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement import glm_.glm import glm_.vec2.Vec2 import glm_.vec3.Vec3 object VecUtil { + val EMPTY_VECTOR = Vec3() + + val BLOCK_SIZE_VECTOR = Vec3(BlockModelElement.BLOCK_RESOLUTION, BlockModelElement.BLOCK_RESOLUTION, BlockModelElement.BLOCK_RESOLUTION) fun jsonToVec3(json: JsonElement): Vec3 { when (json) {