Material modifier render test
This commit is contained in:
parent
bd5a6f3259
commit
122a951b56
@ -4,7 +4,6 @@ import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.Version
|
||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.io.*
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
||||
@ -76,7 +75,17 @@ fun main() {
|
||||
hitTile = true
|
||||
}
|
||||
|
||||
reader.skipBytes(5)
|
||||
reader.skipBytes(1) // Foreground hue shift
|
||||
reader.skipBytes(1) // Foreground color variant
|
||||
|
||||
val modifier = reader.readShort()
|
||||
val getModifier = Starbound.tileModifiersByIDAccess[modifier.toInt()]
|
||||
|
||||
if (getModifier != null && getMat != null) {
|
||||
chunk.foreground[x, y]?.modifier = getModifier
|
||||
}
|
||||
|
||||
reader.skipBytes(1) // Foreground mod hue shift
|
||||
|
||||
val materialID2 = reader.readShort()
|
||||
val getMat2 = Starbound.tilesAccessID[materialID2.toInt()]
|
||||
@ -86,7 +95,17 @@ fun main() {
|
||||
hitTile = true
|
||||
}
|
||||
|
||||
reader.skipBytes(22)
|
||||
reader.skipBytes(1) // Background hue shift
|
||||
reader.skipBytes(1) // Background color variant
|
||||
|
||||
val modifier2 = reader.readShort()
|
||||
val getModifier2 = Starbound.tileModifiersByIDAccess[modifier2.toInt()]
|
||||
|
||||
if (getModifier2 != null && getMat2 != null) {
|
||||
chunk.background[x, y]?.modifier = getModifier2
|
||||
}
|
||||
|
||||
reader.skipBytes(18)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,10 +53,16 @@ object Starbound : IVFS {
|
||||
|
||||
private val tiles = HashMap<String, TileDefinition>()
|
||||
private val tilesByMaterialID = Int2ObjectAVLTreeMap<TileDefinition>()
|
||||
|
||||
private val tileModifiers = HashMap<String, MaterialModifier>()
|
||||
private val tileModifiersByID = Int2ObjectAVLTreeMap<MaterialModifier>()
|
||||
|
||||
private val projectiles = HashMap<String, ConfiguredProjectile>()
|
||||
private val parallax = HashMap<String, ParallaxPrototype>()
|
||||
private val functions = HashMap<String, JsonFunction>()
|
||||
|
||||
val tileModifiersAccess: Map<String, MaterialModifier> = Collections.unmodifiableMap(tileModifiers)
|
||||
val tileModifiersByIDAccess: Map<Int, MaterialModifier> = Collections.unmodifiableMap(tileModifiersByID)
|
||||
val tilesAccess: Map<String, TileDefinition> = Collections.unmodifiableMap(tiles)
|
||||
val tilesAccessID: Map<Int, TileDefinition> = Collections.unmodifiableMap(tilesByMaterialID)
|
||||
val projectilesAccess: Map<String, ConfiguredProjectile> = Collections.unmodifiableMap(projectiles)
|
||||
@ -176,6 +182,7 @@ object Starbound : IVFS {
|
||||
loadStage(callback, this::loadTileMaterials, "materials")
|
||||
loadStage(callback, this::loadProjectiles, "projectiles")
|
||||
loadStage(callback, this::loadParallax, "parallax definitions")
|
||||
loadStage(callback, this::loadMaterialModifiers, "material modifier definitions")
|
||||
|
||||
initializing = false
|
||||
initialized = true
|
||||
@ -320,8 +327,27 @@ object Starbound : IVFS {
|
||||
}
|
||||
|
||||
private fun loadMaterialModifiers(callback: (String) -> Unit) {
|
||||
for (fs in fileSystems) {
|
||||
readingFolder = "/tiles/materials"
|
||||
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.listAllFilesWithExtension("matmod")) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = getPathFolder(listedFile)
|
||||
val tileDef = gson.fromJson(getReader(listedFile), MaterialModifier::class.java)
|
||||
|
||||
check(tileModifiers[tileDef.modName] == null) { "Already has material with name ${tileDef.modName} loaded!" }
|
||||
check(tileModifiersByID[tileDef.modId] == null) { "Already has material with ID ${tileDef.modId} loaded!" }
|
||||
tileModifiersByID[tileDef.modId] = tileDef
|
||||
tileModifiers[tileDef.modName] = tileDef
|
||||
} catch (err: Throwable) {
|
||||
//throw TileDefLoadingException("Loading tile file $listedFile", err)
|
||||
LOGGER.error("Loading tile modifier file $listedFile", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import java.io.Closeable
|
||||
import java.util.LinkedList
|
||||
|
||||
/**
|
||||
* Псевдо zPos у фоновых тайлов
|
||||
@ -24,7 +25,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
|
||||
private inner class TileLayerRenderer(private val layerChangeset: () -> Int, private val isBackground: Boolean) : AutoCloseable {
|
||||
private val layers = TileLayerList()
|
||||
val bakedMeshes = ArrayList<Pair<BakedStaticMesh, Int>>()
|
||||
val bakedMeshes = LinkedList<Pair<BakedStaticMesh, Int>>()
|
||||
private var changeset = -1
|
||||
|
||||
fun bake(view: ITileChunk) {
|
||||
@ -46,8 +47,13 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
|
||||
for ((pos, tile) in view.posToTile) {
|
||||
if (tile != null) {
|
||||
val renderer = state.tileRenderers.get(tile.def.materialName)
|
||||
renderer.tesselate(view, layers, pos, background = isBackground)
|
||||
state.tileRenderers.getTileRenderer(tile.def.materialName).tesselate(view, layers, pos, background = isBackground)
|
||||
|
||||
val modifier = tile.modifier
|
||||
|
||||
if (modifier != null) {
|
||||
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(view, layers, pos, background = isBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,13 +61,19 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
fun loadRenderers(view: ITileChunk) {
|
||||
for ((_, tile) in view.posToTile) {
|
||||
if (tile != null) {
|
||||
state.tileRenderers.get(tile.def.materialName)
|
||||
state.tileRenderers.getTileRenderer(tile.def.materialName)
|
||||
|
||||
val modifier = tile.modifier
|
||||
|
||||
if (modifier != null) {
|
||||
state.tileRenderers.getModifierRenderer(modifier.modName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadStatic(clear: Boolean = true) {
|
||||
for ((baked, builder, zLevel) in layers.buildList()) {
|
||||
for ((baked, builder, zLevel) in layers.buildSortedLayerList()) {
|
||||
bakedMeshes.add(BakedStaticMesh(baked, builder) to zLevel)
|
||||
}
|
||||
|
||||
@ -93,7 +105,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
|
||||
bakedMeshes.clear()
|
||||
|
||||
for ((baked, builder, zLevel) in layers.buildList()) {
|
||||
for ((baked, builder, zLevel) in layers.buildSortedLayerList()) {
|
||||
bakedMeshes.add(BakedStaticMesh(baked, builder) to zLevel)
|
||||
}
|
||||
|
||||
@ -205,22 +217,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
foregroundRenderer.autoUpload()
|
||||
}
|
||||
|
||||
fun renderDebug() {
|
||||
if (debugCollisions) {
|
||||
state.quadWireframe {
|
||||
it.quad(aabb.mins.x.toFloat(), aabb.mins.y.toFloat(), aabb.maxs.x.toFloat(), aabb.maxs.y.toFloat())
|
||||
|
||||
for (layer in foreground.collisionLayers()) {
|
||||
it.quad(layer.mins.x.toFloat(), layer.mins.y.toFloat(), layer.maxs.x.toFloat(), layer.maxs.y.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (renderer in entityRenderers.values) {
|
||||
renderer.renderDebug()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Хранит состояние отрисовки этого чанка
|
||||
*
|
||||
|
@ -18,7 +18,7 @@ import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
* Ожидается, что состояние будет выставлено ПОЛНОСТЬЮ, т.е. НИКАКОЙ предыдущий код НЕ МОЖЕТ повлиять на результат выполнения
|
||||
* шейдерной программы, которая связанна с этим объектом (за исключением не вызова [setTransform] внешним кодом)
|
||||
*/
|
||||
open class BakedProgramState(
|
||||
open class ConfiguredShaderProgram(
|
||||
val program: GLShaderProgram,
|
||||
) {
|
||||
private val transformLocation = program["_transform"]
|
||||
@ -40,13 +40,13 @@ open class BakedProgramState(
|
||||
* с заданной матрицей трансформации
|
||||
*/
|
||||
class BakedStaticMesh(
|
||||
val programState: BakedProgramState,
|
||||
val programState: ConfiguredShaderProgram,
|
||||
val indexCount: Int,
|
||||
val vao: GLVertexArrayObject,
|
||||
) : AutoCloseable {
|
||||
private var onClose = {}
|
||||
|
||||
constructor(programState: BakedProgramState, builder: DynamicVertexBuilder) : this(
|
||||
constructor(programState: ConfiguredShaderProgram, builder: DynamicVertexBuilder) : this(
|
||||
programState,
|
||||
builder.indexCount,
|
||||
programState.program.state.newVAO(),
|
@ -1,45 +1,44 @@
|
||||
package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.client.gl.*
|
||||
import ru.dbotthepony.kstarbound.defs.tile.RenderMatch
|
||||
import ru.dbotthepony.kstarbound.defs.tile.RenderPiece
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.tile.*
|
||||
import ru.dbotthepony.kstarbound.world.ChunkTile
|
||||
import ru.dbotthepony.kstarbound.world.ITileChunk
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
data class TileLayer(
|
||||
val bakedProgramState: BakedProgramState,
|
||||
val bakedProgramState: ConfiguredShaderProgram,
|
||||
val vertexBuilder: DynamicVertexBuilder,
|
||||
val zPos: Int)
|
||||
val zPos: Int
|
||||
)
|
||||
|
||||
class TileLayerList {
|
||||
private val layers = HashMap<BakedProgramState, ArrayList<TileLayer>>()
|
||||
private val layers = HashMap<ConfiguredShaderProgram, Int2ObjectAVLTreeMap<TileLayer>>()
|
||||
|
||||
fun getLayer(programState: BakedProgramState, zLevel: Int, compute: () -> DynamicVertexBuilder): DynamicVertexBuilder {
|
||||
val list = layers.computeIfAbsent(programState) {ArrayList()}
|
||||
|
||||
for (layer in list) {
|
||||
if (layer.zPos == zLevel) {
|
||||
return layer.vertexBuilder
|
||||
}
|
||||
}
|
||||
|
||||
val computed = TileLayer(programState, compute.invoke(), zLevel)
|
||||
list.add(computed)
|
||||
return computed.vertexBuilder
|
||||
/**
|
||||
* Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel].
|
||||
*
|
||||
* Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute].
|
||||
*/
|
||||
fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> DynamicVertexBuilder): DynamicVertexBuilder {
|
||||
return layers.computeIfAbsent(program) { Int2ObjectAVLTreeMap() }.computeIfAbsent(zLevel, Int2ObjectFunction {
|
||||
return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel)
|
||||
}).vertexBuilder
|
||||
}
|
||||
|
||||
fun buildList(): List<TileLayer> {
|
||||
fun buildSortedLayerList(): List<TileLayer> {
|
||||
val list = ArrayList<TileLayer>()
|
||||
|
||||
for (getList in layers.values) {
|
||||
list.addAll(getList)
|
||||
list.addAll(getList.values)
|
||||
}
|
||||
|
||||
list.sortBy {
|
||||
@ -56,19 +55,32 @@ class TileLayerList {
|
||||
val isNotEmpty get() = layers.isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Хранит в себе программы для отрисовки определённых [TileDefinition]
|
||||
*
|
||||
* Создаётся единожды как потомок [GLStateTracker]
|
||||
*/
|
||||
class TileRenderers(val state: GLStateTracker) {
|
||||
private val foregroundTilePrograms = HashMap<GLTexture2D, ForegroundTileProgram>()
|
||||
private val backgroundTilePrograms = HashMap<GLTexture2D, BackgroundTileProgram>()
|
||||
private val tileRenderers = HashMap<String, TileRenderer>()
|
||||
private val tileRenderersCache = HashMap<String, TileRenderer>()
|
||||
private val modifierRenderersCache = HashMap<String, TileRenderer>()
|
||||
|
||||
operator fun get(tile: String): TileRenderer {
|
||||
return tileRenderers.computeIfAbsent(tile) {
|
||||
val def = Starbound.getTileDefinition(tile) // TODO: Пустой рендерер
|
||||
fun getTileRenderer(defName: String): TileRenderer {
|
||||
return tileRenderersCache.computeIfAbsent(defName) {
|
||||
val def = Starbound.tilesAccess[defName] // TODO: Пустой рендерер
|
||||
return@computeIfAbsent TileRenderer(state, def!!)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ForegroundTileProgram(private val texture: GLTexture2D) : BakedProgramState(state.shaderVertexTexture) {
|
||||
fun getModifierRenderer(defName: String): TileRenderer {
|
||||
return modifierRenderersCache.computeIfAbsent(defName) {
|
||||
val def = Starbound.tileModifiersAccess[defName] // TODO: Пустой рендерер
|
||||
return@computeIfAbsent TileRenderer(state, def!!)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTexture) {
|
||||
override fun setup() {
|
||||
super.setup()
|
||||
state.activeTexture = 0
|
||||
@ -96,7 +108,7 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
}
|
||||
}
|
||||
|
||||
private inner class BackgroundTileProgram(private val texture: GLTexture2D) : BakedProgramState(state.shaderVertexTextureColor) {
|
||||
private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureColor) {
|
||||
override fun setup() {
|
||||
super.setup()
|
||||
state.activeTexture = 0
|
||||
@ -127,16 +139,16 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает запечённое состояние shaderVertexTexture с данной текстурой
|
||||
* Возвращает запечённое состояние шейдера shaderVertexTexture с данной текстурой
|
||||
*/
|
||||
fun foreground(texture: GLTexture2D): BakedProgramState {
|
||||
fun foreground(texture: GLTexture2D): ConfiguredShaderProgram {
|
||||
return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram)
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает запечённое состояние shaderVertexTextureColor с данной текстурой
|
||||
* Возвращает запечённое состояние шейдера shaderVertexTextureColor с данной текстурой
|
||||
*/
|
||||
fun background(texture: GLTexture2D): BakedProgramState {
|
||||
fun background(texture: GLTexture2D): ConfiguredShaderProgram {
|
||||
return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram)
|
||||
}
|
||||
|
||||
@ -151,11 +163,30 @@ private enum class TileRenderTesselateResult {
|
||||
HALT
|
||||
}
|
||||
|
||||
class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
|
||||
val texture = state.loadNamedTexture(tile.renderParameters.absoluteTexturePath).also {
|
||||
private fun vertexTextureBuilder() = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
|
||||
|
||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||
override fun test(tile: ChunkTile?): Boolean {
|
||||
return tile?.def == definition
|
||||
}
|
||||
}
|
||||
|
||||
private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester {
|
||||
override fun test(tile: ChunkTile?): Boolean {
|
||||
return tile?.modifier == definition
|
||||
}
|
||||
}
|
||||
|
||||
class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
val texture = state.loadNamedTexture(def.renderParameters.absoluteTexturePath).also {
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
}
|
||||
|
||||
val equalityTester: EqualityRuleTester = when (def) {
|
||||
is TileDefinition -> TileEqualityTester(def)
|
||||
is MaterialModifier -> ModifierEqualityTester(def)
|
||||
}
|
||||
|
||||
val bakedProgramState = state.tileRenderers.foreground(texture)
|
||||
val bakedBackgroundProgramState = state.tileRenderers.background(texture)
|
||||
// private var notifiedDepth = false
|
||||
@ -181,7 +212,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
|
||||
d += offset.y / PIXELS_IN_STARBOUND_UNITf
|
||||
}
|
||||
|
||||
if (tile.renderParameters.variants == 0 || piece.texture != null || piece.variantStride == null) {
|
||||
if (def.renderParameters.variants == 0 || piece.texture != null || piece.variantStride == null) {
|
||||
val (u0, v0) = texture.pixelToUV(piece.texturePosition)
|
||||
val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize)
|
||||
|
||||
@ -192,7 +223,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
|
||||
d,
|
||||
Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
|
||||
} else {
|
||||
val variant = (getter.randomDoubleFor(pos) * tile.renderParameters.variants).toInt()
|
||||
val variant = (getter.randomDoubleFor(pos) * def.renderParameters.variants).toInt()
|
||||
|
||||
val (u0, v0) = texture.pixelToUV(piece.texturePosition + piece.variantStride * variant)
|
||||
val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize + piece.variantStride * variant)
|
||||
@ -206,21 +237,24 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun tesselatePiece(matchPiece: RenderMatch, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: DynamicVertexBuilder, background: Boolean): TileRenderTesselateResult {
|
||||
if (matchPiece.test(getter, tile, pos)) {
|
||||
private fun tesselatePiece(
|
||||
matchPiece: RenderMatch,
|
||||
getter: ITileChunk,
|
||||
layers: TileLayerList,
|
||||
pos: Vector2i,
|
||||
thisBuilder: DynamicVertexBuilder,
|
||||
background: Boolean
|
||||
): TileRenderTesselateResult {
|
||||
if (matchPiece.test(getter, equalityTester, pos)) {
|
||||
for (renderPiece in matchPiece.pieces) {
|
||||
if (renderPiece.piece.texture != null) {
|
||||
val program: BakedProgramState
|
||||
|
||||
if (background) {
|
||||
program = state.tileRenderers.background(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
val program = if (background) {
|
||||
state.tileRenderers.background(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
} else {
|
||||
program = state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
}
|
||||
|
||||
tesselateAt(renderPiece.piece, getter, layers.getLayer(program, tile.renderParameters.zLevel) {
|
||||
return@getLayer DynamicVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
|
||||
}, pos, renderPiece.offset)
|
||||
tesselateAt(renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset)
|
||||
} else {
|
||||
tesselateAt(renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset)
|
||||
}
|
||||
@ -256,15 +290,13 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
|
||||
fun tesselate(getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false) {
|
||||
// если у нас нет renderTemplate
|
||||
// то мы просто не можем его отрисовать
|
||||
val template = tile.renderTemplate
|
||||
val template = def.renderTemplate
|
||||
|
||||
val builder = layers.getLayer(if (background) bakedBackgroundProgramState else bakedProgramState, tile.renderParameters.zLevel) {
|
||||
return@getLayer DynamicVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
|
||||
}
|
||||
val vertexBuilder = layers.computeIfAbsent(if (background) bakedBackgroundProgramState else bakedProgramState, def.renderParameters.zLevel, ::vertexTextureBuilder)
|
||||
|
||||
for ((_, matcher) in template.matches) {
|
||||
for (matchPiece in matcher) {
|
||||
val matched = tesselatePiece(matchPiece, getter, layers, pos, builder, background)
|
||||
val matched = tesselatePiece(matchPiece, getter, layers, pos, vertexBuilder, background)
|
||||
|
||||
if (matched == TileRenderTesselateResult.HALT) {
|
||||
break
|
||||
|
@ -0,0 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound.defs.tile
|
||||
|
||||
sealed interface IRenderableTile {
|
||||
val renderTemplate: RenderTemplate
|
||||
val renderParameters: RenderParameters
|
||||
}
|
@ -6,16 +6,16 @@ import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||
data class MaterialModifier(
|
||||
val modId: Int,
|
||||
val modName: String,
|
||||
val itemDrop: String,
|
||||
val description: String,
|
||||
val health: Int,
|
||||
val harvestLevel: Int,
|
||||
val breaksWithTile: Boolean,
|
||||
val itemDrop: String? = null,
|
||||
val description: String = "...",
|
||||
val health: Int = 0,
|
||||
val harvestLevel: Int = 0,
|
||||
val breaksWithTile: Boolean = true,
|
||||
val miningSounds: List<String> = listOf(),
|
||||
val miningParticle: String? = null,
|
||||
val renderTemplate: RenderTemplate,
|
||||
val renderParameters: RenderParameters
|
||||
) {
|
||||
override val renderTemplate: RenderTemplate,
|
||||
override val renderParameters: RenderParameters
|
||||
) : IRenderableTile {
|
||||
init {
|
||||
require(modId > 0) { "Invalid material modifier ID $modId" }
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.io.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.util.WriteOnce
|
||||
import ru.dbotthepony.kstarbound.world.ChunkTile
|
||||
import ru.dbotthepony.kstarbound.world.ITileGetter
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -37,6 +37,10 @@ data class RenderPiece(
|
||||
}
|
||||
}
|
||||
|
||||
fun interface EqualityRuleTester {
|
||||
fun test(tile: ChunkTile?): Boolean
|
||||
}
|
||||
|
||||
data class RenderRuleList(
|
||||
val entries: List<Entry>,
|
||||
val join: Combination = Combination.ALL
|
||||
@ -50,9 +54,9 @@ data class RenderRuleList(
|
||||
val matchHue: Boolean = false,
|
||||
val inverse: Boolean = false,
|
||||
) {
|
||||
private fun doTest(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
|
||||
private fun doTest(getter: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
|
||||
return when (type) {
|
||||
"EqualsSelf" -> getter[thisPos + offsetPos]?.def == thisRef
|
||||
"EqualsSelf" -> equalityTester.test(getter[thisPos + offsetPos])
|
||||
"Connects" -> getter[thisPos + offsetPos] != null
|
||||
|
||||
else -> {
|
||||
@ -65,12 +69,12 @@ data class RenderRuleList(
|
||||
}
|
||||
}
|
||||
|
||||
fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
|
||||
fun test(getter: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
|
||||
if (inverse) {
|
||||
return !doTest(getter, thisRef, thisPos, offsetPos)
|
||||
return !doTest(getter, equalityTester, thisPos, offsetPos)
|
||||
}
|
||||
|
||||
return doTest(getter, thisRef, thisPos, offsetPos)
|
||||
return doTest(getter, equalityTester, thisPos, offsetPos)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -87,11 +91,11 @@ data class RenderRuleList(
|
||||
}
|
||||
}
|
||||
|
||||
fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offset: Vector2i): Boolean {
|
||||
fun test(getter: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i, offset: Vector2i): Boolean {
|
||||
when (join) {
|
||||
Combination.ALL -> {
|
||||
for (entry in entries) {
|
||||
if (!entry.test(getter, thisRef, thisPos, offset)) {
|
||||
if (!entry.test(getter, equalityTester, thisPos, offset)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -101,7 +105,7 @@ data class RenderRuleList(
|
||||
|
||||
Combination.ANY -> {
|
||||
for (entry in entries) {
|
||||
if (entry.test(getter, thisRef, thisPos, offset)) {
|
||||
if (entry.test(getter, equalityTester, thisPos, offset)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -144,8 +148,8 @@ data class RenderMatch(
|
||||
) {
|
||||
var rule by WriteOnce<RenderRuleList>()
|
||||
|
||||
fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i): Boolean {
|
||||
return rule.test(getter, thisRef, thisPos, offset)
|
||||
fun test(getter: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i): Boolean {
|
||||
return rule.test(getter, equalityTester, thisPos, offset)
|
||||
}
|
||||
|
||||
fun resolve(template: RenderTemplate) {
|
||||
@ -177,10 +181,12 @@ data class RenderMatch(
|
||||
* Если хотя бы один из них вернул false, весь тест возвращает false
|
||||
*
|
||||
* [subMatches] стоит итерировать только если это вернуло true
|
||||
*
|
||||
* [equalityTester] требуется для проверки раенства между "этим" тайлом и другим
|
||||
*/
|
||||
fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i): Boolean {
|
||||
fun test(tileAccess: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i): Boolean {
|
||||
for (matcher in matchAllPoints) {
|
||||
if (!matcher.test(getter, thisRef, thisPos)) {
|
||||
if (!matcher.test(tileAccess, equalityTester, thisPos)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -190,7 +196,7 @@ data class RenderMatch(
|
||||
}
|
||||
|
||||
for (matcher in matchAnyPoints) {
|
||||
if (matcher.test(getter, thisRef, thisPos)) {
|
||||
if (matcher.test(tileAccess, equalityTester, thisPos)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ data class TileDefinition(
|
||||
val health: Double = 0.0,
|
||||
val category: String,
|
||||
|
||||
val renderTemplate: RenderTemplate,
|
||||
val renderParameters: RenderParameters,
|
||||
) {
|
||||
override val renderTemplate: RenderTemplate,
|
||||
override val renderParameters: RenderParameters,
|
||||
) : IRenderableTile {
|
||||
companion object {
|
||||
val ADAPTER = KConcreteTypeAdapter.Builder(TileDefinition::class)
|
||||
.plain(
|
||||
|
@ -4,6 +4,7 @@ import ru.dbotthepony.kbox2d.api.BodyDef
|
||||
import ru.dbotthepony.kbox2d.api.FixtureDef
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.B2Fixture
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderDepthFirst
|
||||
@ -17,18 +18,47 @@ import kotlin.collections.HashSet
|
||||
/**
|
||||
* Представляет из себя класс, который содержит состояние тайла на заданной позиции
|
||||
*/
|
||||
data class ChunkTile(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
|
||||
class ChunkTile(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
|
||||
var color = 0
|
||||
set(value) {
|
||||
field = value
|
||||
chunk.incChangeset()
|
||||
if (value != field) {
|
||||
field = value
|
||||
chunk.incChangeset()
|
||||
}
|
||||
}
|
||||
|
||||
var forceVariant = -1
|
||||
var variant = -1
|
||||
set(value) {
|
||||
field = value
|
||||
chunk.incChangeset()
|
||||
if (value != field) {
|
||||
field = value
|
||||
chunk.incChangeset()
|
||||
}
|
||||
}
|
||||
|
||||
var modifier: MaterialModifier? = null
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
field = value
|
||||
chunk.incChangeset()
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is ChunkTile && other.color == color && other.variant == variant && other.modifier === modifier && other.def === def
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ChunkTile[$chunk, material = ${def.materialName}, color = $color, variant = $variant, modifier = ${modifier?.modName}]"
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = chunk.hashCode()
|
||||
result = 31 * result + def.hashCode()
|
||||
result = 31 * result + color
|
||||
result = 31 * result + variant
|
||||
result = 31 * result + (modifier?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double {
|
||||
|
Loading…
Reference in New Issue
Block a user