Liquid render!
This commit is contained in:
parent
598530c4ec
commit
96c88aa725
@ -123,7 +123,26 @@ fun main() {
|
||||
|
||||
chunk.background[x, y]?.setModifierHueShift(modifierHueShift2)
|
||||
|
||||
reader.skipBytes(17)
|
||||
val liquid = reader.readUnsignedByte()
|
||||
val liquidLevel = reader.readFloat()
|
||||
val liquidPressure = reader.readFloat()
|
||||
val liquidIsInfinite = reader.readBoolean()
|
||||
val collisionMap = reader.readUnsignedByte()
|
||||
val dungeonId = reader.readUnsignedShort()
|
||||
val biome = reader.readUnsignedByte()
|
||||
val envBiome = reader.readUnsignedByte()
|
||||
val indestructible = reader.readBoolean()
|
||||
val unknown = reader.readUnsignedByte()
|
||||
|
||||
val getLiquid = Starbound.liquidByIDAccess[liquid]
|
||||
|
||||
if (getLiquid != null) {
|
||||
val state = chunk.setLiquid(x, y, getLiquid)!!
|
||||
|
||||
state.isInfinite = liquidIsInfinite
|
||||
state.pressure = liquidPressure
|
||||
state.level = liquidLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,8 +163,8 @@ fun main() {
|
||||
|
||||
//ent.position += Vector2d(y = 14.0, x = -10.0)
|
||||
ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0)
|
||||
client.camera.pos.x = 616f
|
||||
client.camera.pos.y = 721f
|
||||
client.camera.pos.x = 578f
|
||||
client.camera.pos.y = 695f
|
||||
|
||||
client.onDrawGUI {
|
||||
client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f)
|
||||
|
@ -1,15 +1,18 @@
|
||||
package ru.dbotthepony.kstarbound.client
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
||||
import ru.dbotthepony.kstarbound.client.render.EntityRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
||||
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.world.*
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import java.io.Closeable
|
||||
import java.util.IdentityHashMap
|
||||
import java.util.LinkedList
|
||||
|
||||
/**
|
||||
@ -19,6 +22,7 @@ import java.util.LinkedList
|
||||
* первыми (на самом дальнем плане)
|
||||
*/
|
||||
const val Z_LEVEL_BACKGROUND = 60000
|
||||
const val Z_LEVEL_LIQUID = 10000
|
||||
|
||||
class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, ClientChunk>(world, pos), Closeable {
|
||||
val state: GLStateTracker get() = world.client.gl
|
||||
@ -223,7 +227,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
* Должен быть использован только один раз, после выкинут, иначе поведение
|
||||
* кода невозможно будет предсказать
|
||||
*/
|
||||
inner class BakedLayeredRenderer constructor(val origin: ChunkPos = pos) : ILayeredRenderer {
|
||||
inner class OneShotRenderer constructor(val origin: ChunkPos = pos) : ILayeredRenderer {
|
||||
private val layerQueue = ArrayDeque<Pair<(Matrix4fStack) -> Unit, Int>>()
|
||||
|
||||
init {
|
||||
@ -241,10 +245,50 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
it.push().translateWithMultiplication(relative.x.toFloat(), relative.y.toFloat())
|
||||
renderer.render(it)
|
||||
it.pop()
|
||||
return@lambda
|
||||
Unit
|
||||
} to renderer.layer)
|
||||
}
|
||||
|
||||
layerQueue.add({ it: Matrix4fStack ->
|
||||
val types = ArrayList<LiquidDefinition>()
|
||||
|
||||
for (x in 0 until CHUNK_SIZE) {
|
||||
for (y in 0 until CHUNK_SIZE) {
|
||||
val state = getLiquid(x, y)
|
||||
|
||||
if (state != null && !types.any { it === state.def }) {
|
||||
types.add(state.def)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val program = state.programs.liquid
|
||||
|
||||
program.use()
|
||||
program.transform.set(it.last)
|
||||
|
||||
val builder = program.builder
|
||||
|
||||
for (type in types) {
|
||||
builder.begin()
|
||||
|
||||
for (x in 0 until CHUNK_SIZE) {
|
||||
for (y in 0 until CHUNK_SIZE) {
|
||||
val state = getLiquid(x, y)
|
||||
|
||||
if (state != null && state.def === type) {
|
||||
builder.quad(x.toFloat(), y.toFloat(), x + 1f, y + state.level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program.baselineColor.set(type.color)
|
||||
|
||||
builder.upload()
|
||||
builder.draw()
|
||||
}
|
||||
} to Z_LEVEL_LIQUID)
|
||||
|
||||
layerQueue.sortBy {
|
||||
return@sortBy it.second
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class ClientWorld(
|
||||
val determineRenderers = ArrayList<ILayeredRenderer>()
|
||||
|
||||
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
|
||||
determineRenderers.add(chunk.second.BakedLayeredRenderer(chunk.first))
|
||||
determineRenderers.add(chunk.second.OneShotRenderer(chunk.first))
|
||||
chunk.second.bake()
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,9 @@ import org.lwjgl.opengl.GL
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.client.freetype.FreeType
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLPrograms
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableColorableProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
||||
@ -76,6 +77,8 @@ interface GLStreamBuilderList {
|
||||
|
||||
class GLStateTracker {
|
||||
init {
|
||||
check(TRACKERS.get() == null) { "Already has state tracker existing at this thread!" }
|
||||
TRACKERS.set(this)
|
||||
// This line is critical for LWJGL's interoperation with GLFW's
|
||||
// OpenGL context, or any context that is managed externally.
|
||||
// LWJGL detects the context that is current in the current thread,
|
||||
@ -393,13 +396,17 @@ class GLStateTracker {
|
||||
}
|
||||
}
|
||||
|
||||
val quadWireframe by lazy {
|
||||
StreamVertexBuilder(this, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 16384)
|
||||
}
|
||||
|
||||
val matrixStack = Matrix4fStack()
|
||||
val freeType = FreeType()
|
||||
|
||||
val font = Font(this)
|
||||
|
||||
inline fun quadWireframe(color: Color = Color.WHITE, lambda: (StreamVertexBuilder) -> Unit) {
|
||||
val builder = flat2DQuadWireframe.small
|
||||
val builder = quadWireframe
|
||||
|
||||
builder.begin()
|
||||
lambda.invoke(builder)
|
||||
@ -423,5 +430,6 @@ class GLStateTracker {
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger(GLStateTracker::class.java)
|
||||
private val TRACKERS = ThreadLocal<GLStateTracker>()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package ru.dbotthepony.kstarbound.client.gl.program
|
||||
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
|
||||
|
||||
private fun loadShaders(
|
||||
vertex: Collection<String>,
|
||||
fragment: Collection<String>
|
||||
): Array<GLShader> {
|
||||
val result = arrayOfNulls<GLShader>(vertex.size + fragment.size)
|
||||
var i = 0
|
||||
|
||||
for (name in vertex) {
|
||||
result[i++] = GLShader.internalVertex("shaders/$name.vsh")
|
||||
}
|
||||
|
||||
for (name in fragment) {
|
||||
result[i++] = GLShader.internalFragment("shaders/$name.fsh")
|
||||
}
|
||||
|
||||
return result as Array<GLShader>
|
||||
}
|
||||
|
||||
open class GLInternalProgram(
|
||||
state: GLStateTracker,
|
||||
vertex: Collection<String>,
|
||||
fragment: Collection<String>
|
||||
) : GLShaderProgram(state, *loadShaders(vertex, fragment)) {
|
||||
constructor(state: GLStateTracker, name: String) : this(state, listOf(name), listOf(name))
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||
package ru.dbotthepony.kstarbound.client.gl.program
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLUniformLocation
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.ShaderLinkException
|
||||
import ru.dbotthepony.kvector.api.IFloatMatrix
|
||||
import ru.dbotthepony.kvector.api.IStruct3f
|
||||
import ru.dbotthepony.kvector.api.IStruct4f
|
||||
@ -12,6 +14,10 @@ import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
open class GLShaderProgram(val state: GLStateTracker, vararg shaders: GLShader) {
|
||||
init {
|
||||
state.ensureSameThread()
|
||||
}
|
||||
|
||||
val pointer = glCreateProgram()
|
||||
var linked = false
|
||||
private set
|
@ -1,8 +1,13 @@
|
||||
package ru.dbotthepony.kstarbound.client.gl
|
||||
package ru.dbotthepony.kstarbound.client.gl.program
|
||||
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableColorableProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexType
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@ -10,6 +15,8 @@ private class SimpleProgram<T : GLShaderProgram>(private val name: String, priva
|
||||
private var value: T? = null
|
||||
|
||||
override fun getValue(thisRef: GLPrograms, property: KProperty<*>): T {
|
||||
thisRef.state.ensureSameThread()
|
||||
|
||||
val value = value
|
||||
|
||||
if (value != null) {
|
||||
@ -34,8 +41,26 @@ private class SimpleProgram<T : GLShaderProgram>(private val name: String, priva
|
||||
}
|
||||
}
|
||||
|
||||
class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid") {
|
||||
init {
|
||||
link()
|
||||
}
|
||||
|
||||
val baselineColor = this["baselineColor"]!!
|
||||
val transform = this["transform"]!!
|
||||
|
||||
val builder by lazy {
|
||||
StreamVertexBuilder(state, FORMAT, VertexType.QUADS, 16384)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
|
||||
}
|
||||
}
|
||||
|
||||
class GLPrograms(val state: GLStateTracker) {
|
||||
val tile by SimpleProgram("tile", ::GLTransformableColorableProgram)
|
||||
val font by SimpleProgram("font", ::GLTransformableColorableProgram)
|
||||
val flat by SimpleProgram("flat", ::GLTransformableColorableProgram)
|
||||
val liquid by lazy { GLLiquidProgram(state) }
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram
|
||||
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
|
||||
|
||||
open class GLTransformableProgram(state: GLStateTracker, vararg shaders: GLShader) : GLShaderProgram(state, *shaders) {
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.client.gl.shader
|
||||
|
||||
import org.lwjgl.opengl.GL41.*
|
||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram
|
||||
import ru.dbotthepony.kvector.api.IFloatMatrix
|
||||
import ru.dbotthepony.kvector.api.IStruct3f
|
||||
import ru.dbotthepony.kvector.api.IStruct4f
|
||||
@ -14,6 +15,7 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin
|
||||
program.state.ensureSameThread()
|
||||
val (v0, v1, v2, v3) = value
|
||||
glProgramUniform4f(program.pointer, pointer, v0, v1, v2, v3)
|
||||
checkForGLError()
|
||||
return this
|
||||
}
|
||||
|
||||
@ -21,12 +23,14 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin
|
||||
program.state.ensureSameThread()
|
||||
val (v0, v1, v2) = value
|
||||
glProgramUniform3f(program.pointer, pointer, v0, v1, v2)
|
||||
checkForGLError()
|
||||
return this
|
||||
}
|
||||
|
||||
fun set(value: Int): GLUniformLocation {
|
||||
program.state.ensureSameThread()
|
||||
glProgramUniform1i(program.pointer, pointer, value)
|
||||
checkForGLError()
|
||||
return this
|
||||
}
|
||||
|
||||
@ -56,4 +60,4 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,9 +87,8 @@ class GLAttributeList(builder: Builder) {
|
||||
|
||||
val VERTEX_TEXTURE = Builder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
||||
|
||||
val VERTEX_HSV_TEXTURE = Builder()
|
||||
.push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
||||
|
||||
val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
||||
|
||||
val TILE = Builder().push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.AbstractVertexBuilder
|
||||
|
@ -168,7 +168,7 @@ private enum class TileRenderTesselateResult {
|
||||
HALT
|
||||
}
|
||||
|
||||
private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.VERTEX_HSV_TEXTURE, VertexType.QUADS)
|
||||
private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.TILE, VertexType.QUADS)
|
||||
|
||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||
override fun test(thisTile: TileState?, otherTile: TileState?): Boolean {
|
||||
|
@ -0,0 +1,45 @@
|
||||
package ru.dbotthepony.kstarbound.util
|
||||
|
||||
import java.util.Spliterator
|
||||
import java.util.function.Consumer
|
||||
|
||||
class ArraySpliterator<T>(private val source: Array<T>, private val offset: Int = 0, private val size: Int = source.size) : Spliterator<T> {
|
||||
init {
|
||||
require(offset + size <= source.size) { "Invalid dimensions for spliterator: offset = $offset, size = $size, source has size of ${source.size}" }
|
||||
}
|
||||
|
||||
private var index = 0
|
||||
|
||||
override fun tryAdvance(action: Consumer<in T>): Boolean {
|
||||
if (index < size) {
|
||||
action.accept(source[offset + index++])
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun trySplit(): Spliterator<T>? {
|
||||
if (index + 1 >= size) {
|
||||
return null
|
||||
}
|
||||
|
||||
val splitSize = size / 2
|
||||
|
||||
if (splitSize == 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
val pointer = index + offset
|
||||
index += splitSize
|
||||
return ArraySpliterator(source, pointer, splitSize)
|
||||
}
|
||||
|
||||
override fun estimateSize(): Long {
|
||||
return (size - index).toLong()
|
||||
}
|
||||
|
||||
override fun characteristics(): Int {
|
||||
return Spliterator.ORDERED or Spliterator.SIZED
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package ru.dbotthepony.kstarbound.util
|
||||
|
||||
import java.util.Spliterator
|
||||
import java.util.function.Consumer
|
||||
|
||||
class IndexedArraySpliterator<T>(private val source: Array<T>, private val offset: Int = 0, private val size: Int = source.size) : Spliterator<IndexedArraySpliterator<T>.Entry> {
|
||||
inner class Entry(val index: Int, val value: T)
|
||||
|
||||
init {
|
||||
require(offset + size <= source.size) { "Invalid dimensions for spliterator: offset = $offset, size = $size, source has size of ${source.size}" }
|
||||
}
|
||||
|
||||
private var index = 0
|
||||
|
||||
override fun tryAdvance(action: Consumer<in IndexedArraySpliterator<T>.Entry>): Boolean {
|
||||
if (index < size) {
|
||||
val pointer = offset + index++
|
||||
action.accept(Entry(pointer, source[pointer]))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun trySplit(): Spliterator<IndexedArraySpliterator<T>.Entry>? {
|
||||
if (index + 1 >= size) {
|
||||
return null
|
||||
}
|
||||
|
||||
val splitSize = size / 2
|
||||
|
||||
if (splitSize == 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
val pointer = index + offset
|
||||
index += splitSize
|
||||
return IndexedArraySpliterator(source, pointer, splitSize)
|
||||
}
|
||||
|
||||
override fun estimateSize(): Long {
|
||||
return (size - index).toLong()
|
||||
}
|
||||
|
||||
override fun characteristics(): Int {
|
||||
return Spliterator.ORDERED or Spliterator.SIZED
|
||||
}
|
||||
}
|
@ -1,8 +1,16 @@
|
||||
package ru.dbotthepony.kstarbound.util
|
||||
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, private val height: Int) {
|
||||
data class Entry<out T>(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val value: T,
|
||||
)
|
||||
|
||||
private val memory: Array<T?> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T?>
|
||||
|
||||
operator fun get(x: Int, y: Int): T? {
|
||||
@ -17,7 +25,7 @@ class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, pri
|
||||
return memory[x + y * width]
|
||||
}
|
||||
|
||||
operator fun set(x: Int, y: Int, value: T): T? {
|
||||
operator fun set(x: Int, y: Int, value: T?): T? {
|
||||
if (x !in 0 until width) {
|
||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
||||
}
|
||||
@ -30,6 +38,18 @@ class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, pri
|
||||
memory[x + y * width] = value
|
||||
return old
|
||||
}
|
||||
|
||||
fun stream(): Stream<out T?> {
|
||||
return StreamSupport.stream(ArraySpliterator(memory), false)
|
||||
}
|
||||
|
||||
fun indexedStream(): Stream<out Entry<T?>> {
|
||||
return StreamSupport.stream(IndexedArraySpliterator(memory), false).map {
|
||||
val x = it.index % width
|
||||
val y = (it.index - x) / width
|
||||
Entry(x, y, it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> TwoDimensionalArray(width: Int, height: Int) = TwoDimensionalArray(T::class, width, height)
|
||||
|
@ -122,7 +122,7 @@ class TileState(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
|
||||
}
|
||||
}
|
||||
|
||||
data class LiquidState(val chunk: Chunk<*, *>.TileLayer, val def: LiquidDefinition) {
|
||||
data class LiquidState(val chunk: Chunk<*, *>, val def: LiquidDefinition) {
|
||||
var pressure: Float = 0f
|
||||
var level: Float = 1f
|
||||
var isInfinite: Boolean = false
|
||||
@ -414,6 +414,18 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
|
||||
protected val liquidStates: TwoDimensionalArray<LiquidState> = TwoDimensionalArray(CHUNK_SIZE, CHUNK_SIZE)
|
||||
|
||||
fun getLiquid(x: Int, y: Int) = liquidStates[x, y]
|
||||
|
||||
fun setLiquid(x: Int, y: Int, value: LiquidDefinition?): LiquidState? {
|
||||
if (value == null) {
|
||||
return liquidStates.set(x, y, null)
|
||||
}
|
||||
|
||||
val state = LiquidState(this, value)
|
||||
liquidStates[x, y] = state
|
||||
return state
|
||||
}
|
||||
|
||||
protected val entities = HashSet<Entity>()
|
||||
val entitiesAccess: Set<Entity> = Collections.unmodifiableSet(entities)
|
||||
|
||||
|
10
src/main/resources/shaders/liquid.fsh
Normal file
10
src/main/resources/shaders/liquid.fsh
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
#version 460
|
||||
|
||||
out vec4 resultColor;
|
||||
|
||||
uniform vec4 baselineColor;
|
||||
|
||||
void main() {
|
||||
resultColor = vec4(baselineColor);
|
||||
}
|
10
src/main/resources/shaders/liquid.vsh
Normal file
10
src/main/resources/shaders/liquid.vsh
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec2 vertexPos;
|
||||
|
||||
uniform mat4 transform;
|
||||
|
||||
void main() {
|
||||
gl_Position = transform * vec4(vertexPos, 5.0, 1.0);
|
||||
}
|
38
src/test/kotlin/ru/dbotthepony/kstarbound/test/MiscTests.kt
Normal file
38
src/test/kotlin/ru/dbotthepony/kstarbound/test/MiscTests.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package ru.dbotthepony.kstarbound.test
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import ru.dbotthepony.kstarbound.util.ArraySpliterator
|
||||
import java.rmi.UnexpectedException
|
||||
|
||||
object MiscTests {
|
||||
@Test
|
||||
@DisplayName("Array Spliterator test")
|
||||
fun arraySpliterator() {
|
||||
val base = arrayOf(1, 2, 3, 4)
|
||||
val spliterator = ArraySpliterator(base)
|
||||
|
||||
assertTrue(spliterator.tryAdvance { assertEquals(1, it) })
|
||||
assertTrue(spliterator.tryAdvance { assertEquals(2, it) })
|
||||
assertTrue(spliterator.tryAdvance { assertEquals(3, it) })
|
||||
assertTrue(spliterator.tryAdvance { assertEquals(4, it) })
|
||||
assertFalse(spliterator.tryAdvance { throw UnexpectedException("Unreachable code") })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Array Spliterator split test")
|
||||
fun arraySpliteratorSplit() {
|
||||
val base = arrayOf(1, 2, 3, 4, 5, 6)
|
||||
val spliterator = ArraySpliterator(base)
|
||||
|
||||
var i = 0
|
||||
|
||||
val splitted = spliterator.trySplit()!!
|
||||
|
||||
splitted.forEachRemaining { assertEquals(++i, it) }
|
||||
spliterator.forEachRemaining { assertEquals(++i, it) }
|
||||
|
||||
assertEquals(6, i)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user