From 5b03608527a408d7acdc1c2999b7ffd5421f5326 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 3 Feb 2022 14:09:34 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D1=80=D0=B8=D1=81=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=84=D0=BE=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20?= =?UTF-8?q?=D1=82=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/ru/dbotthepony/kstarbound/Main.kt | 32 +++-- .../ru/dbotthepony/kstarbound/api/Structs.kt | 2 +- .../ru/dbotthepony/kstarbound/gl/GLShader.kt | 16 +++ .../kstarbound/gl/GLShaderProgram.kt | 16 +-- .../kstarbound/gl/GLStateTracker.kt | 27 +++-- .../ru/dbotthepony/kstarbound/math/Vector.kt | 40 ++++-- .../kstarbound/render/ChunkRenderer.kt | 52 ++++++-- .../kstarbound/render/TileRenderer.kt | 80 +++++++++--- .../ru/dbotthepony/kstarbound/util/Color.kt | 3 +- .../ru/dbotthepony/kstarbound/world/Chunk.kt | 114 +++++++++++++++++- .../ru/dbotthepony/kstarbound/world/World.kt | 77 ++++++++++++ .../{f_texture.glsl => fragment/texture.glsl} | 4 - .../shaders/fragment/texture_color.glsl | 13 ++ src/main/resources/shaders/tile_fragment.glsl | 13 -- src/main/resources/shaders/tile_vertex.glsl | 14 --- .../texture.glsl} | 0 16 files changed, 408 insertions(+), 95 deletions(-) rename src/main/resources/shaders/{f_texture.glsl => fragment/texture.glsl} (81%) create mode 100644 src/main/resources/shaders/fragment/texture_color.glsl delete mode 100644 src/main/resources/shaders/tile_fragment.glsl delete mode 100644 src/main/resources/shaders/tile_vertex.glsl rename src/main/resources/shaders/{v_vertex_texture.glsl => vertex/texture.glsl} (100%) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 768c48da..d80660af 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -144,17 +144,17 @@ private fun loop() { var y = 0 for (tile in Starbound.tilesAccess.values) { - chunk.foreground[x, y + 1] = tile - chunk.foreground[x++, y] = tile - chunk.foreground[x, y + 1] = tile - chunk.foreground[x++, y] = tile - chunk.foreground[x, y + 1] = tile - chunk.foreground[x++, y] = tile - chunk.foreground[x, y + 1] = tile - chunk.foreground[x++, y] = tile - chunk.foreground[x, y + 1] = tile - chunk.foreground[x++, y] = tile - chunk.foreground[x, y + 1] = tile + chunk.background[x, y + 1] = tile + chunk.background[x++, y] = tile + chunk.background[x, y + 1] = tile + chunk.background[x++, y] = tile + chunk.background[x, y + 1] = tile + chunk.background[x++, y] = tile + chunk.background[x, y + 1] = tile + chunk.background[x++, y] = tile + chunk.background[x, y + 1] = tile + chunk.background[x++, y] = tile + chunk.background[x, y + 1] = tile if (x >= 32) { x = 0 @@ -162,6 +162,14 @@ private fun loop() { } } + val tile = Starbound.getTileDefinition("glass") + + for (x in 0 .. 32) { + for (y in 0 .. 32) { + chunk.foreground[x, y] = tile + } + } + /* for (x in 0 .. 24) { for (y in 0 .. 24) { @@ -170,7 +178,7 @@ private fun loop() { } */ - val chunkRenderer = ChunkRenderer(state, chunk) + val chunkRenderer = ChunkRenderer(state, chunk, Starbound.world) chunkRenderer.tesselateStatic() chunkRenderer.uploadStatic() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/api/Structs.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/api/Structs.kt index 13a8ff08..4364e7dc 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/api/Structs.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/api/Structs.kt @@ -9,6 +9,6 @@ interface IStruct3f : IStruct2f { operator fun component3(): Float } -interface IStruct4F : IStruct3f { +interface IStruct4f : IStruct3f { operator fun component4(): Float } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShader.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShader.kt index 60be4047..45dea9c0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShader.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShader.kt @@ -18,6 +18,10 @@ class GLShader( private set init { + if (body == "") { + throw IllegalArgumentException("Shader source is empty") + } + glShaderSource(pointer, body) glCompileShader(pointer) @@ -43,5 +47,17 @@ class GLShader( companion object { fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER) fun fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER) + + private fun readInternal(file: String): String { + return ClassLoader.getSystemClassLoader().getResourceAsStream(file)!!.bufferedReader() + .let { + val read = it.readText() + it.close() + return@let read + } + } + + fun internalVertex(file: String) = GLShader(readInternal(file), GL_VERTEX_SHADER) + fun internalFragment(file: String) = GLShader(readInternal(file), GL_FRAGMENT_SHADER) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShaderProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShaderProgram.kt index 20c78def..28f786cf 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShaderProgram.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLShaderProgram.kt @@ -2,10 +2,9 @@ package ru.dbotthepony.kstarbound.gl import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import it.unimi.dsi.fastutil.objects.Object2ObjectFunction -import org.lwjgl.opengl.GL41 import org.lwjgl.opengl.GL46.* import ru.dbotthepony.kstarbound.api.IStruct3f -import ru.dbotthepony.kstarbound.api.IStruct4F +import ru.dbotthepony.kstarbound.api.IStruct4f import ru.dbotthepony.kstarbound.math.AbstractMatrix3f import ru.dbotthepony.kstarbound.math.AbstractMatrix4f import ru.dbotthepony.kstarbound.math.FloatMatrix @@ -14,11 +13,11 @@ import kotlin.collections.HashSet class ShaderLinkException(reason: String) : RuntimeException(reason) -data class Uniform4f(val x: Float, val y: Float, val z: Float, val w: Float) : IStruct4F +data class Uniform4f(val x: Float, val y: Float, val z: Float, val w: Float) : IStruct4f data class Uniform3f(val x: Float, val y: Float, val z: Float) : IStruct3f class GLUniformLocation(val program: GLShaderProgram, val name: String, val pointer: Int) { - fun set(value: IStruct4F): GLUniformLocation { + fun set(value: IStruct4f): GLUniformLocation { program.state.ensureSameThread() val (v0, v1, v2, v3) = value glProgramUniform4f(program.pointer, pointer, v0, v1, v2, v3) @@ -99,24 +98,25 @@ class GLShaderProgram(val state: GLStateTracker, vararg shaders: GLShader) { }).orElse(null) } - operator fun set(name: String, value: Uniform4f) = this[name]?.set(value) - operator fun set(name: String, value: Uniform3f) = this[name]?.set(value) + operator fun set(name: String, value: IStruct4f) = this[name]?.set(value) + operator fun set(name: String, value: IStruct3f) = this[name]?.set(value) operator fun set(name: String, value: Int) = this[name]?.set(value) operator fun set(name: String, value: FloatMatrix<*>) = this[name]?.set(value) fun attach(shader: GLShader) { state.ensureSameThread() - check(!linked) { "Already linked!" } + check(!linked) { "Program is already linked!" } if (!attached.add(shader)) { throw IllegalStateException("Already attached! $shader") } + require(!shader.unlinked) { "$shader is already unlinked, and thus can not be used" } glAttachShader(pointer, shader.pointer) } fun link() { - check(!linked) { "Already linked!" } + check(!linked) { "Program is already linked!" } glLinkProgram(pointer) val success = intArrayOf(0) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLStateTracker.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLStateTracker.kt index 5947c508..bf77137c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLStateTracker.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/gl/GLStateTracker.kt @@ -204,13 +204,26 @@ class GLStateTracker { return obj } - val shaderVertexTexture = program( - GLShader.fragment(File("./src/main/resources/shaders/f_texture.glsl")), - GLShader.vertex(File("./src/main/resources/shaders/v_vertex_texture.glsl")) - ).also { - it.link() - it.unlinkChildren() - it["_transform"] = Matrix4f.IDENTITY + val shaderVertexTexture: GLShaderProgram + val shaderVertexTextureColor: GLShaderProgram + + init { + val textureF = GLShader.internalFragment("shaders/fragment/texture.glsl") + val textureColorF = GLShader.internalFragment("shaders/fragment/texture_color.glsl") + val textureV = GLShader.internalVertex("shaders/vertex/texture.glsl") + + shaderVertexTexture = program(textureF, textureV) + shaderVertexTextureColor = program(textureColorF, textureV) + + shaderVertexTexture.link() + shaderVertexTexture["_transform"] = Matrix4f.IDENTITY + + shaderVertexTextureColor.link() + shaderVertexTextureColor["_transform"] = Matrix4f.IDENTITY + + textureF.unlink() + textureColorF.unlink() + textureV.unlink() } val matrixStack = Matrix4fStack() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/math/Vector.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/math/Vector.kt index 72a067c7..4247e397 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/math/Vector.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/math/Vector.kt @@ -4,19 +4,27 @@ import com.google.gson.JsonArray import kotlin.math.cos import kotlin.math.sin -data class Vector2i(val x: Int, val y: Int) : IMatrixLike, IMatrixLikeGetterI { - operator fun plus(other: Vector2i) = Vector2i(x + other.x, y + other.y) - operator fun plus(other: Int) = Vector2i(x + other, y + other) - operator fun minus(other: Vector2i) = Vector2i(x - other.x, y - other.y) - operator fun minus(other: Int) = Vector2i(x - other, y - other) - operator fun times(other: Vector2i) = Vector2i(x * other.x, y * other.y) - operator fun times(other: Int) = Vector2i(x * other, y * other) - operator fun div(other: Vector2i) = Vector2i(x / other.x, y / other.y) - operator fun div(other: Int) = Vector2i(x / other, y / other) - +abstract class IVector2i> : IMatrixLike, IMatrixLikeGetterI { override val columns = 1 override val rows = 2 + abstract val x: Int + abstract val y: Int + + operator fun plus(other: IVector2i<*>) = make(x + other.x, y + other.y) + operator fun plus(other: Int) = make(x + other, y + other) + operator fun minus(other: IVector2i<*>) = make(x - other.x, y - other.y) + operator fun minus(other: Int) = make(x - other, y - other) + operator fun times(other: IVector2i<*>) = make(x * other.x, y * other.y) + operator fun times(other: Int) = make(x * other, y * other) + operator fun div(other: IVector2i<*>) = make(x / other.x, y / other.y) + operator fun div(other: Int) = make(x / other, y / other) + + fun left() = make(x - 1, y) + fun right() = make(x + 1, y) + fun up() = make(x, y + 1) + fun down() = make(x, y - 1) + override fun get(row: Int, column: Int): Int { if (column != 0) { throw IndexOutOfBoundsException("Column must be 0 ($column given)") @@ -29,12 +37,22 @@ data class Vector2i(val x: Int, val y: Int) : IMatrixLike, IMatrixLikeGetterI { } } + protected abstract fun make(x: Int, y: Int): T +} + +data class Vector2i(override val x: Int = 0, override val y: Int = 0) : IVector2i() { + override fun make(x: Int, y: Int) = Vector2i(x, y) + companion object { fun fromJson(input: JsonArray): Vector2i { return Vector2i(input[0].asInt, input[1].asInt) } - val ZERO = Vector2i(0, 0) + val ZERO = Vector2i() + val LEFT = Vector2i().left() + val RIGHT = Vector2i().right() + val UP = Vector2i().up() + val DOWN = Vector2i().down() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt index 7eb3d4de..b8d86d1f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt @@ -3,14 +3,24 @@ package ru.dbotthepony.kstarbound.render import ru.dbotthepony.kstarbound.gl.* import ru.dbotthepony.kstarbound.math.FloatMatrix import ru.dbotthepony.kstarbound.world.Chunk +import ru.dbotthepony.kstarbound.world.ITileChunk +import ru.dbotthepony.kstarbound.world.World import kotlin.collections.ArrayList -import kotlin.collections.HashMap -class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable { - private val layers = TileLayerList() +class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk, val world: World? = null) : AutoCloseable { + private val foregroundLayers = TileLayerList() + private val backgroundLayers = TileLayerList() private val bakedMeshes = ArrayList() private val unloadableBakedMeshes = ArrayList() + private fun getForeground(): ITileChunk { + return world?.getForegroundView(chunk.pos) ?: chunk.foreground + } + + private fun getBackground(): ITileChunk { + return world?.getBackgroundView(chunk.pos) ?: chunk.background + } + /** * Тесселирует "статичную" геометрию в builders (к примеру тайлы). * @@ -29,13 +39,26 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable bakedMeshes.clear() } - layers.clear() + foregroundLayers.clear() + + val foreground = getForeground() // TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) - for ((pos, tile) in chunk.foreground.posToTile) { + for ((pos, tile) in foreground.posToTile) { if (tile != null) { val renderer = state.tileRenderers.get(tile.def.materialName) - renderer.tesselate(chunk.foreground, layers, pos) + renderer.tesselate(foreground, foregroundLayers, pos) + } + } + + backgroundLayers.clear() + + val background = getBackground() + + for ((pos, tile) in background.posToTile) { + if (tile != null) { + val renderer = state.tileRenderers.get(tile.def.materialName) + renderer.tesselate(background, backgroundLayers, pos, background = true) } } } @@ -49,7 +72,13 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable unloadUnused() // TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) - for ((pos, tile) in chunk.foreground.posToTile) { + for ((_, tile) in getForeground().posToTile) { + if (tile != null) { + state.tileRenderers.get(tile.def.materialName) + } + } + + for ((_, tile) in getBackground().posToTile) { if (tile != null) { state.tileRenderers.get(tile.def.materialName) } @@ -69,12 +98,17 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable fun uploadStatic(clear: Boolean = true) { unloadUnused() - for ((baked, builder) in layers.buildList()) { + for ((baked, builder) in backgroundLayers.buildList()) { + bakedMeshes.add(BakedStaticMesh(baked, builder)) + } + + for ((baked, builder) in foregroundLayers.buildList()) { bakedMeshes.add(BakedStaticMesh(baked, builder)) } if (clear) { - layers.clear() + backgroundLayers.clear() + foregroundLayers.clear() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt index e2e7073c..1ca2aaeb 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt @@ -8,6 +8,7 @@ import ru.dbotthepony.kstarbound.defs.TileRenderMatchPiece import ru.dbotthepony.kstarbound.defs.TileRenderPiece import ru.dbotthepony.kstarbound.gl.* import ru.dbotthepony.kstarbound.math.Vector2i +import ru.dbotthepony.kstarbound.util.Color import ru.dbotthepony.kstarbound.world.ITileChunk import kotlin.collections.HashMap @@ -52,7 +53,8 @@ class TileLayerList { } class TileRenderers(val state: GLStateTracker) { - private val simpleBakedPrograms = HashMap() + private val foregroundTilePrograms = HashMap() + private val backgroundTilePrograms = HashMap() private val tileRenderers = HashMap() operator fun get(tile: String): TileRenderer { @@ -61,11 +63,11 @@ class TileRenderers(val state: GLStateTracker) { } } - private inner class SimpleBakedProgram(private val texture: GLTexture2D) : BakedProgramState(state.shaderVertexTexture) { + private inner class ForegroundTileProgram(private val texture: GLTexture2D) : BakedProgramState(state.shaderVertexTexture) { override fun setup() { super.setup() state.activeTexture = 0 - // state.depthTest = true + state.depthTest = false program["_texture"] = 0 texture.bind() texture.textureMagFilter = GL_NEAREST @@ -77,7 +79,37 @@ class TileRenderers(val state: GLStateTracker) { return true } - if (other is SimpleBakedProgram) { + if (other is ForegroundTileProgram) { + return texture == other.texture + } + + return super.equals(other) + } + + override fun hashCode(): Int { + return texture.hashCode() + } + } + + private inner class BackgroundTileProgram(private val texture: GLTexture2D) : BakedProgramState(state.shaderVertexTextureColor) { + override fun setup() { + super.setup() + state.activeTexture = 0 + state.depthTest = false + program["_texture"] = 0 + texture.bind() + texture.textureMagFilter = GL_NEAREST + texture.textureMinFilter = GL_NEAREST + + program["_color"] = BACKGROUND_COLOR + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + if (other is BackgroundTileProgram) { return texture == other.texture } @@ -92,8 +124,19 @@ class TileRenderers(val state: GLStateTracker) { /** * Возвращает запечённое состояние shaderVertexTexture с данной текстурой */ - fun simpleProgram(texture: GLTexture2D): BakedProgramState { - return simpleBakedPrograms.computeIfAbsent(texture, ::SimpleBakedProgram) + fun foreground(texture: GLTexture2D): BakedProgramState { + return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram) + } + + /** + * Возвращает запечённое состояние shaderVertexTextureColor с данной текстурой + */ + fun background(texture: GLTexture2D): BakedProgramState { + return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram) + } + + companion object { + val BACKGROUND_COLOR = Color(0.4f, 0.4f, 0.4f) } } @@ -108,7 +151,8 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { it.textureMagFilter = GL_NEAREST } - val bakedProgramState = state.tileRenderers.simpleProgram(texture) + val bakedProgramState = state.tileRenderers.foreground(texture) + val bakedBackgroundProgramState = state.tileRenderers.background(texture) // private var notifiedDepth = false private fun tesselateAt(piece: TileRenderPiece, getter: ITileChunk, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) { @@ -155,11 +199,19 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { } } - private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder): TileRenderTesselateResult { + private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder, background: Boolean): 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) { + val program: BakedProgramState + + if (background) { + program = state.tileRenderers.background(state.loadNamedTexture(renderPiece.piece.texture)) + } else { + program = state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture)) + } + + tesselateAt(renderPiece.piece, getter, layers.getLayer(program, tile.render.zLevel) { return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS) }, pos, renderPiece.offset) } else { @@ -168,7 +220,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { } for (subPiece in matchPiece.subMatches) { - val matched = tesselatePiece(subPiece, getter, layers, pos, thisBuilder) + val matched = tesselatePiece(subPiece, getter, layers, pos, thisBuilder, background) if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) { return TileRenderTesselateResult.HALT @@ -194,18 +246,18 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { * * Тесселирует тайлы в границы -1f .. CHUNK_SIZEf + 1f на основе [pos] */ - fun tesselate(getter: ITileChunk, layers: TileLayerList, pos: Vector2i) { + fun tesselate(getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false) { // если у нас нет renderTemplate // то мы просто не можем его отрисовать tile.render.renderTemplate ?: return - val builder = layers.getLayer(bakedProgramState, tile.render.zLevel) { + val builder = layers.getLayer(if (background) bakedBackgroundProgramState else bakedProgramState, tile.render.zLevel) { return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS) } for ((_, matcher) in tile.render.renderTemplate.matches) { for (matchPiece in matcher.pieces) { - val matched = tesselatePiece(matchPiece, getter, layers, pos, builder) + val matched = tesselatePiece(matchPiece, getter, layers, pos, builder, background) if (matched == TileRenderTesselateResult.HALT) { break @@ -216,7 +268,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { companion object { const val BASELINE_TEXTURE_SIZE = 8f - const val Z_LEVEL = 1f + const val Z_LEVEL = 10f private val LOGGER = LogManager.getLogger() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Color.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Color.kt index c02c19dd..d7ff31bf 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Color.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Color.kt @@ -1,8 +1,9 @@ package ru.dbotthepony.kstarbound.util import com.google.gson.JsonArray +import ru.dbotthepony.kstarbound.api.IStruct4f -data class Color(val red: Float, val green: Float, val blue: Float, val alpha: Float = 1f) { +data class Color(val red: Float, val green: Float, val blue: Float, val alpha: Float = 1f) : IStruct4f { constructor(input: JsonArray) : this(input[0].asFloat / 255f, input[1].asFloat / 255f, input[2].asFloat / 255f, if (input.size() >= 4) input[3].asFloat / 255f else 1f) companion object { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index b0f63666..ef968f75 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.kstarbound.world import ru.dbotthepony.kstarbound.defs.TileDefinition +import ru.dbotthepony.kstarbound.math.IVector2i import ru.dbotthepony.kstarbound.math.Vector2i /** @@ -137,13 +138,124 @@ const val CHUNK_SHIFT = 6 const val CHUNK_SIZE = 1 shl CHUNK_SHIFT // 64 const val CHUNK_SIZE_FF = CHUNK_SIZE - 1 -data class ChunkPos(val x: Int, val y: Int) { +data class ChunkPos(override val x: Int, override val y: Int) : IVector2i() { constructor(pos: Vector2i) : this(pos.x shr CHUNK_SHIFT, pos.y shr CHUNK_SHIFT) + override fun make(x: Int, y: Int) = ChunkPos(x, y) val firstBlock get() = Vector2i(x shl CHUNK_SHIFT, y shl CHUNK_SHIFT) val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SHIFT) - 1, ((y + 1) shl CHUNK_SHIFT) - 1) } +/** + * Предоставляет доступ к чанку и его соседям + * + * В основном для использования в местах, где нужен не мир, а определённый чанк мира, + * и при этом координаты проверяются относительно чанка и могут спокойно выйти за его пределы, + * с желанием получить тайл из соседнего чанка + */ +open class TileChunkView( + open val center: ITileChunk, + + open val right: ITileChunk?, + open val top: ITileChunk?, + open val topRight: ITileChunk?, + open val topLeft: ITileChunk?, + + open val left: ITileChunk?, + open val bottom: ITileChunk?, + open val bottomLeft: ITileChunk?, + open val bottomRight: ITileChunk?, +) : ITileChunk { + override fun get(x: Int, y: Int): ChunkTile? { + if (x in 0 .. CHUNK_SIZE_FF) { + if (y in 0 .. CHUNK_SIZE_FF) { + return center[x, y] + } + + if (y < 0) { + return bottom?.get(x, y + CHUNK_SIZE) + } else { + return top?.get(x, y - CHUNK_SIZE) + } + } + + if (x < 0) { + if (y in 0 .. CHUNK_SIZE_FF) { + return left?.get(x + CHUNK_SIZE, y) + } + + if (y < 0) { + return bottomLeft?.get(x + CHUNK_SIZE, y + CHUNK_SIZE) + } else { + return topLeft?.get(x + CHUNK_SIZE, y - CHUNK_SIZE) + } + } else { + if (y in 0 .. CHUNK_SIZE_FF) { + return right?.get(x - CHUNK_SIZE, y) + } + + if (y < 0) { + return bottomRight?.get(x - CHUNK_SIZE, y + CHUNK_SIZE) + } else { + return topRight?.get(x - CHUNK_SIZE, y - CHUNK_SIZE) + } + } + } + + override val pos: ChunkPos + get() = center.pos +} + +class MutableTileChunkView( + override val center: IMutableTileChunk, + + override val right: IMutableTileChunk?, + override val top: IMutableTileChunk?, + override val topRight: IMutableTileChunk?, + override val topLeft: IMutableTileChunk?, + + override val left: IMutableTileChunk?, + override val bottom: IMutableTileChunk?, + override val bottomLeft: IMutableTileChunk?, + override val bottomRight: IMutableTileChunk?, +) : TileChunkView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk { + override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { + if (x in 0 .. CHUNK_SIZE_FF) { + if (y in 0 .. CHUNK_SIZE_FF) { + return center.set(x, y, tile) + } + + if (y < 0) { + return bottom?.set(x, y + CHUNK_SIZE, tile) + } else { + return top?.set(x, y - CHUNK_SIZE, tile) + } + } + + if (x < 0) { + if (y in 0 .. CHUNK_SIZE_FF) { + return left?.set(x + CHUNK_SIZE, y, tile) + } + + if (y < 0) { + return bottomLeft?.set(x + CHUNK_SIZE, y + CHUNK_SIZE, tile) + } else { + return topLeft?.set(x + CHUNK_SIZE, y - CHUNK_SIZE, tile) + } + } else { + if (y in 0 .. CHUNK_SIZE_FF) { + return right?.set(x - CHUNK_SIZE, y, tile) + } + + if (y < 0) { + return bottomRight?.set(x - CHUNK_SIZE, y + CHUNK_SIZE, tile) + } else { + return topRight?.set(x - CHUNK_SIZE, y - CHUNK_SIZE, tile) + } + } + } +} + open class Chunk(val world: World, val pos: ChunkPos) { inner class ChunkTileLayer : IMutableTileChunk { override val pos: ChunkPos diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 16a838e3..6fac7904 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -39,9 +39,86 @@ class World(val seed: Long = 0L) { return chunk } + fun getForegroundView(pos: ChunkPos): TileChunkView? { + val get = getChunk(pos) ?: return null + + return TileChunkView( + center = get.foreground, + left = getChunk(pos.left())?.foreground, + top = getChunk(pos.up())?.foreground, + topLeft = getChunk(pos.up().left())?.foreground, + topRight = getChunk(pos.up().right())?.foreground, + right = getChunk(pos.right())?.foreground, + bottom = getChunk(pos.down())?.foreground, + bottomLeft = getChunk(pos.down().left())?.foreground, + bottomRight = getChunk(pos.down().right())?.foreground, + ) + } + + fun getBackgroundView(pos: ChunkPos): TileChunkView? { + val get = getChunk(pos) ?: return null + + return TileChunkView( + center = get.background, + left = getChunk(pos.left())?.background, + top = getChunk(pos.up())?.background, + topLeft = getChunk(pos.up().left())?.background, + topRight = getChunk(pos.up().right())?.background, + right = getChunk(pos.right())?.background, + bottom = getChunk(pos.down())?.background, + bottomLeft = getChunk(pos.down().left())?.background, + bottomRight = getChunk(pos.down().right())?.background, + ) + } + + /** + * Считается, что [pos] это абсолютные координаты ТАЙЛА в мире, поэтому они + * трансформируются в координаты чанка + */ fun getChunk(pos: Vector2i) = getChunk(ChunkPos(pos)) + + /** + * Считается, что [pos] это абсолютные координаты ТАЙЛА в мире, поэтому они + * трансформируются в координаты чанка + */ fun getOrMakeChunk(pos: Vector2i) = getOrMakeChunk(ChunkPos(pos)) + /** + * Считается, что [pos] это абсолютные координаты ТАЙЛА в мире, поэтому они + * трансформируются в координаты чанка + */ + fun getForegroundView(pos: Vector2i) = getForegroundView(ChunkPos(pos)) + + /** + * Считается, что [pos] это абсолютные координаты ТАЙЛА в мире, поэтому они + * трансформируются в координаты чанка + */ + fun getBackgroundView(pos: Vector2i) = getBackgroundView(ChunkPos(pos)) + + /** + * Считается, что [x] и [y] это абсолютные координаты ЧАНКА в мире, поэтому они + * НЕ трансформируются в координаты чанка, а используются напрямую + */ + fun getChunk(x: Int, y: Int) = getChunk(ChunkPos(x, y)) + + /** + * Считается, что [x] и [y] это абсолютные координаты ЧАНКА в мире, поэтому они + * НЕ трансформируются в координаты чанка, а используются напрямую + */ + fun getOrMakeChunk(x: Int, y: Int) = getOrMakeChunk(ChunkPos(x, y)) + + /** + * Считается, что [x] и [y] это абсолютные координаты ЧАНКА в мире, поэтому они + * НЕ трансформируются в координаты чанка, а используются напрямую + */ + fun getForegroundView(x: Int, y: Int) = getForegroundView(ChunkPos(x, y)) + + /** + * Считается, что [x] и [y] это абсолютные координаты ЧАНКА в мире, поэтому они + * НЕ трансформируются в координаты чанка, а используются напрямую + */ + fun getBackgroundView(x: Int, y: Int) = getBackgroundView(ChunkPos(x, y)) + fun getTile(pos: Vector2i): ChunkTile? { return getChunk(pos)?.foreground?.get(pos.x, pos.y) } diff --git a/src/main/resources/shaders/f_texture.glsl b/src/main/resources/shaders/fragment/texture.glsl similarity index 81% rename from src/main/resources/shaders/f_texture.glsl rename to src/main/resources/shaders/fragment/texture.glsl index c6094cc7..b25001e6 100644 --- a/src/main/resources/shaders/f_texture.glsl +++ b/src/main/resources/shaders/fragment/texture.glsl @@ -9,9 +9,5 @@ out vec4 _color_out; void main() { vec4 texel = texture(_texture, _uv_out); - - //if (texel.a < 0.5) - // discard; - _color_out = texel; } diff --git a/src/main/resources/shaders/fragment/texture_color.glsl b/src/main/resources/shaders/fragment/texture_color.glsl new file mode 100644 index 00000000..e4885cfa --- /dev/null +++ b/src/main/resources/shaders/fragment/texture_color.glsl @@ -0,0 +1,13 @@ + +#version 460 + +uniform sampler2D _texture; +uniform vec4 _color; + +in vec2 _uv_out; +out vec4 _color_out; + +void main() { + vec4 texel = texture(_texture, _uv_out); + _color_out = texel * _color; +} diff --git a/src/main/resources/shaders/tile_fragment.glsl b/src/main/resources/shaders/tile_fragment.glsl deleted file mode 100644 index 56e0526e..00000000 --- a/src/main/resources/shaders/tile_fragment.glsl +++ /dev/null @@ -1,13 +0,0 @@ - -#version 460 - -out vec4 FragColor; -in vec3 ourColor; -in vec2 TexCoord; - -uniform vec3 globalColor; -uniform sampler2D ourTexture; - -void main() { - FragColor = texture(ourTexture, TexCoord); -} diff --git a/src/main/resources/shaders/tile_vertex.glsl b/src/main/resources/shaders/tile_vertex.glsl deleted file mode 100644 index 4711ada5..00000000 --- a/src/main/resources/shaders/tile_vertex.glsl +++ /dev/null @@ -1,14 +0,0 @@ - -#version 460 - -layout (location = 0) in vec3 aPos; -layout (location = 1) in vec3 aColor; -layout (location = 2) in vec2 aTexCoord; -out vec3 ourColor; -out vec2 TexCoord; - -void main() { - gl_Position = vec4(aPos, 1.0); - ourColor = aColor; - TexCoord = aTexCoord; -} diff --git a/src/main/resources/shaders/v_vertex_texture.glsl b/src/main/resources/shaders/vertex/texture.glsl similarity index 100% rename from src/main/resources/shaders/v_vertex_texture.glsl rename to src/main/resources/shaders/vertex/texture.glsl