Отрисовка фоновых тайлов!

This commit is contained in:
DBotThePony 2022-02-03 14:09:34 +07:00
parent 3962eec095
commit 5b03608527
Signed by: DBot
GPG Key ID: DCC23B5715498507
16 changed files with 408 additions and 95 deletions

View File

@ -144,17 +144,17 @@ private fun loop() {
var y = 0 var y = 0
for (tile in Starbound.tilesAccess.values) { for (tile in Starbound.tilesAccess.values) {
chunk.foreground[x, y + 1] = tile chunk.background[x, y + 1] = tile
chunk.foreground[x++, y] = tile chunk.background[x++, y] = tile
chunk.foreground[x, y + 1] = tile chunk.background[x, y + 1] = tile
chunk.foreground[x++, y] = tile chunk.background[x++, y] = tile
chunk.foreground[x, y + 1] = tile chunk.background[x, y + 1] = tile
chunk.foreground[x++, y] = tile chunk.background[x++, y] = tile
chunk.foreground[x, y + 1] = tile chunk.background[x, y + 1] = tile
chunk.foreground[x++, y] = tile chunk.background[x++, y] = tile
chunk.foreground[x, y + 1] = tile chunk.background[x, y + 1] = tile
chunk.foreground[x++, y] = tile chunk.background[x++, y] = tile
chunk.foreground[x, y + 1] = tile chunk.background[x, y + 1] = tile
if (x >= 32) { if (x >= 32) {
x = 0 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 (x in 0 .. 24) {
for (y 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.tesselateStatic()
chunkRenderer.uploadStatic() chunkRenderer.uploadStatic()

View File

@ -9,6 +9,6 @@ interface IStruct3f : IStruct2f {
operator fun component3(): Float operator fun component3(): Float
} }
interface IStruct4F : IStruct3f { interface IStruct4f : IStruct3f {
operator fun component4(): Float operator fun component4(): Float
} }

View File

@ -18,6 +18,10 @@ class GLShader(
private set private set
init { init {
if (body == "") {
throw IllegalArgumentException("Shader source is empty")
}
glShaderSource(pointer, body) glShaderSource(pointer, body)
glCompileShader(pointer) glCompileShader(pointer)
@ -43,5 +47,17 @@ class GLShader(
companion object { companion object {
fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER) fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER)
fun fragment(file: File) = GLShader(file, GL_FRAGMENT_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)
} }
} }

View File

@ -2,10 +2,9 @@ package ru.dbotthepony.kstarbound.gl
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import org.lwjgl.opengl.GL41
import org.lwjgl.opengl.GL46.* import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.api.IStruct3f 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.AbstractMatrix3f
import ru.dbotthepony.kstarbound.math.AbstractMatrix4f import ru.dbotthepony.kstarbound.math.AbstractMatrix4f
import ru.dbotthepony.kstarbound.math.FloatMatrix import ru.dbotthepony.kstarbound.math.FloatMatrix
@ -14,11 +13,11 @@ import kotlin.collections.HashSet
class ShaderLinkException(reason: String) : RuntimeException(reason) 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 data class Uniform3f(val x: Float, val y: Float, val z: Float) : IStruct3f
class GLUniformLocation(val program: GLShaderProgram, val name: String, val pointer: Int) { class GLUniformLocation(val program: GLShaderProgram, val name: String, val pointer: Int) {
fun set(value: IStruct4F): GLUniformLocation { fun set(value: IStruct4f): GLUniformLocation {
program.state.ensureSameThread() program.state.ensureSameThread()
val (v0, v1, v2, v3) = value val (v0, v1, v2, v3) = value
glProgramUniform4f(program.pointer, pointer, v0, v1, v2, v3) glProgramUniform4f(program.pointer, pointer, v0, v1, v2, v3)
@ -99,24 +98,25 @@ class GLShaderProgram(val state: GLStateTracker, vararg shaders: GLShader) {
}).orElse(null) }).orElse(null)
} }
operator fun set(name: String, value: Uniform4f) = this[name]?.set(value) operator fun set(name: String, value: IStruct4f) = this[name]?.set(value)
operator fun set(name: String, value: Uniform3f) = 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: Int) = this[name]?.set(value)
operator fun set(name: String, value: FloatMatrix<*>) = this[name]?.set(value) operator fun set(name: String, value: FloatMatrix<*>) = this[name]?.set(value)
fun attach(shader: GLShader) { fun attach(shader: GLShader) {
state.ensureSameThread() state.ensureSameThread()
check(!linked) { "Already linked!" } check(!linked) { "Program is already linked!" }
if (!attached.add(shader)) { if (!attached.add(shader)) {
throw IllegalStateException("Already attached! $shader") throw IllegalStateException("Already attached! $shader")
} }
require(!shader.unlinked) { "$shader is already unlinked, and thus can not be used" }
glAttachShader(pointer, shader.pointer) glAttachShader(pointer, shader.pointer)
} }
fun link() { fun link() {
check(!linked) { "Already linked!" } check(!linked) { "Program is already linked!" }
glLinkProgram(pointer) glLinkProgram(pointer)
val success = intArrayOf(0) val success = intArrayOf(0)

View File

@ -204,13 +204,26 @@ class GLStateTracker {
return obj return obj
} }
val shaderVertexTexture = program( val shaderVertexTexture: GLShaderProgram
GLShader.fragment(File("./src/main/resources/shaders/f_texture.glsl")), val shaderVertexTextureColor: GLShaderProgram
GLShader.vertex(File("./src/main/resources/shaders/v_vertex_texture.glsl"))
).also { init {
it.link() val textureF = GLShader.internalFragment("shaders/fragment/texture.glsl")
it.unlinkChildren() val textureColorF = GLShader.internalFragment("shaders/fragment/texture_color.glsl")
it["_transform"] = Matrix4f.IDENTITY 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() val matrixStack = Matrix4fStack()

View File

@ -4,19 +4,27 @@ import com.google.gson.JsonArray
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
data class Vector2i(val x: Int, val y: Int) : IMatrixLike, IMatrixLikeGetterI { abstract class IVector2i<T : IVector2i<T>> : 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)
override val columns = 1 override val columns = 1
override val rows = 2 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 { override fun get(row: Int, column: Int): Int {
if (column != 0) { if (column != 0) {
throw IndexOutOfBoundsException("Column must be 0 ($column given)") 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<Vector2i>() {
override fun make(x: Int, y: Int) = Vector2i(x, y)
companion object { companion object {
fun fromJson(input: JsonArray): Vector2i { fun fromJson(input: JsonArray): Vector2i {
return Vector2i(input[0].asInt, input[1].asInt) 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()
} }
} }

View File

@ -3,14 +3,24 @@ package ru.dbotthepony.kstarbound.render
import ru.dbotthepony.kstarbound.gl.* import ru.dbotthepony.kstarbound.gl.*
import ru.dbotthepony.kstarbound.math.FloatMatrix import ru.dbotthepony.kstarbound.math.FloatMatrix
import ru.dbotthepony.kstarbound.world.Chunk 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.ArrayList
import kotlin.collections.HashMap
class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable { class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk, val world: World? = null) : AutoCloseable {
private val layers = TileLayerList() private val foregroundLayers = TileLayerList()
private val backgroundLayers = TileLayerList()
private val bakedMeshes = ArrayList<BakedStaticMesh>() private val bakedMeshes = ArrayList<BakedStaticMesh>()
private val unloadableBakedMeshes = ArrayList<BakedStaticMesh>() private val unloadableBakedMeshes = ArrayList<BakedStaticMesh>()
private fun getForeground(): ITileChunk {
return world?.getForegroundView(chunk.pos) ?: chunk.foreground
}
private fun getBackground(): ITileChunk {
return world?.getBackgroundView(chunk.pos) ?: chunk.background
}
/** /**
* Тесселирует "статичную" геометрию в builders (к примеру тайлы). * Тесселирует "статичную" геометрию в builders (к примеру тайлы).
* *
@ -29,13 +39,26 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable
bakedMeshes.clear() bakedMeshes.clear()
} }
layers.clear() foregroundLayers.clear()
val foreground = getForeground()
// TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) // TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока)
for ((pos, tile) in chunk.foreground.posToTile) { for ((pos, tile) in foreground.posToTile) {
if (tile != null) { if (tile != null) {
val renderer = state.tileRenderers.get(tile.def.materialName) 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() unloadUnused()
// TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) // 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) { if (tile != null) {
state.tileRenderers.get(tile.def.materialName) state.tileRenderers.get(tile.def.materialName)
} }
@ -69,12 +98,17 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable
fun uploadStatic(clear: Boolean = true) { fun uploadStatic(clear: Boolean = true) {
unloadUnused() 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)) bakedMeshes.add(BakedStaticMesh(baked, builder))
} }
if (clear) { if (clear) {
layers.clear() backgroundLayers.clear()
foregroundLayers.clear()
} }
} }

View File

@ -8,6 +8,7 @@ import ru.dbotthepony.kstarbound.defs.TileRenderMatchPiece
import ru.dbotthepony.kstarbound.defs.TileRenderPiece import ru.dbotthepony.kstarbound.defs.TileRenderPiece
import ru.dbotthepony.kstarbound.gl.* import ru.dbotthepony.kstarbound.gl.*
import ru.dbotthepony.kstarbound.math.Vector2i import ru.dbotthepony.kstarbound.math.Vector2i
import ru.dbotthepony.kstarbound.util.Color
import ru.dbotthepony.kstarbound.world.ITileChunk import ru.dbotthepony.kstarbound.world.ITileChunk
import kotlin.collections.HashMap import kotlin.collections.HashMap
@ -52,7 +53,8 @@ class TileLayerList {
} }
class TileRenderers(val state: GLStateTracker) { class TileRenderers(val state: GLStateTracker) {
private val simpleBakedPrograms = HashMap<GLTexture2D, SimpleBakedProgram>() private val foregroundTilePrograms = HashMap<GLTexture2D, ForegroundTileProgram>()
private val backgroundTilePrograms = HashMap<GLTexture2D, BackgroundTileProgram>()
private val tileRenderers = HashMap<String, TileRenderer>() private val tileRenderers = HashMap<String, TileRenderer>()
operator fun get(tile: String): TileRenderer { 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() { override fun setup() {
super.setup() super.setup()
state.activeTexture = 0 state.activeTexture = 0
// state.depthTest = true state.depthTest = false
program["_texture"] = 0 program["_texture"] = 0
texture.bind() texture.bind()
texture.textureMagFilter = GL_NEAREST texture.textureMagFilter = GL_NEAREST
@ -77,7 +79,37 @@ class TileRenderers(val state: GLStateTracker) {
return true 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 return texture == other.texture
} }
@ -92,8 +124,19 @@ class TileRenderers(val state: GLStateTracker) {
/** /**
* Возвращает запечённое состояние shaderVertexTexture с данной текстурой * Возвращает запечённое состояние shaderVertexTexture с данной текстурой
*/ */
fun simpleProgram(texture: GLTexture2D): BakedProgramState { fun foreground(texture: GLTexture2D): BakedProgramState {
return simpleBakedPrograms.computeIfAbsent(texture, ::SimpleBakedProgram) 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 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 var notifiedDepth = false
private fun tesselateAt(piece: TileRenderPiece, getter: ITileChunk, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) { 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)) { if (matchPiece.test(getter, tile, pos)) {
for (renderPiece in matchPiece.pieces) { for (renderPiece in matchPiece.pieces) {
if (renderPiece.piece.texture != null) { 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) return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
}, pos, renderPiece.offset) }, pos, renderPiece.offset)
} else { } else {
@ -168,7 +220,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
} }
for (subPiece in matchPiece.subMatches) { 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) { if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) {
return TileRenderTesselateResult.HALT return TileRenderTesselateResult.HALT
@ -194,18 +246,18 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
* *
* Тесселирует тайлы в границы -1f .. CHUNK_SIZEf + 1f на основе [pos] * Тесселирует тайлы в границы -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 // если у нас нет renderTemplate
// то мы просто не можем его отрисовать // то мы просто не можем его отрисовать
tile.render.renderTemplate ?: return 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) return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
} }
for ((_, matcher) in tile.render.renderTemplate.matches) { for ((_, matcher) in tile.render.renderTemplate.matches) {
for (matchPiece in matcher.pieces) { 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) { if (matched == TileRenderTesselateResult.HALT) {
break break
@ -216,7 +268,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
companion object { companion object {
const val BASELINE_TEXTURE_SIZE = 8f const val BASELINE_TEXTURE_SIZE = 8f
const val Z_LEVEL = 1f const val Z_LEVEL = 10f
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
} }
} }

View File

@ -1,8 +1,9 @@
package ru.dbotthepony.kstarbound.util package ru.dbotthepony.kstarbound.util
import com.google.gson.JsonArray 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) 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 { companion object {

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.kstarbound.world package ru.dbotthepony.kstarbound.world
import ru.dbotthepony.kstarbound.defs.TileDefinition import ru.dbotthepony.kstarbound.defs.TileDefinition
import ru.dbotthepony.kstarbound.math.IVector2i
import ru.dbotthepony.kstarbound.math.Vector2i 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 = 1 shl CHUNK_SHIFT // 64
const val CHUNK_SIZE_FF = CHUNK_SIZE - 1 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<ChunkPos>() {
constructor(pos: Vector2i) : this(pos.x shr CHUNK_SHIFT, pos.y shr CHUNK_SHIFT) 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 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) 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) { open class Chunk(val world: World, val pos: ChunkPos) {
inner class ChunkTileLayer : IMutableTileChunk { inner class ChunkTileLayer : IMutableTileChunk {
override val pos: ChunkPos override val pos: ChunkPos

View File

@ -39,9 +39,86 @@ class World(val seed: Long = 0L) {
return chunk 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)) fun getChunk(pos: Vector2i) = getChunk(ChunkPos(pos))
/**
* Считается, что [pos] это абсолютные координаты ТАЙЛА в мире, поэтому они
* трансформируются в координаты чанка
*/
fun getOrMakeChunk(pos: Vector2i) = getOrMakeChunk(ChunkPos(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? { fun getTile(pos: Vector2i): ChunkTile? {
return getChunk(pos)?.foreground?.get(pos.x, pos.y) return getChunk(pos)?.foreground?.get(pos.x, pos.y)
} }

View File

@ -9,9 +9,5 @@ out vec4 _color_out;
void main() { void main() {
vec4 texel = texture(_texture, _uv_out); vec4 texel = texture(_texture, _uv_out);
//if (texel.a < 0.5)
// discard;
_color_out = texel; _color_out = texel;
} }

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}