wip: fluid rendering

This commit is contained in:
Bixilon 2021-11-24 09:06:01 +01:00
parent 5fc6b606bb
commit f2247e7fa3
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
18 changed files with 276 additions and 100 deletions

View File

@ -134,7 +134,7 @@ Not compatible (yet)
1. Install Maven and java 11+ (On Ubuntu based distributions: `sudo apt install maven openjdk-11-jdk`). For Windows users, download and install java from oracle or openjdk. Also download maven and follow along
2. Clone this repository (`git clone https://gitlab.bixilon.de/bixilon/minosoft.git`)
3. Change directory (`cd minosoft`)
4. Optional: Checkout the branch of the feature you want to test (`git checkout <branch>`)
4. Optional: Checkout a current feature branch (Warning: might be unstable; might not even build) (`git checkout <branch>`)
5. Build and run Minosoft with `mvn clean verify exec:java`. If any errors occur, feel free to open an issue. In this early stage it might be helpful to delete the config file
6. (Optional) Build a fat jar with `mvn package`. You'll find the jar with all dependencies in `target/`. Then you don't need to recompile everytime

View File

@ -1,24 +0,0 @@
/*
* 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.data.direction
import glm_.vec3.Vec3
import glm_.vec3.Vec3d
import glm_.vec3.Vec3i
interface AbstractDirection {
val vector: Vec3i
val vectorf: Vec3
val vectord: Vec3d
}

View File

@ -27,8 +27,8 @@ import kotlin.math.abs
enum class Directions(
val horizontalId: Int,
override val vector: Vec3i,
) : AbstractDirection {
val vector: Vec3i,
) {
DOWN(-1, Vec3i(0, -1, 0)),
UP(-1, Vec3i(0, 1, 0)),
NORTH(2, Vec3i(0, 0, -1)),
@ -38,8 +38,8 @@ enum class Directions(
val negative = ordinal % 2 == 0
override val vectorf = Vec3(vector)
override val vectord = Vec3d(vector)
val vectorf = Vec3(vector)
val vectord = Vec3d(vector)
val axis: Axes get() = Axes[this] // ToDo
val debugColor = ChatColors[ordinal]

View File

@ -1,33 +0,0 @@
/*
* 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.data.direction
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3d
import glm_.vec3.Vec3
import glm_.vec3.Vec3d
import glm_.vec3.Vec3i
class FakeDirection(override val vector: Vec3i) : AbstractDirection {
override val vectorf: Vec3 = vector.toVec3
override val vectord: Vec3d = vector.toVec3d
companion object {
val NORTH_WEST = FakeDirection(Directions.NORTH + Directions.WEST)
val NORTH_EAST = FakeDirection(Directions.NORTH + Directions.EAST)
val SOUTH_WEST = FakeDirection(Directions.SOUTH + Directions.WEST)
val SOUTH_EAST = FakeDirection(Directions.SOUTH + Directions.EAST)
}
}

View File

@ -16,10 +16,11 @@ package de.bixilon.minosoft.data.registries.blocks.types
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.blocks.BlockFactory
import de.bixilon.minosoft.data.registries.fluid.DefaultFluids
import de.bixilon.minosoft.data.registries.fluid.Fluid
import de.bixilon.minosoft.data.registries.registries.Registries
open class KelpBlock(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>) : Block(resourceLocation, registries, data), FluidFillable {
override val fluid: ResourceLocation = DefaultFluids.WATER
override val fluid: Fluid = registries.fluidRegistry[DefaultFluids.WATER]!!
companion object : BlockFactory<KelpBlock> {

View File

@ -15,8 +15,8 @@ package de.bixilon.minosoft.data.registries.fluid
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.EMPTY
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -28,30 +28,26 @@ abstract class FlowableFluid(
registries: Registries,
data: Map<String, Any>,
) : Fluid(resourceLocation, registries, data) {
open val flowingTexture: ResourceLocation? = null
abstract val flowingTextureName: ResourceLocation
var flowingTexture: AbstractTexture? = null
abstract fun getVelocityMultiplier(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i): Double
open fun getVelocity(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i): Vec3d {
if (blockState.block !is FluidBlock || !blockState.block.fluid.matches(this)) {
if (!this.matches(blockState)) {
return Vec3d.EMPTY
}
val fluidHeight = getHeight(blockState)
val velocity = Vec3d.EMPTY
for (direction in Directions.SIDES) {
val neighbourBlockState = connection.world[blockPosition + direction] ?: continue
if (neighbourBlockState.block !is FluidBlock) {
if (!this.matches(neighbourBlockState)) {
continue
}
val fluid = neighbourBlockState.block.fluid
if (!matches(fluid)) {
continue
}
val height = neighbourBlockState.block.fluid.getHeight(neighbourBlockState)
val height = getHeight(neighbourBlockState)
var heightDifference = 0.0f
@ -64,11 +60,14 @@ abstract class FlowableFluid(
if (heightDifference != 0.0f) {
velocity += (direction.vectord * heightDifference)
}
}
// ToDo: Falling fluid
if (velocity.x == 0.0 && velocity.y == 0.0 && velocity.z == 0.0) {
return velocity
}
return velocity.normalize()
}
}

View File

@ -23,6 +23,8 @@ import de.bixilon.minosoft.data.registries.particle.ParticleType
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.registries.registry.RegistryItem
import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationDeserializer
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.tint.TintProvider
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.nullCast
import de.bixilon.minosoft.util.KUtil.unsafeCast
@ -36,7 +38,9 @@ open class Fluid(
registries: Registries,
data: Map<String, Any>,
) : RegistryItem() {
open val stillTexture: ResourceLocation? = null
open val tintProvider: TintProvider? = null
open val stillTextureName: ResourceLocation? = null
var stillTexture: AbstractTexture? = null
val dripParticle: ParticleType? = null
val bucketItem: Item? = null

View File

@ -42,8 +42,8 @@ class LavaFluid(
data: Map<String, Any>,
) : FlowableFluid(resourceLocation, registries, data) {
private val lavaParticleType: ParticleType? = null
override val stillTexture: ResourceLocation = "minecraft:block/lava_still".toResourceLocation()
override val flowingTexture: ResourceLocation = "minecraft:block/lava_flow".toResourceLocation()
override val stillTextureName: ResourceLocation = "minecraft:block/lava_still".toResourceLocation()
override val flowingTextureName: ResourceLocation = "minecraft:block/lava_flow".toResourceLocation()
init {
this::lavaParticleType.inject(LavaParticle)

View File

@ -28,6 +28,8 @@ import de.bixilon.minosoft.data.registries.fluid.Fluid
import de.bixilon.minosoft.data.registries.fluid.FluidFactory
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple.water.UnderwaterParticle
import de.bixilon.minosoft.gui.rendering.tint.TintProvider
import de.bixilon.minosoft.gui.rendering.tint.WaterTintProvider
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3d
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -46,8 +48,9 @@ class WaterFluid(
) : FlowableFluid(resourceLocation, registries, data) {
private val depthStriderEnchantment: Enchantment? = null
private val dolphinsGraceStatusEffect: StatusEffect? = null
override val stillTexture: ResourceLocation = "minecraft:block/water_still".toResourceLocation()
override val flowingTexture: ResourceLocation = "minecraft:block/water_flow".toResourceLocation()
override val stillTextureName: ResourceLocation = "minecraft:block/water_still".toResourceLocation()
override val flowingTextureName: ResourceLocation = "minecraft:block/water_flow".toResourceLocation()
override val tintProvider: TintProvider = WaterTintProvider
init {

View File

@ -171,6 +171,7 @@ open class Registry<T : RegistryItem>(
this.valueIdMap.putAll(valueIdMap)
}
@Deprecated("Too slow, should be used with a ToDo: RegistryIterator")
fun forEachItem(lambda: (T) -> Unit) {
for (item in resourceLocationMap.values) {
lambda(item)

View File

@ -19,6 +19,7 @@ import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.models.FaceProperties
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.getMesh
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.get
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rgb
import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
@ -41,11 +42,7 @@ class BakedFace(
get() = texture.transparency // ToDo
fun singleRender(position: FloatArray, mesh: WorldMesh, light: Int, ambientLight: FloatArray, tint: Int) {
val meshToUse = when (texture.transparency) {
TextureTransparencies.OPAQUE -> mesh.opaqueMesh
TextureTransparencies.TRANSLUCENT -> mesh.translucentMesh
TextureTransparencies.TRANSPARENT -> mesh.transparentMesh
}!!
val meshToUse = transparency.getMesh(mesh)
// ToDo: Ambient light
val color = Vec3(shade)
if (tint >= 0) {

View File

@ -14,6 +14,9 @@
package de.bixilon.minosoft.gui.rendering.textures
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
import de.bixilon.minosoft.util.KUtil.toResourceLocation
object TextureUtil {
@ -32,4 +35,12 @@ object TextureUtil {
return "$namespace:$path".toResourceLocation()
}
fun TextureTransparencies.getMesh(mesh: WorldMesh): SingleWorldMesh {
return when (this) {
TextureTransparencies.OPAQUE -> mesh.opaqueMesh
TextureTransparencies.TRANSLUCENT -> mesh.translucentMesh
TextureTransparencies.TRANSPARENT -> mesh.transparentMesh
}!!
}
}

View File

@ -20,6 +20,7 @@ import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.registries.blocks.MinecraftBlocks
import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties
import de.bixilon.minosoft.data.registries.blocks.properties.Halves
import de.bixilon.minosoft.data.registries.fluid.Fluid
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.text.RGBColor.Companion.asRGBColor
import de.bixilon.minosoft.data.world.Chunk
@ -43,8 +44,7 @@ class TintManager(private val connection: PlayConnection) {
}
}
fun getAverageTint(chunk: Chunk, neighbours: Array<Chunk>, blockState: BlockState, x: Int, y: Int, z: Int): IntArray? {
val tintProvider = blockState.block.tintProvider ?: return null
private fun getAverageTint(chunk: Chunk, neighbours: Array<Chunk>, blockState: BlockState, tintProvider: TintProvider, x: Int, y: Int, z: Int): IntArray? {
val inChunkX = x and 0x0F
val inChunkZ = z and 0x0F
val biome = chunk.getBiome(inChunkX, y, inChunkZ)
@ -56,6 +56,14 @@ class TintManager(private val connection: PlayConnection) {
return tints
}
fun getAverageTint(chunk: Chunk, neighbours: Array<Chunk>, blockState: BlockState, x: Int, y: Int, z: Int): IntArray? {
return getAverageTint(chunk, neighbours, blockState, blockState.block.tintProvider ?: return null, x, y, z)
}
fun getAverageTint(chunk: Chunk, neighbours: Array<Chunk>, blockState: BlockState, fluid: Fluid, x: Int, y: Int, z: Int): IntArray? {
return getAverageTint(chunk, neighbours, blockState, fluid.tintProvider ?: return null, x, y, z)
}
fun getTint(blockState: BlockState, biome: Biome?, x: Int, y: Int, z: Int): IntArray? {
val tintProvider = blockState.block.tintProvider ?: return null
connection.world.getBiome(x, y, z)

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.gui.rendering.util
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.direction.AbstractDirection
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.AABB
import de.bixilon.minosoft.data.registries.blocks.RandomOffsetTypes
@ -208,7 +207,7 @@ object VecUtil {
return Vec3i((x + vec2.x), y, (z + vec2.y))
}
infix operator fun Vec3i.plus(direction: AbstractDirection?): Vec3i {
infix operator fun Vec3i.plus(direction: Directions?): Vec3i {
return this + direction?.vector
}
@ -220,7 +219,7 @@ object VecUtil {
return Vec2i(x + vec3.x, y + vec3.z)
}
infix operator fun Vec2i.plus(direction: AbstractDirection): Vec2i {
infix operator fun Vec2i.plus(direction: Directions): Vec2i {
return this + direction.vector
}
@ -409,7 +408,7 @@ object VecUtil {
}
}
operator fun AbstractDirection.plus(direction: AbstractDirection): Vec3i {
operator fun Directions.plus(direction: Directions): Vec3i {
return this.vector + direction.vector
}

View File

@ -42,7 +42,7 @@ object Vec3Util {
val Vec3.rgb: Int
get() = ((r * RGBColor.COLOR_FLOAT_DIVIDER).toInt() shl 16) or ((g * RGBColor.COLOR_FLOAT_DIVIDER).toInt() shl 8) or (b * RGBColor.COLOR_FLOAT_DIVIDER).toInt()
fun rotateAssign(x: Float, y: Float, sin: Float, cos: Float, rescale: Boolean): Vec2 {
fun rotate(x: Float, y: Float, sin: Float, cos: Float, rescale: Boolean): Vec2 {
val result = Vec2(x * cos - y * sin, x * sin + y * cos)
if (rescale) {
return result / cos
@ -56,9 +56,9 @@ object Vec3Util {
return
}
when (axis) {
Axes.X -> this.yz = rotateAssign(this.y, this.z, angle.sin, angle.cos, rescale)
Axes.Y -> this.xz = rotateAssign(this.x, this.z, angle.sin, angle.cos, rescale)
Axes.Z -> this.xy = rotateAssign(this.x, this.y, angle.sin, angle.cos, rescale)
Axes.X -> this.yz = rotate(this.y, this.z, angle.sin, angle.cos, rescale)
Axes.Y -> this.xz = rotate(this.x, this.z, angle.sin, angle.cos, rescale)
Axes.Z -> this.xy = rotate(this.x, this.y, angle.sin, angle.cos, rescale)
}
}

View File

@ -20,6 +20,7 @@ import de.bixilon.minosoft.data.assets.AssetsUtil
import de.bixilon.minosoft.data.assets.Resources
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.fluid.FlowableFluid
import de.bixilon.minosoft.data.world.Chunk
import de.bixilon.minosoft.data.world.ChunkSection
import de.bixilon.minosoft.data.world.World
@ -35,6 +36,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.RenderingCapabilities
import de.bixilon.minosoft.gui.rendering.system.base.phases.OpaqueDrawable
import de.bixilon.minosoft.gui.rendering.system.base.phases.TranslucentDrawable
import de.bixilon.minosoft.gui.rendering.system.base.phases.TransparentDrawable
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition
import de.bixilon.minosoft.gui.rendering.util.VecUtil.empty
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkSectionPosition
@ -131,6 +133,13 @@ class WorldRenderer(
val zip = ZipInputStream(GZIPInputStream(FileInputStream(AssetsUtil.getAssetDiskPath(asset.clientJarHash!!, true))))
val modelLoader = ModelLoader(zip, renderWindow)
modelLoader.load()
connection.registries.fluidRegistry.forEachItem {
if (it is FlowableFluid) {
it.flowingTexture = renderWindow.textureManager.staticTextures.createTexture(it.flowingTextureName.texture())
}
it.stillTexture = it.stillTextureName?.let { texture -> renderWindow.textureManager.staticTextures.createTexture(texture.texture()) }
}
}
override fun postInit() {

View File

@ -1,31 +1,45 @@
package de.bixilon.minosoft.gui.rendering.world.preparer.cull
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties
import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock
import de.bixilon.minosoft.data.registries.blocks.types.FluidFillable
import de.bixilon.minosoft.data.registries.fluid.DefaultFluids
import de.bixilon.minosoft.data.registries.fluid.FlowableFluid
import de.bixilon.minosoft.data.registries.fluid.Fluid
import de.bixilon.minosoft.data.world.Chunk
import de.bixilon.minosoft.data.world.ChunkSection
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.getMesh
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotate
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.toVec3
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
import de.bixilon.minosoft.gui.rendering.world.preparer.FluidSectionPreparer
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.chunk.ChunkUtil.acquire
import de.bixilon.minosoft.util.chunk.ChunkUtil.release
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import glm_.func.cos
import glm_.func.sin
import glm_.vec2.Vec2
import glm_.vec2.Vec2i
import glm_.vec3.Vec3
import glm_.vec3.Vec3i
import kotlin.math.atan2
class FluidCullSectionPreparer(
val renderWindow: RenderWindow,
) : FluidSectionPreparer {
private val world: World = renderWindow.connection.world
private val water = renderWindow.connection.registries.fluidRegistry[DefaultFluids.WATER]
private val tintColorCalculator = renderWindow.tintManager
private val tintManager = renderWindow.tintManager
// ToDo: Should this be combined with the solid renderer (but we'd need to render faces twice, because of cullface)
override fun prepareFluid(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbours: Array<ChunkSection?>, neighbourChunks: Array<Chunk>): WorldMesh? {
val mesh = WorldMesh(renderWindow, chunkPosition, sectionHeight, smallMesh = true)
@ -56,7 +70,147 @@ class FluidCullSectionPreparer(
block is FluidFillable -> block.fluid
else -> continue
}
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Can not render fluid: $fluid" }
position = Vec3i(offsetX + x, offsetY + y, offsetZ + z)
tints = tintManager.getAverageTint(chunk, neighbourChunks, blockState, fluid, position.x, position.y, position.z)
val skipTop = fluid.matches(chunk.get(x, offsetY + y + 1, z))
val skipBottom = !shouldRenderSide(position, Directions.DOWN)
val skipNorth = !shouldRenderSide(position, Directions.NORTH)
val skipSouth = !shouldRenderSide(position, Directions.SOUTH)
val skipWest = !shouldRenderSide(position, Directions.WEST)
val skipEast = !shouldRenderSide(position, Directions.EAST)
if (skipTop && skipBottom && skipNorth && skipSouth && skipWest && skipEast) {
continue
}
val cornerHeights = floatArrayOf(
getCornerHeight(position, fluid),
getCornerHeight(position + Directions.EAST, fluid),
getCornerHeight(position + Directions.EAST + Directions.SOUTH, fluid),
getCornerHeight(position + Directions.SOUTH, fluid),
)
val floatPosition = position.toVec3()
floatPosition.y += cornerHeights[0]
if (!skipTop) {
val velocity = if (fluid is FlowableFluid) fluid.getVelocity(renderWindow.connection, blockState, position) else null
val still = velocity == null || velocity.x == 0.0 && velocity.z == 0.0
val texture: AbstractTexture
val minUV = Vec2.EMPTY
val maxUV = Vec2(if (still) 1.0f else 0.5f) // Minecraft just uses half of the sprite
val texturePositions = arrayOf(
Vec2(maxUV.x, minUV.y),
minUV,
Vec2(minUV.x, maxUV.y),
maxUV,
)
if (still) {
texture = fluid.stillTexture!!
} else {
texture = (fluid as FlowableFluid).flowingTexture!!
maxUV.x = 0.5f
val atan = atan2(velocity!!.x, velocity.z).toFloat()
val sin = atan.sin
val cos = atan.cos
for (i in 0 until 4) {
texturePositions[i] = (rotate(texturePositions[i].x - TEXTURE_CENTER, texturePositions[i].y - TEXTURE_CENTER, sin, cos, false) + TEXTURE_CENTER)
}
}
val meshToUse = texture.transparency.getMesh(mesh)
val positions = arrayOf(
Vec3(position.x, position.y + cornerHeights[0], position.z),
Vec3(position.x + 1, position.y + cornerHeights[1], position.z),
Vec3(position.x + 1, position.y + cornerHeights[2], position.z + 1),
Vec3(position.x, position.y + cornerHeights[3], position.z + 1),
)
for ((positionIndex, textureIndex) in meshToUse.order) {
meshToUse.addVertex(positions[positionIndex].array, texturePositions[textureIndex], texture, tints?.get(0) ?: 0xFFFFFF, chunk.getLight(position))
}
rendered = true
}
// ToDo: Sides: Minecraft uses (for water) a overlay texture (with cullface) that is used, when the face fits to a non opaque block
// ToDo: Sides that are connecting with non full cubes (e.g. air) also have cullface disabled
for (direction in 0 until 4) {
var faceX = 0.0f
var faceXEnd = 0.0f
var faceZ = 0.0f
var faceZEnd = 0.0f
var v1 = 0.0f
var v2 = 0.0f
when (direction) {
0 -> {
if (skipNorth) {
continue
}
faceXEnd = 1.0f
v1 = cornerHeights[0]
v2 = cornerHeights[1]
}
1 -> {
if (skipSouth) {
continue
}
faceX = 1.0f
faceZ = 1.0f
faceZEnd = 1.0f
v1 = cornerHeights[2]
v2 = cornerHeights[3]
}
2 -> {
if (skipWest) {
continue
}
faceZ = 1.0f
v1 = cornerHeights[3]
v2 = cornerHeights[0]
}
3 -> {
if (skipEast) {
continue
}
faceX = 1.0f
faceXEnd = 1.0f
faceZEnd = 1.0f
v1 = cornerHeights[1]
v2 = cornerHeights[2]
}
}
val positions = arrayOf(
Vec3(position.x + faceX, position.y + v1, position.z + faceZ),
Vec3(position.x + faceX, position.y, position.z + faceZ),
Vec3(position.x + faceXEnd, position.y, position.z + faceZEnd),
Vec3(position.x + faceXEnd, position.y + v2, position.z + faceZEnd),
)
val texturePositions = arrayOf(
Vec2(0.5f, v2 / 2),
Vec2(0.5f, 0.0f),
Vec2(0.0f, 0.0f),
Vec2(0.0f, v1 / 2),
)
val texture = (fluid as FlowableFluid).flowingTexture!!
val meshToUse = texture.transparency.getMesh(mesh)
for ((positionIndex, textureIndex) in meshToUse.order) {
meshToUse.addVertex(positions[positionIndex].array, texturePositions[textureIndex], texture, tints?.get(0) ?: 0xFFFFFF, 0xFF)
}
rendered = true
}
if (rendered) {
@ -75,4 +229,51 @@ class FluidCullSectionPreparer(
return mesh
}
private fun isSideCovered(position: Vec3i, direction: Directions, height: Float): Boolean {
return world[position + direction] != null
}
private fun shouldRenderSide(position: Vec3i, direction: Directions, height: Float = 1.0f): Boolean {
return !isSideCovered(position, direction, height) /* && fluid.matches(other) */
}
private fun getCornerHeight(position: Vec3i, fluid: Fluid): Float {
// ToDo: Optimize
var totalHeight = 0.0f
var count = 0
for (side in 0 until 4) {
val blockPosition = position + Vec3i(-(side and 0x01), 0, -(side shr 1 and 0x01))
if (fluid.matches(world[blockPosition + Directions.UP])) {
return 1.0f
}
val blockState = world[blockPosition]
if (blockState == null || !fluid.matches(blockState)) {
if (blockState?.material?.solid != true) {
count++
}
continue
}
val height = fluid.getHeight(blockState)
if (height >= 0.8f) {
totalHeight += height * 10.0f
count += 10
} else {
totalHeight += height
count++
}
}
return totalHeight / count
}
private companion object {
private const val TEXTURE_CENTER = 1.0f / 2.0f
}
}

View File

@ -25,7 +25,7 @@ object SystemInformation {
val SYSTEM_MEMORY = HARDWARE_SYSTEM_INFO.memory.total
val OS_TEXT: String = "${System.getProperty("os.name")}: ${SYSTEM_INFO.operatingSystem.family} ${SYSTEM_INFO.operatingSystem.bitness}bit"
val PROCESSOR_TEXT = " ${RUNTIME.availableProcessors()}x ${HARDWARE_SYSTEM_INFO.processor.processorIdentifier.name.replace("\\s{2,}".toRegex(), "")}"
val PROCESSOR_TEXT = "${RUNTIME.availableProcessors()}x ${HARDWARE_SYSTEM_INFO.processor.processorIdentifier.name.replace("\\s{2,}".toRegex(), "")}"
val MAX_MEMORY_TEXT: String = getFormattedMaxMemory()
val PROCESSOR_SPEED = HARDWARE_SYSTEM_INFO.processor.maxFreq