diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index f28e6c95..be4458ee 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -162,6 +162,14 @@ private fun loop() { } } + /* + for (x in 0 .. 24) { + for (y in 0 .. 24) { + chunk[x, y] = Starbound.getTileDefinition("sewerpipe") + } + } + */ + val chunkRenderer = ChunkRenderer(state, chunk) chunkRenderer.tesselateStatic() chunkRenderer.uploadStatic() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt index bdd2ad60..1fadbb81 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt @@ -188,8 +188,8 @@ sealed class RenderRule(params: Map) { "EqualsSelf" -> RenderRuleEqualsSelf(params) "Shadows" -> RenderRuleShadows(params) - // неизвестно что оно делает - "Connects" -> AlwaysFailingRenderRule(params) + // неизвестно что оно делает, но вероятнее всего, есть ли там что либо в принципе + "Connects" -> RenderRuleConnects(params) else -> throw IllegalArgumentException("Unknown render rule '$name'") } @@ -236,6 +236,15 @@ class RenderRuleShadows(params: Map) : RenderRule(params) { } } +class RenderRuleConnects(params: Map) : RenderRule(params) { + override fun test(getter: IChunk, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + if (inverse) + return getter[thisPos + offsetPos] == null + + return getter[thisPos + offsetPos] != null + } +} + class AlwaysPassingRenderRule(params: Map) : RenderRule(params) { override fun test(getter: IChunk, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { return inverse @@ -345,10 +354,19 @@ data class TileRenderMatchPositioned( data class TileRenderMatchPiece( val pieces: List, val matchAllPoints: List, - val subMatches: List + val matchAnyPoints: List, + val subMatches: List, + val haltOnSubMatch: Boolean, + val haltOnMatch: Boolean ) { + init { + if (matchAnyPoints.isNotEmpty() || matchAllPoints.isNotEmpty()) { + require(matchAnyPoints.isEmpty() || matchAllPoints.isEmpty()) { "Both matchAllPoints and matchAnyPoints are present, this is not valid." } + } + } + /** - * Возвращает, сработали ли ВСЕ [matchAllPoints] + * Возвращает, сработали ли ВСЕ [matchAllPoints] или ЛЮБОЙ ИЗ [matchAnyPoints] * * Если хотя бы один из них вернул false, весь тест возвращает false * @@ -361,7 +379,17 @@ data class TileRenderMatchPiece( } } - return true + if (matchAnyPoints.isEmpty()) { + return true + } + + for (matcher in matchAnyPoints) { + if (matcher.test(getter, thisRef, thisPos)) { + return true + } + } + + return false } companion object { @@ -386,6 +414,16 @@ data class TileRenderMatchPiece( return@let ImmutableList.copyOf(list) } ?: listOf() + val matchAnyPoints = input["matchAnyPoints"]?.asJsonArray?.let { + val list = ArrayList() + + for (thisPiece in it) { + list.add(TileRenderMatchPositioned.fromJson(thisPiece.asJsonArray, rulePieces)) + } + + return@let ImmutableList.copyOf(list) + } ?: listOf() + val subMatches = input["subMatches"]?.asJsonArray?.let { val list = ArrayList() @@ -396,7 +434,18 @@ data class TileRenderMatchPiece( return@let ImmutableList.copyOf(list) } ?: listOf() - return TileRenderMatchPiece(pieces, matchAllPoints, subMatches) + val haltOnSubMatch = input["haltOnSubMatch"]?.asBoolean ?: false + val haltOnMatch = input["haltOnMatch"]?.asBoolean ?: false + + return TileRenderMatchPiece( + pieces = pieces, + matchAllPoints = matchAllPoints, + matchAnyPoints = matchAnyPoints, + subMatches = subMatches, + + haltOnSubMatch = haltOnSubMatch, + haltOnMatch = haltOnMatch, + ) } } } @@ -410,8 +459,12 @@ data class TileRenderMatch( val name = input[0].asString val pieces = ArrayList() - for (elem in input[1].asJsonArray) { - pieces.add(TileRenderMatchPiece.fromJson(elem.asJsonObject, tilePieces, rulePieces)) + try { + for (elem in input[1].asJsonArray) { + pieces.add(TileRenderMatchPiece.fromJson(elem.asJsonObject, tilePieces, rulePieces)) + } + } catch(err: Throwable) { + throw IllegalArgumentException("Failed to deserialize render match rule $name", err) } return TileRenderMatch(name, ImmutableList.copyOf(pieces)) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt index 764cd3d5..4e046caf 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.render +import org.apache.logging.log4j.LogManager import org.lwjgl.glfw.GLFW.glfwGetTime import org.lwjgl.opengl.GL46.* import ru.dbotthepony.kstarbound.Starbound @@ -12,7 +13,10 @@ import ru.dbotthepony.kstarbound.math.Vector2i import ru.dbotthepony.kstarbound.world.IChunk import kotlin.collections.HashMap -data class TileLayer(val bakedProgramState: BakedProgramState, val vertexBuilder: VertexBuilder, val zPos: Int) +data class TileLayer( + val bakedProgramState: BakedProgramState, + val vertexBuilder: VertexBuilder, + val zPos: Int) class TileLayerList { private val layers = HashMap>() @@ -63,6 +67,7 @@ class TileRenderers(val state: GLStateTracker) { override fun setup() { super.setup() state.activeTexture = 0 + // state.depthTest = true program["_texture"] = 0 texture.bind() texture.textureMagFilter = GL_NEAREST @@ -94,12 +99,19 @@ class TileRenderers(val state: GLStateTracker) { } } +private enum class TileRenderTesselateResult { + NO_MATCH, + CONTINUE, + HALT +} + class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { val texture = state.loadNamedTexture(tile.render.texture).also { it.textureMagFilter = GL_NEAREST } val bakedProgramState = state.tileRenderers.simpleProgram(texture) + // private var notifiedDepth = false private fun tesselateAt(piece: TileRenderPiece, getter: IChunk, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) { val fx = pos.x.toFloat() @@ -121,10 +133,18 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { d += offset.y / BASELINE_TEXTURE_SIZE } + /* + if (!notifiedDepth && tile.render.zLevel >= 5900) { + LOGGER.warn("Tile {} has out of bounds zLevel of {}", tile.materialName, tile.render.zLevel) + notifiedDepth = true + } + */ + if (tile.render.variants == 0 || piece.texture != null || piece.variantStride == null) { val (u0, v0) = texture.pixelToUV(piece.texturePosition) val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize) + //builder.quadZ(a, b, c, d, tile.render.zLevel.toFloat() + 200f, VertexTransformers.uv(u0, v1, u1, v0)) builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0)) } else { val variant = (getter.randomDoubleFor(pos) * tile.render.variants).toInt() @@ -132,15 +152,16 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { val (u0, v0) = texture.pixelToUV(piece.texturePosition + piece.variantStride * variant) val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize + piece.variantStride * variant) + //builder.quadZ(a, b, c, d, tile.render.zLevel.toFloat() + 200f, VertexTransformers.uv(u0, v1, u1, v0)) builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0)) } } - private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: IChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder) { + private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: IChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder): TileRenderTesselateResult { if (matchPiece.test(getter, tile, pos)) { for (renderPiece in matchPiece.pieces) { if (renderPiece.piece.texture != null) { - tesselateAt(renderPiece.piece, getter, layers.getLayer(state.tileRenderers.simpleProgram(state.loadNamedTexture(renderPiece.piece.texture)), tile.render.zLevel - 1) { + tesselateAt(renderPiece.piece, getter, layers.getLayer(state.tileRenderers.simpleProgram(state.loadNamedTexture(renderPiece.piece.texture)), tile.render.zLevel) { return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS) }, pos, renderPiece.offset) } else { @@ -149,9 +170,21 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { } for (subPiece in matchPiece.subMatches) { - tesselatePiece(subPiece, getter, layers, pos, thisBuilder) + val matched = tesselatePiece(subPiece, getter, layers, pos, thisBuilder) + + if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) { + return TileRenderTesselateResult.HALT + } } + + if (matchPiece.haltOnMatch) { + return TileRenderTesselateResult.HALT + } + + return TileRenderTesselateResult.CONTINUE } + + return TileRenderTesselateResult.NO_MATCH } /** @@ -174,7 +207,11 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { for ((_, matcher) in tile.render.renderTemplate.matches) { for (matchPiece in matcher.pieces) { - tesselatePiece(matchPiece, getter, layers, pos, builder) + val matched = tesselatePiece(matchPiece, getter, layers, pos, builder) + + if (matched == TileRenderTesselateResult.HALT) { + break + } } } @@ -275,5 +312,6 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { companion object { const val BASELINE_TEXTURE_SIZE = 8f const val Z_LEVEL = 1f + private val LOGGER = LogManager.getLogger() } } diff --git a/src/main/resources/shaders/f_texture.glsl b/src/main/resources/shaders/f_texture.glsl index fd8ec725..c6094cc7 100644 --- a/src/main/resources/shaders/f_texture.glsl +++ b/src/main/resources/shaders/f_texture.glsl @@ -8,6 +8,10 @@ in vec2 _uv_out; out vec4 _color_out; void main() { - _color_out = texture(_texture, _uv_out); - //_color_out = vec4(1.0, 1.0, 1.0, 1.0); + vec4 texel = texture(_texture, _uv_out); + + //if (texel.a < 0.5) + // discard; + + _color_out = texel; }