more shader stuff, hue shift, color variants, liquid def loader test

This commit is contained in:
DBotThePony 2022-09-10 22:30:34 +07:00
parent 19cf788d87
commit 48cf205506
Signed by: DBot
GPG Key ID: DCC23B5715498507
23 changed files with 356 additions and 82 deletions

View File

@ -0,0 +1,8 @@
package ru.dbotthepony.kstarbound
import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapter
inline fun <reified T> GsonBuilder.registerTypeAdapter(adapter: TypeAdapter<T>): GsonBuilder {
return registerTypeAdapter(T::class.java, adapter)
}

View File

@ -12,6 +12,7 @@ import java.io.ByteArrayInputStream
import java.io.DataInputStream import java.io.DataInputStream
import java.io.File import java.io.File
import java.util.zip.Inflater import java.util.zip.Inflater
import kotlin.math.roundToInt
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
@ -75,12 +76,17 @@ fun main() {
hitTile = true hitTile = true
} }
reader.skipBytes(1) // Foreground hue shift // reader.skipBytes(1) // Foreground hue shift
reader.skipBytes(1) // Foreground color variant // reader.skipBytes(1) // Foreground color variant
val colorShift = reader.readUnsignedByte()
val colorVariant = reader.readUnsignedByte()
val modifier = reader.readUnsignedShort() val modifier = reader.readUnsignedShort()
val getModifier = Starbound.tileModifiersByIDAccess[modifier] val getModifier = Starbound.tileModifiersByIDAccess[modifier]
chunk.foreground[x, y]?.color = colorVariant
chunk.foreground[x, y]?.setHueShift(colorShift)
if (getModifier != null && getMat != null) { if (getModifier != null && getMat != null) {
chunk.foreground[x, y]?.modifier = getModifier chunk.foreground[x, y]?.modifier = getModifier
} }

View File

@ -8,6 +8,7 @@ import ru.dbotthepony.kstarbound.api.PhysicalFS
import ru.dbotthepony.kstarbound.api.getPathFilename import ru.dbotthepony.kstarbound.api.getPathFilename
import ru.dbotthepony.kstarbound.api.getPathFolder import ru.dbotthepony.kstarbound.api.getPathFolder
import ru.dbotthepony.kstarbound.defs.* import ru.dbotthepony.kstarbound.defs.*
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.projectile.* import ru.dbotthepony.kstarbound.defs.projectile.*
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.RenderParameters import ru.dbotthepony.kstarbound.defs.tile.RenderParameters
@ -57,10 +58,15 @@ object Starbound : IVFS {
private val tileModifiers = HashMap<String, MaterialModifier>() private val tileModifiers = HashMap<String, MaterialModifier>()
private val tileModifiersByID = Int2ObjectAVLTreeMap<MaterialModifier>() private val tileModifiersByID = Int2ObjectAVLTreeMap<MaterialModifier>()
private val liquid = HashMap<String, LiquidDefinition>()
private val liquidByID = Int2ObjectAVLTreeMap<LiquidDefinition>()
private val projectiles = HashMap<String, ConfiguredProjectile>() private val projectiles = HashMap<String, ConfiguredProjectile>()
private val parallax = HashMap<String, ParallaxPrototype>() private val parallax = HashMap<String, ParallaxPrototype>()
private val functions = HashMap<String, JsonFunction>() private val functions = HashMap<String, JsonFunction>()
val liquidAccess: Map<String, LiquidDefinition> = Collections.unmodifiableMap(liquid)
val liquidByIDAccess: Map<Int, LiquidDefinition> = Collections.unmodifiableMap(liquidByID)
val tileModifiersAccess: Map<String, MaterialModifier> = Collections.unmodifiableMap(tileModifiers) val tileModifiersAccess: Map<String, MaterialModifier> = Collections.unmodifiableMap(tileModifiers)
val tileModifiersByIDAccess: Map<Int, MaterialModifier> = Collections.unmodifiableMap(tileModifiersByID) val tileModifiersByIDAccess: Map<Int, MaterialModifier> = Collections.unmodifiableMap(tileModifiersByID)
val tilesAccess: Map<String, TileDefinition> = Collections.unmodifiableMap(tiles) val tilesAccess: Map<String, TileDefinition> = Collections.unmodifiableMap(tiles)
@ -94,6 +100,7 @@ object Starbound : IVFS {
.also(RenderParameters::registerGson) .also(RenderParameters::registerGson)
.also(RenderTemplate::registerGson) .also(RenderTemplate::registerGson)
.also(TileDefinition::registerGson) .also(TileDefinition::registerGson)
.also(LiquidDefinition::registerGson)
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe()) .registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
@ -183,6 +190,7 @@ object Starbound : IVFS {
loadStage(callback, this::loadProjectiles, "projectiles") loadStage(callback, this::loadProjectiles, "projectiles")
loadStage(callback, this::loadParallax, "parallax definitions") loadStage(callback, this::loadParallax, "parallax definitions")
loadStage(callback, this::loadMaterialModifiers, "material modifier definitions") loadStage(callback, this::loadMaterialModifiers, "material modifier definitions")
loadStage(callback, this::loadLiquidDefinitions, "liquid definitions")
initializing = false initializing = false
initialized = true initialized = true
@ -350,4 +358,25 @@ object Starbound : IVFS {
readingFolder = null readingFolder = null
} }
private fun loadLiquidDefinitions(callback: (String) -> Unit) {
for (fs in fileSystems) {
for (listedFile in fs.listAllFilesWithExtension("liquid")) {
try {
callback("Loading $listedFile")
readingFolder = getPathFolder(listedFile)
val liquidDef = gson.fromJson(getReader(listedFile), LiquidDefinition::class.java)
check(liquid.put(liquidDef.name, liquidDef) == null) { "Already has liquid with name ${liquidDef.name} loaded!" }
check(liquidByID.put(liquidDef.liquidId, liquidDef) == null) { "Already has liquid with ID ${liquidDef.liquidId} loaded!" }
} catch (err: Throwable) {
//throw TileDefLoadingException("Loading tile file $listedFile", err)
LOGGER.error("Loading liquid definition file $listedFile", err)
}
}
}
readingFolder = null
}
} }

View File

@ -47,12 +47,12 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
for ((pos, tile) in view.posToTile) { for ((pos, tile) in view.posToTile) {
if (tile != null) { if (tile != null) {
state.tileRenderers.getTileRenderer(tile.def.materialName).tesselate(view, layers, pos, background = isBackground) state.tileRenderers.getTileRenderer(tile.def.materialName).tesselate(tile, view, layers, pos, background = isBackground)
val modifier = tile.modifier val modifier = tile.modifier
if (modifier != null) { if (modifier != null) {
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(view, layers, pos, background = isBackground) state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, pos, background = isBackground, isModifier = true)
} }
} }
} }

View File

@ -23,6 +23,9 @@ enum class GLType(val identity: Int, val typeIndentity: Int, val byteSize: Int,
} }
interface IGLAttributeList { interface IGLAttributeList {
/**
* Применяет список атрибутов к заданному [GLVertexArrayObject] (попутно включая или отключая их через [enable])
*/
fun apply(target: GLVertexArrayObject, enable: Boolean = false) fun apply(target: GLVertexArrayObject, enable: Boolean = false)
} }
@ -85,10 +88,15 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
} }
companion object { companion object {
val VEC2F = GLFlatAttributeListBuilder().also {it.push(GLType.VEC2F)}.build() val VEC2F = GLFlatAttributeListBuilder().push(GLType.VEC2F).build()
val VEC3F = GLFlatAttributeListBuilder().also {it.push(GLType.VEC3F)}.build() val VEC3F = GLFlatAttributeListBuilder().push(GLType.VEC3F).build()
val VERTEX_TEXTURE = GLFlatAttributeListBuilder().also {it.push(GLType.VEC3F).push(GLType.VEC2F)}.build()
val VERTEX_2D_TEXTURE = GLFlatAttributeListBuilder().also {it.push(GLType.VEC2F).push(GLType.VEC2F)}.build() val VERTEX_TEXTURE = GLFlatAttributeListBuilder().push(GLType.VEC3F).push(GLType.VEC2F).build()
val VERTEX_HSV_TEXTURE = GLFlatAttributeListBuilder()
.push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
val VERTEX_2D_TEXTURE = GLFlatAttributeListBuilder().push(GLType.VEC2F).push(GLType.VEC2F).build()
} }
} }
@ -109,6 +117,11 @@ class GLFlatAttributeListBuilder : IGLAttributeList {
return push("$type#${attributes.size}", type) return push("$type#${attributes.size}", type)
} }
fun push(vararg types: GLType): GLFlatAttributeListBuilder {
for (type in types) push(type)
return this
}
fun push(name: String, type: GLType): GLFlatAttributeListBuilder { fun push(name: String, type: GLType): GLFlatAttributeListBuilder {
check(!findName(name)) { "Already has named attribute $name!" } check(!findName(name)) { "Already has named attribute $name!" }
attributes.add(name to type) attributes.add(name to type)
@ -117,7 +130,7 @@ class GLFlatAttributeListBuilder : IGLAttributeList {
fun build() = GLFlatAttributeList(this) fun build() = GLFlatAttributeList(this)
@Deprecated("Используй build()") @Deprecated("Используй build()", replaceWith = ReplaceWith("build()"))
override fun apply(target: GLVertexArrayObject, enable: Boolean) { override fun apply(target: GLVertexArrayObject, enable: Boolean) {
var offset = 0L var offset = 0L
var stride = 0 var stride = 0
@ -136,9 +149,4 @@ class GLFlatAttributeListBuilder : IGLAttributeList {
} }
} }
} }
companion object {
val VEC3F = GLFlatAttributeList.VEC3F
val VERTEX_TEXTURE = GLFlatAttributeList.VERTEX_TEXTURE
}
} }

View File

@ -227,7 +227,7 @@ class GLStateTracker {
checkForGLError() checkForGLError()
} }
val thread = Thread.currentThread() val thread: Thread = Thread.currentThread()
val tileRenderers = TileRenderers(this) val tileRenderers = TileRenderers(this)
fun ensureSameThread() { fun ensureSameThread() {
@ -356,18 +356,25 @@ class GLStateTracker {
val shaderVertexTexture: GLTransformableProgram val shaderVertexTexture: GLTransformableProgram
val shaderVertexTextureColor: GLTransformableColorableProgram val shaderVertexTextureColor: GLTransformableColorableProgram
val shaderVertexTextureHSVColor: GLTransformableColorableProgram
init { init {
val textureF = GLShader.internalFragment("shaders/fragment/texture.glsl") val textureF = GLShader.internalFragment("shaders/fragment/texture.glsl")
val textureColorF = GLShader.internalFragment("shaders/fragment/texture_color.glsl") val textureColorF = GLShader.internalFragment("shaders/fragment/texture_color.glsl")
val textureV = GLShader.internalVertex("shaders/vertex/texture.glsl") val textureV = GLShader.internalVertex("shaders/vertex/texture.glsl")
val textureFragmentHSV = GLShader.internalFragment("shaders/fragment/texture_color_per_vertex.glsl")
val textureVertexHSV = GLShader.internalVertex("shaders/vertex/texture_hsv.glsl")
shaderVertexTexture = GLTransformableProgram(this, textureF, textureV) shaderVertexTexture = GLTransformableProgram(this, textureF, textureV)
shaderVertexTextureColor = GLTransformableColorableProgram(this, textureColorF, textureV) shaderVertexTextureColor = GLTransformableColorableProgram(this, textureColorF, textureV)
shaderVertexTextureHSVColor = GLTransformableColorableProgram(this, textureFragmentHSV, textureVertexHSV)
textureF.unlink() textureF.unlink()
textureColorF.unlink() textureColorF.unlink()
textureV.unlink() textureV.unlink()
textureFragmentHSV.unlink()
textureVertexHSV.unlink()
} }
val fontProgram: GLTransformableColorableProgram val fontProgram: GLTransformableColorableProgram

View File

@ -125,6 +125,7 @@ interface IVertex<This : IVertex<This, VertexBuilderType>, VertexBuilderType> {
fun expect(type: GLType): This fun expect(type: GLType): This
fun pushVec3f(x: Float, y: Float, z: Float): This fun pushVec3f(x: Float, y: Float, z: Float): This
fun pushVec2f(x: Float, y: Float): This fun pushVec2f(x: Float, y: Float): This
fun push(value: Float): This
fun end(): VertexBuilderType fun end(): VertexBuilderType
} }
@ -132,6 +133,20 @@ typealias VertexTransformer = (IVertex<*, *>, Int) -> IVertex<*, *>
private val emptyTransform: VertexTransformer = { it, _ -> it } private val emptyTransform: VertexTransformer = { it, _ -> it }
private val EMPTY_BUFFER = ByteBuffer.allocateDirect(0) private val EMPTY_BUFFER = ByteBuffer.allocateDirect(0)
fun VertexTransformer.before(other: VertexTransformer): VertexTransformer {
return { a, b ->
other.invoke(a, b)
this.invoke(a, b)
}
}
fun VertexTransformer.after(other: VertexTransformer): VertexTransformer {
return { a, b ->
this.invoke(a, b)
other.invoke(a, b)
}
}
object VertexTransformers { object VertexTransformers {
fun uv(u0: Float, fun uv(u0: Float,
v0: Float, v0: Float,
@ -279,6 +294,7 @@ class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val typ
is IntArray -> for (i in element) bytes.putInt(i) is IntArray -> for (i in element) bytes.putInt(i)
is ByteArray -> for (i in element) bytes.put(i) is ByteArray -> for (i in element) bytes.put(i)
is DoubleArray -> for (i in element) bytes.putDouble(i) is DoubleArray -> for (i in element) bytes.putDouble(i)
is Float -> bytes.putFloat(element)
else -> throw IllegalStateException("Unknown element $element") else -> throw IllegalStateException("Unknown element $element")
} }
} }
@ -291,6 +307,7 @@ class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val typ
is IntArray -> it.joinToString(", ") is IntArray -> it.joinToString(", ")
is ByteArray -> it.joinToString(", ") is ByteArray -> it.joinToString(", ")
is DoubleArray -> it.joinToString(", ") is DoubleArray -> it.joinToString(", ")
is Float -> it
else -> "null" else -> "null"
} }.joinToString("; ")})" } }.joinToString("; ")})"
} }
@ -331,6 +348,12 @@ class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val typ
return this return this
} }
override fun push(value: Float): Vertex {
expect(GLType.FLOAT)
store[index++] = value
return this
}
override fun checkValid() { override fun checkValid() {
for (elem in store.indices) { for (elem in store.indices) {
if (store[elem] == null) { if (store[elem] == null) {
@ -521,6 +544,15 @@ class StreamVertexBuilder(
return this return this
} }
override fun push(value: Float): Vertex {
expect(GLType.FLOAT)
vertexBuffer.position(bufferPosition)
vertexBuffer.putFloat(value)
index++
bufferPosition += 4
return this
}
override fun end(): StreamVertexBuilder { override fun end(): StreamVertexBuilder {
check(index == attributes.size) { "Vertex $vertexIndex is not fully filled (only $index attributes provided, ${attributes.size} required)" } check(index == attributes.size) { "Vertex $vertexIndex is not fully filled (only $index attributes provided, ${attributes.size} required)" }
return this@StreamVertexBuilder return this@StreamVertexBuilder

View File

@ -8,7 +8,7 @@ import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.gl.* import ru.dbotthepony.kstarbound.client.gl.*
import ru.dbotthepony.kstarbound.defs.tile.* import ru.dbotthepony.kstarbound.defs.tile.*
import ru.dbotthepony.kstarbound.world.ChunkTile import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kstarbound.world.ITileChunk import ru.dbotthepony.kstarbound.world.ITileChunk
import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
@ -80,7 +80,7 @@ class TileRenderers(val state: GLStateTracker) {
} }
} }
private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTexture) { private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureHSVColor) {
override fun setup() { override fun setup() {
super.setup() super.setup()
state.activeTexture = 0 state.activeTexture = 0
@ -89,6 +89,8 @@ class TileRenderers(val state: GLStateTracker) {
texture.bind() texture.bind()
texture.textureMagFilter = GL_NEAREST texture.textureMagFilter = GL_NEAREST
texture.textureMinFilter = GL_NEAREST texture.textureMinFilter = GL_NEAREST
program["_color"] = FOREGROUND_COLOR
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -108,7 +110,7 @@ class TileRenderers(val state: GLStateTracker) {
} }
} }
private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureColor) { private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureHSVColor) {
override fun setup() { override fun setup() {
super.setup() super.setup()
state.activeTexture = 0 state.activeTexture = 0
@ -154,6 +156,7 @@ class TileRenderers(val state: GLStateTracker) {
companion object { companion object {
val BACKGROUND_COLOR = Color(0.4f, 0.4f, 0.4f) val BACKGROUND_COLOR = Color(0.4f, 0.4f, 0.4f)
val FOREGROUND_COLOR = Color(1f, 1f, 1f)
} }
} }
@ -163,16 +166,16 @@ private enum class TileRenderTesselateResult {
HALT HALT
} }
private fun vertexTextureBuilder() = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS) private fun vertexTextureBuilder() = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_HSV_TEXTURE, VertexType.QUADS)
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester { private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
override fun test(tile: ChunkTile?): Boolean { override fun test(tile: TileState?): Boolean {
return tile?.def == definition return tile?.def == definition
} }
} }
private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester { private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester {
override fun test(tile: ChunkTile?): Boolean { override fun test(tile: TileState?): Boolean {
return tile?.modifier == definition return tile?.modifier == definition
} }
} }
@ -191,7 +194,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
val bakedBackgroundProgramState = state.tileRenderers.background(texture) val bakedBackgroundProgramState = state.tileRenderers.background(texture)
// private var notifiedDepth = false // private var notifiedDepth = false
private fun tesselateAt(piece: RenderPiece, getter: ITileChunk, builder: DynamicVertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) { private fun tesselateAt(self: TileState, piece: RenderPiece, getter: ITileChunk, builder: DynamicVertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
val fx = pos.x.toFloat() val fx = pos.x.toFloat()
val fy = pos.y.toFloat() val fy = pos.y.toFloat()
@ -212,38 +215,35 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
d += offset.y / PIXELS_IN_STARBOUND_UNITf d += offset.y / PIXELS_IN_STARBOUND_UNITf
} }
if (def.renderParameters.variants == 0 || piece.texture != null || piece.variantStride == null) { var mins = piece.texturePosition
val (u0, v0) = texture.pixelToUV(piece.texturePosition) var maxs = piece.texturePosition + piece.textureSize
val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize)
builder.quadZ( if (def.renderParameters.variants != 0 && piece.variantStride != null && piece.texture == null) {
a,
b,
c,
d,
Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
} else {
val variant = (getter.randomDoubleFor(pos) * def.renderParameters.variants).toInt() val variant = (getter.randomDoubleFor(pos) * def.renderParameters.variants).toInt()
mins += piece.variantStride * variant
val (u0, v0) = texture.pixelToUV(piece.texturePosition + piece.variantStride * variant) maxs += piece.variantStride * variant
val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize + piece.variantStride * variant)
builder.quadZ(
a,
b,
c,
d,
Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
} }
if (def.renderParameters.multiColored && piece.colorStride != null && self.color != 0) {
mins += piece.colorStride * self.color
maxs += piece.colorStride * self.color
}
val (u0, v0) = texture.pixelToUV(mins)
val (u1, v1) = texture.pixelToUV(maxs)
builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0).after { it, _ -> it.push(if (!isModifier || self.modifier?.grass == true) self.hueShift else 0f) })
} }
private fun tesselatePiece( private fun tesselatePiece(
self: TileState,
matchPiece: RenderMatch, matchPiece: RenderMatch,
getter: ITileChunk, getter: ITileChunk,
layers: TileLayerList, layers: TileLayerList,
pos: Vector2i, pos: Vector2i,
thisBuilder: DynamicVertexBuilder, thisBuilder: DynamicVertexBuilder,
background: Boolean background: Boolean,
isModifier: Boolean,
): TileRenderTesselateResult { ): TileRenderTesselateResult {
if (matchPiece.test(getter, equalityTester, pos)) { if (matchPiece.test(getter, equalityTester, pos)) {
for (renderPiece in matchPiece.pieces) { for (renderPiece in matchPiece.pieces) {
@ -254,14 +254,14 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!)) state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
} }
tesselateAt(renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset) tesselateAt(self, renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset, isModifier)
} else { } else {
tesselateAt(renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset) tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier)
} }
} }
for (subPiece in matchPiece.subMatches) { for (subPiece in matchPiece.subMatches) {
val matched = tesselatePiece(subPiece, getter, layers, pos, thisBuilder, background) val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier)
if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) { if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) {
return TileRenderTesselateResult.HALT return TileRenderTesselateResult.HALT
@ -287,7 +287,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
* *
* Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf] * Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf]
*/ */
fun tesselate(getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false) { fun tesselate(self: TileState, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) {
// если у нас нет renderTemplate // если у нас нет renderTemplate
// то мы просто не можем его отрисовать // то мы просто не можем его отрисовать
val template = def.renderTemplate val template = def.renderTemplate
@ -296,7 +296,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
for ((_, matcher) in template.matches) { for ((_, matcher) in template.matches) {
for (matchPiece in matcher) { for (matchPiece in matcher) {
val matched = tesselatePiece(matchPiece, getter, layers, pos, vertexBuilder, background) val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier)
if (matched == TileRenderTesselateResult.HALT) { if (matched == TileRenderTesselateResult.HALT) {
break break

View File

@ -0,0 +1,53 @@
package ru.dbotthepony.kstarbound.defs.liquid
import com.google.gson.GsonBuilder
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
import ru.dbotthepony.kstarbound.registerTypeAdapter
import ru.dbotthepony.kvector.vector.Color
data class LiquidDefinition(
val name: String,
val liquidId: Int,
val description: String = "...",
val tickDelta: Int = 1,
val color: Color,
val itemDrop: String? = null,
val statusEffects: List<String> = listOf(),
val interactions: List<Interaction> = listOf(),
val texture: String,
val bottomLightMix: Color,
val textureMovementFactor: Double,
) {
data class Interaction(val liquid: Int, val liquidResult: Int? = null, val materialResult: String? = null) {
init {
require(liquidResult != null || materialResult != null) { "Both liquidResult and materialResult are missing" }
}
}
companion object {
val ADAPTER = KConcreteTypeAdapter.Builder(LiquidDefinition::class)
.plain(LiquidDefinition::name)
.plain(LiquidDefinition::liquidId)
.plain(LiquidDefinition::description)
.plain(LiquidDefinition::tickDelta)
.plain(LiquidDefinition::color)
.plain(LiquidDefinition::itemDrop)
.list(LiquidDefinition::statusEffects)
.list(LiquidDefinition::interactions)
.plain(LiquidDefinition::texture)
.plain(LiquidDefinition::bottomLightMix)
.plain(LiquidDefinition::textureMovementFactor)
.build()
val INTERACTION_ADAPTER = KConcreteTypeAdapter.Builder(Interaction::class)
.plain(Interaction::liquid)
.plain(Interaction::liquidResult)
.plain(Interaction::materialResult)
.build()
fun registerGson(gsonBuilder: GsonBuilder) {
gsonBuilder.registerTypeAdapter(ADAPTER)
gsonBuilder.registerTypeAdapter(INTERACTION_ADAPTER)
}
}
}

View File

@ -11,6 +11,7 @@ data class MaterialModifier(
val health: Int = 0, val health: Int = 0,
val harvestLevel: Int = 0, val harvestLevel: Int = 0,
val breaksWithTile: Boolean = true, val breaksWithTile: Boolean = true,
val grass: Boolean = false,
val miningSounds: List<String> = listOf(), val miningSounds: List<String> = listOf(),
val miningParticle: String? = null, val miningParticle: String? = null,
override val renderTemplate: RenderTemplate, override val renderTemplate: RenderTemplate,
@ -29,6 +30,7 @@ data class MaterialModifier(
.plain(MaterialModifier::health) .plain(MaterialModifier::health)
.plain(MaterialModifier::harvestLevel) .plain(MaterialModifier::harvestLevel)
.plain(MaterialModifier::breaksWithTile) .plain(MaterialModifier::breaksWithTile)
.plain(MaterialModifier::grass)
.list(MaterialModifier::miningSounds) .list(MaterialModifier::miningSounds)
.plain(MaterialModifier::miningParticle) .plain(MaterialModifier::miningParticle)
.plain(MaterialModifier::renderTemplate, RenderTemplate.CACHE) .plain(MaterialModifier::renderTemplate, RenderTemplate.CACHE)

View File

@ -4,6 +4,8 @@ import com.google.gson.GsonBuilder
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
const val TILE_COLOR_VARIANTS = 9
data class RenderParameters( data class RenderParameters(
val texture: String, val texture: String,
val variants: Int = 0, val variants: Int = 0,

View File

@ -12,7 +12,7 @@ import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.io.EnumAdapter import ru.dbotthepony.kstarbound.io.EnumAdapter
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
import ru.dbotthepony.kstarbound.util.WriteOnce import ru.dbotthepony.kstarbound.util.WriteOnce
import ru.dbotthepony.kstarbound.world.ChunkTile import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kstarbound.world.ITileGetter import ru.dbotthepony.kstarbound.world.ITileGetter
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -38,7 +38,7 @@ data class RenderPiece(
} }
fun interface EqualityRuleTester { fun interface EqualityRuleTester {
fun test(tile: ChunkTile?): Boolean fun test(tile: TileState?): Boolean
} }
data class RenderRuleList( data class RenderRuleList(

View File

@ -279,7 +279,7 @@ class KConcreteTypeAdapter<T : Any>(
val name = fieldId.toString() val name = fieldId.toString()
if (loggedMisses.add(name)) { if (loggedMisses.add(name)) {
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field") LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
} }
reader.skipValue() reader.skipValue()
@ -306,7 +306,7 @@ class KConcreteTypeAdapter<T : Any>(
if (fieldId == -1) { if (fieldId == -1) {
if (loggedMisses.add(name)) { if (loggedMisses.add(name)) {
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field") LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
} }
reader.skipValue() reader.skipValue()

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.kstarbound.util
import kotlin.reflect.KClass
class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, private val height: Int) {
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? {
if (x !in 0 until width) {
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
}
if (y !in 0 until height) {
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
}
return memory[x + y * width]
}
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")
}
if (y !in 0 until height) {
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
}
val old = memory[x + y * width]
memory[x + y * width] = value
return old
}
}
inline fun <reified T : Any> TwoDimensionalArray(width: Int, height: Int) = TwoDimensionalArray(T::class, width, height)

View File

@ -4,8 +4,11 @@ import ru.dbotthepony.kbox2d.api.BodyDef
import ru.dbotthepony.kbox2d.api.FixtureDef import ru.dbotthepony.kbox2d.api.FixtureDef
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
import ru.dbotthepony.kbox2d.dynamics.B2Fixture import ru.dbotthepony.kbox2d.dynamics.B2Fixture
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TILE_COLOR_VARIANTS
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kstarbound.util.TwoDimensionalArray
import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderDepthFirst import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderDepthFirst
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderSizeFirst import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderSizeFirst
@ -18,19 +21,46 @@ import kotlin.collections.HashSet
/** /**
* Представляет из себя класс, который содержит состояние тайла на заданной позиции * Представляет из себя класс, который содержит состояние тайла на заданной позиции
*/ */
class ChunkTile(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) { class TileState(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
var color = 0 var color = 0
set(value) { set(value) {
if (value != field) { if (value != field) {
if (!def.renderParameters.multiColored) {
throw IllegalStateException("${def.materialName} can't be colored")
}
if (value !in 0 until TILE_COLOR_VARIANTS) {
throw IndexOutOfBoundsException("Tile variant $value is out of possible range 0 to $TILE_COLOR_VARIANTS")
}
field = value field = value
chunk.incChangeset() chunk.incChangeset()
} }
} }
var variant = -1 /**
* Выставляет hue shift как байтовое значение в диапазоне 0 .. 255
*/
fun setHueShift(value: Int) {
if (value < 0) {
hueShift = 0f
} else if (value > 255) {
hueShift = 360f
} else {
hueShift = (value / 255f) * 360f
}
}
var hueShift = 0f
set(value) { set(value) {
if (value != field) { var newValue = value % 360f
field = value
if (newValue < 0f) {
newValue += 360f
}
if (newValue != field) {
field = newValue
chunk.incChangeset() chunk.incChangeset()
} }
} }
@ -44,23 +74,28 @@ class ChunkTile(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return other is ChunkTile && other.color == color && other.variant == variant && other.modifier === modifier && other.def === def return other is TileState && other.color == color && other.modifier === modifier && other.def === def
} }
override fun toString(): String { override fun toString(): String {
return "ChunkTile[$chunk, material = ${def.materialName}, color = $color, variant = $variant, modifier = ${modifier?.modName}]" return "ChunkTile[$chunk, material = ${def.materialName}, color = $color, modifier = ${modifier?.modName}]"
} }
override fun hashCode(): Int { override fun hashCode(): Int {
var result = chunk.hashCode() var result = chunk.hashCode()
result = 31 * result + def.hashCode() result = 31 * result + def.hashCode()
result = 31 * result + color result = 31 * result + color
result = 31 * result + variant
result = 31 * result + (modifier?.hashCode() ?: 0) result = 31 * result + (modifier?.hashCode() ?: 0)
return result return result
} }
} }
data class LiquidState(val chunk: Chunk<*, *>.TileLayer, val def: LiquidDefinition) {
var pressure: Float = 0f
var level: Float = 1f
var isInfinite: Boolean = false
}
private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double { private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double {
if (point.x > 0.0) { if (point.x > 0.0) {
return point.dot(axis) return point.dot(axis)
@ -308,16 +343,16 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
/** /**
* Хранит тайлы как x + y * CHUNK_SIZE * Хранит тайлы как x + y * CHUNK_SIZE
*/ */
private val tiles = arrayOfNulls<ChunkTile>(CHUNK_SIZE * CHUNK_SIZE) private val tiles = arrayOfNulls<TileState>(CHUNK_SIZE * CHUNK_SIZE)
override operator fun get(x: Int, y: Int): ChunkTile? { override operator fun get(x: Int, y: Int): TileState? {
if (isOutside(x, y)) if (isOutside(x, y))
return null return null
return tiles[x or (y shl CHUNK_SHIFT)] return tiles[x or (y shl CHUNK_SHIFT)]
} }
operator fun set(x: Int, y: Int, tile: ChunkTile?) { operator fun set(x: Int, y: Int, tile: TileState?) {
if (isOutside(x, y)) if (isOutside(x, y))
throw IndexOutOfBoundsException("Trying to set tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range") throw IndexOutOfBoundsException("Trying to set tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range")
@ -326,11 +361,11 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
markPhysicsDirty() markPhysicsDirty()
} }
override operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { override operator fun set(x: Int, y: Int, tile: TileDefinition?): TileState? {
if (isOutside(x, y)) if (isOutside(x, y))
throw IndexOutOfBoundsException("Trying to set tile ${tile?.materialName} at $x $y, but that is outside of chunk's range") throw IndexOutOfBoundsException("Trying to set tile ${tile?.materialName} at $x $y, but that is outside of chunk's range")
val chunkTile = if (tile != null) ChunkTile(this, tile) else null val chunkTile = if (tile != null) TileState(this, tile) else null
this[x, y] = chunkTile this[x, y] = chunkTile
changeset++ changeset++
markPhysicsDirty() markPhysicsDirty()
@ -344,8 +379,11 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
val foreground = TileLayer() val foreground = TileLayer()
val background = TileLayer() val background = TileLayer()
protected val liquidStates: TwoDimensionalArray<LiquidState> = TwoDimensionalArray(CHUNK_SIZE, CHUNK_SIZE)
protected val entities = HashSet<Entity>() protected val entities = HashSet<Entity>()
val entitiesAccess = Collections.unmodifiableSet(entities) val entitiesAccess: Set<Entity> = Collections.unmodifiableSet(entities)
protected abstract fun onEntityAdded(entity: Entity) protected abstract fun onEntityAdded(entity: Entity)
protected abstract fun onEntityTransferedToThis(entity: Entity, otherChunk: This) protected abstract fun onEntityTransferedToThis(entity: Entity, otherChunk: This)
@ -396,8 +434,8 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
val EMPTY = object : IMutableTileChunk { val EMPTY = object : IMutableTileChunk {
override val pos = ChunkPos(0, 0) override val pos = ChunkPos(0, 0)
override fun get(x: Int, y: Int): ChunkTile? = null override fun get(x: Int, y: Int): TileState? = null
override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? = null override fun set(x: Int, y: Int, tile: TileDefinition?): TileState? = null
} }
private val aabbBase = AABB( private val aabbBase = AABB(

View File

@ -26,7 +26,7 @@ interface ITileGetter : ITileMap {
/** /**
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка * Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/ */
operator fun get(x: Int, y: Int): ChunkTile? operator fun get(x: Int, y: Int): TileState?
/** /**
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка * Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
@ -38,8 +38,8 @@ interface ITileGetter : ITileMap {
* *
* Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка * Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка
*/ */
val posToTile: Iterator<Pair<Vector2i, ChunkTile?>> get() { val posToTile: Iterator<Pair<Vector2i, TileState?>> get() {
return object : Iterator<Pair<Vector2i, ChunkTile?>> { return object : Iterator<Pair<Vector2i, TileState?>> {
private var x = 0 private var x = 0
private var y = 0 private var y = 0
@ -49,7 +49,7 @@ interface ITileGetter : ITileMap {
return idx() < CHUNK_SIZE * CHUNK_SIZE return idx() < CHUNK_SIZE * CHUNK_SIZE
} }
override fun next(): Pair<Vector2i, ChunkTile?> { override fun next(): Pair<Vector2i, TileState?> {
if (!hasNext()) { if (!hasNext()) {
throw IllegalStateException("Already iterated everything!") throw IllegalStateException("Already iterated everything!")
} }
@ -121,7 +121,7 @@ interface ITileSetter : ITileMap {
/** /**
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка * Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/ */
operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? operator fun set(x: Int, y: Int, tile: TileDefinition?): TileState?
/** /**
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка * Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/ */

View File

@ -22,7 +22,7 @@ open class TileView(
open val bottomLeft: ITileChunk?, open val bottomLeft: ITileChunk?,
open val bottomRight: ITileChunk?, open val bottomRight: ITileChunk?,
) : ITileChunk { ) : ITileChunk {
override fun get(x: Int, y: Int): ChunkTile? { override fun get(x: Int, y: Int): TileState? {
if (x in 0 ..CHUNK_SIZE_FF) { if (x in 0 ..CHUNK_SIZE_FF) {
if (y in 0 ..CHUNK_SIZE_FF) { if (y in 0 ..CHUNK_SIZE_FF) {
return center[x, y] return center[x, y]
@ -75,7 +75,7 @@ class MutableTileView(
override val bottomLeft: IMutableTileChunk?, override val bottomLeft: IMutableTileChunk?,
override val bottomRight: IMutableTileChunk?, override val bottomRight: IMutableTileChunk?,
) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk { ) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { override fun set(x: Int, y: Int, tile: TileDefinition?): TileState? {
if (x in 0 .. CHUNK_SIZE_FF) { if (x in 0 .. CHUNK_SIZE_FF) {
if (y in 0 .. CHUNK_SIZE_FF) { if (y in 0 .. CHUNK_SIZE_FF) {
return center.set(x, y, tile) return center.set(x, y, tile)

View File

@ -323,7 +323,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
) )
} }
fun getTile(pos: Vector2i): ChunkTile? { fun getTile(pos: Vector2i): TileState? {
return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
} }
@ -333,7 +333,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
return chunk return chunk
} }
fun getBackgroundTile(pos: Vector2i): ChunkTile? { fun getBackgroundTile(pos: Vector2i): TileState? {
return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
} }

View File

@ -2,11 +2,11 @@ package ru.dbotthepony.kstarbound.world.phys
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.ChunkTile import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
class RectTileFlooderDepthFirst( class RectTileFlooderDepthFirst(
private val tiles: Array<ChunkTile?>, private val tiles: Array<TileState?>,
private val seen: BooleanArray, private val seen: BooleanArray,
rootx: Int, rootx: Int,
rooty: Int rooty: Int

View File

@ -2,11 +2,11 @@ package ru.dbotthepony.kstarbound.world.phys
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.ChunkTile import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
class RectTileFlooderSizeFirst( class RectTileFlooderSizeFirst(
private val tiles: Array<ChunkTile?>, private val tiles: Array<TileState?>,
private val seen: BooleanArray, private val seen: BooleanArray,
private val rootx: Int, private val rootx: Int,
private val rooty: Int private val rooty: Int

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.world.phys
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.ChunkTile import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.ndouble.Vector2d
private data class TileExposure( private data class TileExposure(
@ -14,7 +14,7 @@ private data class TileExposure(
) )
private class TileFlooder( private class TileFlooder(
private val tiles: Array<ChunkTile?>, private val tiles: Array<TileState?>,
private val seen: BooleanArray, private val seen: BooleanArray,
rootx: Int, rootx: Int,
rooty: Int rooty: Int

View File

@ -0,0 +1,38 @@
#version 460
uniform sampler2D _texture;
uniform vec4 _color;
in vec2 _uv_out;
in float _hsv_vertex;
out vec4 _color_out;
// https://gist.github.com/983/e170a24ae8eba2cd174f
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
vec4 texel = texture(_texture, _uv_out);
vec3 hsv2 = rgb2hsv(vec3(texel[0], texel[1], texel[2]));
hsv2[0] += _hsv_vertex / 360;
vec4 rgb2 = vec4(hsv2rgb(hsv2), texel[3]);
_color_out = _color * rgb2;
}

View File

@ -0,0 +1,16 @@
#version 460
layout (location = 0) in vec3 _pos;
layout (location = 1) in vec2 _uv_in;
layout (location = 2) in float _hsv_in;
out vec2 _uv_out;
out float _hsv_vertex;
uniform mat4 _transform;
void main() {
_uv_out = _uv_in;
_hsv_vertex = _hsv_in;
gl_Position = _transform * vec4(_pos, 1.0);
}