more shader stuff, hue shift, color variants, liquid def loader test
This commit is contained in:
parent
19cf788d87
commit
48cf205506
8
src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt
Normal file
8
src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.TypeAdapter
|
||||
|
||||
inline fun <reified T> GsonBuilder.registerTypeAdapter(adapter: TypeAdapter<T>): GsonBuilder {
|
||||
return registerTypeAdapter(T::class.java, adapter)
|
||||
}
|
@ -12,6 +12,7 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.util.zip.Inflater
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
@ -75,12 +76,17 @@ fun main() {
|
||||
hitTile = true
|
||||
}
|
||||
|
||||
reader.skipBytes(1) // Foreground hue shift
|
||||
reader.skipBytes(1) // Foreground color variant
|
||||
// reader.skipBytes(1) // Foreground hue shift
|
||||
// reader.skipBytes(1) // Foreground color variant
|
||||
val colorShift = reader.readUnsignedByte()
|
||||
val colorVariant = reader.readUnsignedByte()
|
||||
|
||||
val modifier = reader.readUnsignedShort()
|
||||
val getModifier = Starbound.tileModifiersByIDAccess[modifier]
|
||||
|
||||
chunk.foreground[x, y]?.color = colorVariant
|
||||
chunk.foreground[x, y]?.setHueShift(colorShift)
|
||||
|
||||
if (getModifier != null && getMat != null) {
|
||||
chunk.foreground[x, y]?.modifier = getModifier
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import ru.dbotthepony.kstarbound.api.PhysicalFS
|
||||
import ru.dbotthepony.kstarbound.api.getPathFilename
|
||||
import ru.dbotthepony.kstarbound.api.getPathFolder
|
||||
import ru.dbotthepony.kstarbound.defs.*
|
||||
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.*
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.RenderParameters
|
||||
@ -57,10 +58,15 @@ object Starbound : IVFS {
|
||||
private val tileModifiers = HashMap<String, MaterialModifier>()
|
||||
private val tileModifiersByID = Int2ObjectAVLTreeMap<MaterialModifier>()
|
||||
|
||||
private val liquid = HashMap<String, LiquidDefinition>()
|
||||
private val liquidByID = Int2ObjectAVLTreeMap<LiquidDefinition>()
|
||||
|
||||
private val projectiles = HashMap<String, ConfiguredProjectile>()
|
||||
private val parallax = HashMap<String, ParallaxPrototype>()
|
||||
private val functions = HashMap<String, JsonFunction>()
|
||||
|
||||
val liquidAccess: Map<String, LiquidDefinition> = Collections.unmodifiableMap(liquid)
|
||||
val liquidByIDAccess: Map<Int, LiquidDefinition> = Collections.unmodifiableMap(liquidByID)
|
||||
val tileModifiersAccess: Map<String, MaterialModifier> = Collections.unmodifiableMap(tileModifiers)
|
||||
val tileModifiersByIDAccess: Map<Int, MaterialModifier> = Collections.unmodifiableMap(tileModifiersByID)
|
||||
val tilesAccess: Map<String, TileDefinition> = Collections.unmodifiableMap(tiles)
|
||||
@ -94,6 +100,7 @@ object Starbound : IVFS {
|
||||
.also(RenderParameters::registerGson)
|
||||
.also(RenderTemplate::registerGson)
|
||||
.also(TileDefinition::registerGson)
|
||||
.also(LiquidDefinition::registerGson)
|
||||
|
||||
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
||||
|
||||
@ -183,6 +190,7 @@ object Starbound : IVFS {
|
||||
loadStage(callback, this::loadProjectiles, "projectiles")
|
||||
loadStage(callback, this::loadParallax, "parallax definitions")
|
||||
loadStage(callback, this::loadMaterialModifiers, "material modifier definitions")
|
||||
loadStage(callback, this::loadLiquidDefinitions, "liquid definitions")
|
||||
|
||||
initializing = false
|
||||
initialized = true
|
||||
@ -350,4 +358,25 @@ object Starbound : IVFS {
|
||||
|
||||
readingFolder = null
|
||||
}
|
||||
|
||||
private fun loadLiquidDefinitions(callback: (String) -> Unit) {
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.listAllFilesWithExtension("liquid")) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = getPathFolder(listedFile)
|
||||
val liquidDef = gson.fromJson(getReader(listedFile), LiquidDefinition::class.java)
|
||||
|
||||
check(liquid.put(liquidDef.name, liquidDef) == null) { "Already has liquid with name ${liquidDef.name} loaded!" }
|
||||
check(liquidByID.put(liquidDef.liquidId, liquidDef) == null) { "Already has liquid with ID ${liquidDef.liquidId} loaded!" }
|
||||
} catch (err: Throwable) {
|
||||
//throw TileDefLoadingException("Loading tile file $listedFile", err)
|
||||
LOGGER.error("Loading liquid definition file $listedFile", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
}
|
||||
}
|
||||
|
@ -47,12 +47,12 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
|
||||
for ((pos, tile) in view.posToTile) {
|
||||
if (tile != null) {
|
||||
state.tileRenderers.getTileRenderer(tile.def.materialName).tesselate(view, layers, pos, background = isBackground)
|
||||
state.tileRenderers.getTileRenderer(tile.def.materialName).tesselate(tile, view, layers, pos, background = isBackground)
|
||||
|
||||
val modifier = tile.modifier
|
||||
|
||||
if (modifier != null) {
|
||||
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(view, layers, pos, background = isBackground)
|
||||
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, pos, background = isBackground, isModifier = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ enum class GLType(val identity: Int, val typeIndentity: Int, val byteSize: Int,
|
||||
}
|
||||
|
||||
interface IGLAttributeList {
|
||||
/**
|
||||
* Применяет список атрибутов к заданному [GLVertexArrayObject] (попутно включая или отключая их через [enable])
|
||||
*/
|
||||
fun apply(target: GLVertexArrayObject, enable: Boolean = false)
|
||||
}
|
||||
|
||||
@ -85,10 +88,15 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
|
||||
}
|
||||
|
||||
companion object {
|
||||
val VEC2F = GLFlatAttributeListBuilder().also {it.push(GLType.VEC2F)}.build()
|
||||
val VEC3F = GLFlatAttributeListBuilder().also {it.push(GLType.VEC3F)}.build()
|
||||
val VERTEX_TEXTURE = GLFlatAttributeListBuilder().also {it.push(GLType.VEC3F).push(GLType.VEC2F)}.build()
|
||||
val VERTEX_2D_TEXTURE = GLFlatAttributeListBuilder().also {it.push(GLType.VEC2F).push(GLType.VEC2F)}.build()
|
||||
val VEC2F = GLFlatAttributeListBuilder().push(GLType.VEC2F).build()
|
||||
val VEC3F = GLFlatAttributeListBuilder().push(GLType.VEC3F).build()
|
||||
|
||||
val VERTEX_TEXTURE = GLFlatAttributeListBuilder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
||||
|
||||
val VERTEX_HSV_TEXTURE = GLFlatAttributeListBuilder()
|
||||
.push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
||||
|
||||
val VERTEX_2D_TEXTURE = GLFlatAttributeListBuilder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,6 +117,11 @@ class GLFlatAttributeListBuilder : IGLAttributeList {
|
||||
return push("$type#${attributes.size}", type)
|
||||
}
|
||||
|
||||
fun push(vararg types: GLType): GLFlatAttributeListBuilder {
|
||||
for (type in types) push(type)
|
||||
return this
|
||||
}
|
||||
|
||||
fun push(name: String, type: GLType): GLFlatAttributeListBuilder {
|
||||
check(!findName(name)) { "Already has named attribute $name!" }
|
||||
attributes.add(name to type)
|
||||
@ -117,7 +130,7 @@ class GLFlatAttributeListBuilder : IGLAttributeList {
|
||||
|
||||
fun build() = GLFlatAttributeList(this)
|
||||
|
||||
@Deprecated("Используй build()")
|
||||
@Deprecated("Используй build()", replaceWith = ReplaceWith("build()"))
|
||||
override fun apply(target: GLVertexArrayObject, enable: Boolean) {
|
||||
var offset = 0L
|
||||
var stride = 0
|
||||
@ -136,9 +149,4 @@ class GLFlatAttributeListBuilder : IGLAttributeList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val VEC3F = GLFlatAttributeList.VEC3F
|
||||
val VERTEX_TEXTURE = GLFlatAttributeList.VERTEX_TEXTURE
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ class GLStateTracker {
|
||||
checkForGLError()
|
||||
}
|
||||
|
||||
val thread = Thread.currentThread()
|
||||
val thread: Thread = Thread.currentThread()
|
||||
val tileRenderers = TileRenderers(this)
|
||||
|
||||
fun ensureSameThread() {
|
||||
@ -356,18 +356,25 @@ class GLStateTracker {
|
||||
|
||||
val shaderVertexTexture: GLTransformableProgram
|
||||
val shaderVertexTextureColor: GLTransformableColorableProgram
|
||||
val shaderVertexTextureHSVColor: GLTransformableColorableProgram
|
||||
|
||||
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")
|
||||
|
||||
val textureFragmentHSV = GLShader.internalFragment("shaders/fragment/texture_color_per_vertex.glsl")
|
||||
val textureVertexHSV = GLShader.internalVertex("shaders/vertex/texture_hsv.glsl")
|
||||
|
||||
shaderVertexTexture = GLTransformableProgram(this, textureF, textureV)
|
||||
shaderVertexTextureColor = GLTransformableColorableProgram(this, textureColorF, textureV)
|
||||
shaderVertexTextureHSVColor = GLTransformableColorableProgram(this, textureFragmentHSV, textureVertexHSV)
|
||||
|
||||
textureF.unlink()
|
||||
textureColorF.unlink()
|
||||
textureV.unlink()
|
||||
textureFragmentHSV.unlink()
|
||||
textureVertexHSV.unlink()
|
||||
}
|
||||
|
||||
val fontProgram: GLTransformableColorableProgram
|
||||
|
@ -125,6 +125,7 @@ interface IVertex<This : IVertex<This, VertexBuilderType>, VertexBuilderType> {
|
||||
fun expect(type: GLType): This
|
||||
fun pushVec3f(x: Float, y: Float, z: Float): This
|
||||
fun pushVec2f(x: Float, y: Float): This
|
||||
fun push(value: Float): This
|
||||
fun end(): VertexBuilderType
|
||||
}
|
||||
|
||||
@ -132,6 +133,20 @@ typealias VertexTransformer = (IVertex<*, *>, Int) -> IVertex<*, *>
|
||||
private val emptyTransform: VertexTransformer = { it, _ -> it }
|
||||
private val EMPTY_BUFFER = ByteBuffer.allocateDirect(0)
|
||||
|
||||
fun VertexTransformer.before(other: VertexTransformer): VertexTransformer {
|
||||
return { a, b ->
|
||||
other.invoke(a, b)
|
||||
this.invoke(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
fun VertexTransformer.after(other: VertexTransformer): VertexTransformer {
|
||||
return { a, b ->
|
||||
this.invoke(a, b)
|
||||
other.invoke(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
object VertexTransformers {
|
||||
fun uv(u0: Float,
|
||||
v0: Float,
|
||||
@ -279,6 +294,7 @@ class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val typ
|
||||
is IntArray -> for (i in element) bytes.putInt(i)
|
||||
is ByteArray -> for (i in element) bytes.put(i)
|
||||
is DoubleArray -> for (i in element) bytes.putDouble(i)
|
||||
is Float -> bytes.putFloat(element)
|
||||
else -> throw IllegalStateException("Unknown element $element")
|
||||
}
|
||||
}
|
||||
@ -291,6 +307,7 @@ class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val typ
|
||||
is IntArray -> it.joinToString(", ")
|
||||
is ByteArray -> it.joinToString(", ")
|
||||
is DoubleArray -> it.joinToString(", ")
|
||||
is Float -> it
|
||||
else -> "null"
|
||||
} }.joinToString("; ")})"
|
||||
}
|
||||
@ -331,6 +348,12 @@ class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val typ
|
||||
return this
|
||||
}
|
||||
|
||||
override fun push(value: Float): Vertex {
|
||||
expect(GLType.FLOAT)
|
||||
store[index++] = value
|
||||
return this
|
||||
}
|
||||
|
||||
override fun checkValid() {
|
||||
for (elem in store.indices) {
|
||||
if (store[elem] == null) {
|
||||
@ -521,6 +544,15 @@ class StreamVertexBuilder(
|
||||
return this
|
||||
}
|
||||
|
||||
override fun push(value: Float): Vertex {
|
||||
expect(GLType.FLOAT)
|
||||
vertexBuffer.position(bufferPosition)
|
||||
vertexBuffer.putFloat(value)
|
||||
index++
|
||||
bufferPosition += 4
|
||||
return this
|
||||
}
|
||||
|
||||
override fun end(): StreamVertexBuilder {
|
||||
check(index == attributes.size) { "Vertex $vertexIndex is not fully filled (only $index attributes provided, ${attributes.size} required)" }
|
||||
return this@StreamVertexBuilder
|
||||
|
@ -8,7 +8,7 @@ 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.*
|
||||
import ru.dbotthepony.kstarbound.world.ChunkTile
|
||||
import ru.dbotthepony.kstarbound.world.TileState
|
||||
import ru.dbotthepony.kstarbound.world.ITileChunk
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
@ -80,7 +80,7 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTexture) {
|
||||
private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureHSVColor) {
|
||||
override fun setup() {
|
||||
super.setup()
|
||||
state.activeTexture = 0
|
||||
@ -89,6 +89,8 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
texture.bind()
|
||||
texture.textureMagFilter = GL_NEAREST
|
||||
texture.textureMinFilter = GL_NEAREST
|
||||
|
||||
program["_color"] = FOREGROUND_COLOR
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@ -108,7 +110,7 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
}
|
||||
}
|
||||
|
||||
private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureColor) {
|
||||
private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.shaderVertexTextureHSVColor) {
|
||||
override fun setup() {
|
||||
super.setup()
|
||||
state.activeTexture = 0
|
||||
@ -154,6 +156,7 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
|
||||
companion object {
|
||||
val BACKGROUND_COLOR = Color(0.4f, 0.4f, 0.4f)
|
||||
val FOREGROUND_COLOR = Color(1f, 1f, 1f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,16 +166,16 @@ private enum class TileRenderTesselateResult {
|
||||
HALT
|
||||
}
|
||||
|
||||
private fun vertexTextureBuilder() = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
|
||||
private fun vertexTextureBuilder() = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_HSV_TEXTURE, VertexType.QUADS)
|
||||
|
||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||
override fun test(tile: ChunkTile?): Boolean {
|
||||
override fun test(tile: TileState?): Boolean {
|
||||
return tile?.def == definition
|
||||
}
|
||||
}
|
||||
|
||||
private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester {
|
||||
override fun test(tile: ChunkTile?): Boolean {
|
||||
override fun test(tile: TileState?): Boolean {
|
||||
return tile?.modifier == definition
|
||||
}
|
||||
}
|
||||
@ -191,7 +194,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
val bakedBackgroundProgramState = state.tileRenderers.background(texture)
|
||||
// private var notifiedDepth = false
|
||||
|
||||
private fun tesselateAt(piece: RenderPiece, getter: ITileChunk, builder: DynamicVertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) {
|
||||
private fun tesselateAt(self: TileState, piece: RenderPiece, getter: ITileChunk, builder: DynamicVertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
|
||||
val fx = pos.x.toFloat()
|
||||
val fy = pos.y.toFloat()
|
||||
|
||||
@ -212,38 +215,35 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
d += offset.y / PIXELS_IN_STARBOUND_UNITf
|
||||
}
|
||||
|
||||
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)
|
||||
var mins = piece.texturePosition
|
||||
var maxs = piece.texturePosition + piece.textureSize
|
||||
|
||||
builder.quadZ(
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
|
||||
} else {
|
||||
if (def.renderParameters.variants != 0 && piece.variantStride != null && piece.texture == null) {
|
||||
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)
|
||||
|
||||
builder.quadZ(
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
|
||||
mins += piece.variantStride * variant
|
||||
maxs += piece.variantStride * variant
|
||||
}
|
||||
|
||||
if (def.renderParameters.multiColored && piece.colorStride != null && self.color != 0) {
|
||||
mins += piece.colorStride * self.color
|
||||
maxs += piece.colorStride * self.color
|
||||
}
|
||||
|
||||
val (u0, v0) = texture.pixelToUV(mins)
|
||||
val (u1, v1) = texture.pixelToUV(maxs)
|
||||
|
||||
builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0).after { it, _ -> it.push(if (!isModifier || self.modifier?.grass == true) self.hueShift else 0f) })
|
||||
}
|
||||
|
||||
private fun tesselatePiece(
|
||||
self: TileState,
|
||||
matchPiece: RenderMatch,
|
||||
getter: ITileChunk,
|
||||
layers: TileLayerList,
|
||||
pos: Vector2i,
|
||||
thisBuilder: DynamicVertexBuilder,
|
||||
background: Boolean
|
||||
background: Boolean,
|
||||
isModifier: Boolean,
|
||||
): TileRenderTesselateResult {
|
||||
if (matchPiece.test(getter, equalityTester, pos)) {
|
||||
for (renderPiece in matchPiece.pieces) {
|
||||
@ -254,14 +254,14 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
}
|
||||
|
||||
tesselateAt(renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset)
|
||||
tesselateAt(self, renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset, isModifier)
|
||||
} else {
|
||||
tesselateAt(renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset)
|
||||
tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier)
|
||||
}
|
||||
}
|
||||
|
||||
for (subPiece in matchPiece.subMatches) {
|
||||
val matched = tesselatePiece(subPiece, getter, layers, pos, thisBuilder, background)
|
||||
val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier)
|
||||
|
||||
if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
||||
return TileRenderTesselateResult.HALT
|
||||
@ -287,7 +287,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
*
|
||||
* Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf]
|
||||
*/
|
||||
fun tesselate(getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false) {
|
||||
fun tesselate(self: TileState, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) {
|
||||
// если у нас нет renderTemplate
|
||||
// то мы просто не можем его отрисовать
|
||||
val template = def.renderTemplate
|
||||
@ -296,7 +296,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
|
||||
for ((_, matcher) in template.matches) {
|
||||
for (matchPiece in matcher) {
|
||||
val matched = tesselatePiece(matchPiece, getter, layers, pos, vertexBuilder, background)
|
||||
val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier)
|
||||
|
||||
if (matched == TileRenderTesselateResult.HALT) {
|
||||
break
|
||||
|
@ -0,0 +1,53 @@
|
||||
package ru.dbotthepony.kstarbound.defs.liquid
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
|
||||
data class LiquidDefinition(
|
||||
val name: String,
|
||||
val liquidId: Int,
|
||||
val description: String = "...",
|
||||
val tickDelta: Int = 1,
|
||||
val color: Color,
|
||||
val itemDrop: String? = null,
|
||||
val statusEffects: List<String> = listOf(),
|
||||
val interactions: List<Interaction> = listOf(),
|
||||
val texture: String,
|
||||
val bottomLightMix: Color,
|
||||
val textureMovementFactor: Double,
|
||||
) {
|
||||
data class Interaction(val liquid: Int, val liquidResult: Int? = null, val materialResult: String? = null) {
|
||||
init {
|
||||
require(liquidResult != null || materialResult != null) { "Both liquidResult and materialResult are missing" }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = KConcreteTypeAdapter.Builder(LiquidDefinition::class)
|
||||
.plain(LiquidDefinition::name)
|
||||
.plain(LiquidDefinition::liquidId)
|
||||
.plain(LiquidDefinition::description)
|
||||
.plain(LiquidDefinition::tickDelta)
|
||||
.plain(LiquidDefinition::color)
|
||||
.plain(LiquidDefinition::itemDrop)
|
||||
.list(LiquidDefinition::statusEffects)
|
||||
.list(LiquidDefinition::interactions)
|
||||
.plain(LiquidDefinition::texture)
|
||||
.plain(LiquidDefinition::bottomLightMix)
|
||||
.plain(LiquidDefinition::textureMovementFactor)
|
||||
.build()
|
||||
|
||||
val INTERACTION_ADAPTER = KConcreteTypeAdapter.Builder(Interaction::class)
|
||||
.plain(Interaction::liquid)
|
||||
.plain(Interaction::liquidResult)
|
||||
.plain(Interaction::materialResult)
|
||||
.build()
|
||||
|
||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(INTERACTION_ADAPTER)
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ data class MaterialModifier(
|
||||
val health: Int = 0,
|
||||
val harvestLevel: Int = 0,
|
||||
val breaksWithTile: Boolean = true,
|
||||
val grass: Boolean = false,
|
||||
val miningSounds: List<String> = listOf(),
|
||||
val miningParticle: String? = null,
|
||||
override val renderTemplate: RenderTemplate,
|
||||
@ -29,6 +30,7 @@ data class MaterialModifier(
|
||||
.plain(MaterialModifier::health)
|
||||
.plain(MaterialModifier::harvestLevel)
|
||||
.plain(MaterialModifier::breaksWithTile)
|
||||
.plain(MaterialModifier::grass)
|
||||
.list(MaterialModifier::miningSounds)
|
||||
.plain(MaterialModifier::miningParticle)
|
||||
.plain(MaterialModifier::renderTemplate, RenderTemplate.CACHE)
|
||||
|
@ -4,6 +4,8 @@ import com.google.gson.GsonBuilder
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||
|
||||
const val TILE_COLOR_VARIANTS = 9
|
||||
|
||||
data class RenderParameters(
|
||||
val texture: String,
|
||||
val variants: Int = 0,
|
||||
|
@ -12,7 +12,7 @@ import ru.dbotthepony.kstarbound.Starbound
|
||||
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.TileState
|
||||
import ru.dbotthepony.kstarbound.world.ITileGetter
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -38,7 +38,7 @@ data class RenderPiece(
|
||||
}
|
||||
|
||||
fun interface EqualityRuleTester {
|
||||
fun test(tile: ChunkTile?): Boolean
|
||||
fun test(tile: TileState?): Boolean
|
||||
}
|
||||
|
||||
data class RenderRuleList(
|
||||
|
@ -279,7 +279,7 @@ class KConcreteTypeAdapter<T : Any>(
|
||||
val name = fieldId.toString()
|
||||
|
||||
if (loggedMisses.add(name)) {
|
||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field")
|
||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
|
||||
}
|
||||
|
||||
reader.skipValue()
|
||||
@ -306,7 +306,7 @@ class KConcreteTypeAdapter<T : Any>(
|
||||
|
||||
if (fieldId == -1) {
|
||||
if (loggedMisses.add(name)) {
|
||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field")
|
||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
|
||||
}
|
||||
|
||||
reader.skipValue()
|
||||
|
@ -0,0 +1,35 @@
|
||||
package ru.dbotthepony.kstarbound.util
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, private val height: Int) {
|
||||
private val memory: Array<T?> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T?>
|
||||
|
||||
operator fun get(x: Int, y: Int): T? {
|
||||
if (x !in 0 until width) {
|
||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
||||
}
|
||||
|
||||
if (y !in 0 until height) {
|
||||
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
|
||||
}
|
||||
|
||||
return memory[x + y * width]
|
||||
}
|
||||
|
||||
operator fun set(x: Int, y: Int, value: T): T? {
|
||||
if (x !in 0 until width) {
|
||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
||||
}
|
||||
|
||||
if (y !in 0 until height) {
|
||||
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
|
||||
}
|
||||
|
||||
val old = memory[x + y * width]
|
||||
memory[x + y * width] = value
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> TwoDimensionalArray(width: Int, height: Int) = TwoDimensionalArray(T::class, width, height)
|
@ -4,8 +4,11 @@ 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.liquid.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TILE_COLOR_VARIANTS
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.util.TwoDimensionalArray
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderDepthFirst
|
||||
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderSizeFirst
|
||||
@ -18,19 +21,46 @@ import kotlin.collections.HashSet
|
||||
/**
|
||||
* Представляет из себя класс, который содержит состояние тайла на заданной позиции
|
||||
*/
|
||||
class ChunkTile(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
|
||||
class TileState(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
|
||||
var color = 0
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
if (!def.renderParameters.multiColored) {
|
||||
throw IllegalStateException("${def.materialName} can't be colored")
|
||||
}
|
||||
|
||||
if (value !in 0 until TILE_COLOR_VARIANTS) {
|
||||
throw IndexOutOfBoundsException("Tile variant $value is out of possible range 0 to $TILE_COLOR_VARIANTS")
|
||||
}
|
||||
|
||||
field = value
|
||||
chunk.incChangeset()
|
||||
}
|
||||
}
|
||||
|
||||
var variant = -1
|
||||
/**
|
||||
* Выставляет hue shift как байтовое значение в диапазоне 0 .. 255
|
||||
*/
|
||||
fun setHueShift(value: Int) {
|
||||
if (value < 0) {
|
||||
hueShift = 0f
|
||||
} else if (value > 255) {
|
||||
hueShift = 360f
|
||||
} else {
|
||||
hueShift = (value / 255f) * 360f
|
||||
}
|
||||
}
|
||||
|
||||
var hueShift = 0f
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
field = value
|
||||
var newValue = value % 360f
|
||||
|
||||
if (newValue < 0f) {
|
||||
newValue += 360f
|
||||
}
|
||||
|
||||
if (newValue != field) {
|
||||
field = newValue
|
||||
chunk.incChangeset()
|
||||
}
|
||||
}
|
||||
@ -44,23 +74,28 @@ class ChunkTile(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is ChunkTile && other.color == color && other.variant == variant && other.modifier === modifier && other.def === def
|
||||
return other is TileState && other.color == color && other.modifier === modifier && other.def === def
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ChunkTile[$chunk, material = ${def.materialName}, color = $color, variant = $variant, modifier = ${modifier?.modName}]"
|
||||
return "ChunkTile[$chunk, material = ${def.materialName}, color = $color, modifier = ${modifier?.modName}]"
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
data class LiquidState(val chunk: Chunk<*, *>.TileLayer, val def: LiquidDefinition) {
|
||||
var pressure: Float = 0f
|
||||
var level: Float = 1f
|
||||
var isInfinite: Boolean = false
|
||||
}
|
||||
|
||||
private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double {
|
||||
if (point.x > 0.0) {
|
||||
return point.dot(axis)
|
||||
@ -308,16 +343,16 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
/**
|
||||
* Хранит тайлы как x + y * CHUNK_SIZE
|
||||
*/
|
||||
private val tiles = arrayOfNulls<ChunkTile>(CHUNK_SIZE * CHUNK_SIZE)
|
||||
private val tiles = arrayOfNulls<TileState>(CHUNK_SIZE * CHUNK_SIZE)
|
||||
|
||||
override operator fun get(x: Int, y: Int): ChunkTile? {
|
||||
override operator fun get(x: Int, y: Int): TileState? {
|
||||
if (isOutside(x, y))
|
||||
return null
|
||||
|
||||
return tiles[x or (y shl CHUNK_SHIFT)]
|
||||
}
|
||||
|
||||
operator fun set(x: Int, y: Int, tile: ChunkTile?) {
|
||||
operator fun set(x: Int, y: Int, tile: TileState?) {
|
||||
if (isOutside(x, y))
|
||||
throw IndexOutOfBoundsException("Trying to set tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range")
|
||||
|
||||
@ -326,11 +361,11 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
markPhysicsDirty()
|
||||
}
|
||||
|
||||
override operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? {
|
||||
override operator fun set(x: Int, y: Int, tile: TileDefinition?): TileState? {
|
||||
if (isOutside(x, y))
|
||||
throw IndexOutOfBoundsException("Trying to set tile ${tile?.materialName} at $x $y, but that is outside of chunk's range")
|
||||
|
||||
val chunkTile = if (tile != null) ChunkTile(this, tile) else null
|
||||
val chunkTile = if (tile != null) TileState(this, tile) else null
|
||||
this[x, y] = chunkTile
|
||||
changeset++
|
||||
markPhysicsDirty()
|
||||
@ -344,8 +379,11 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
|
||||
val foreground = TileLayer()
|
||||
val background = TileLayer()
|
||||
|
||||
protected val liquidStates: TwoDimensionalArray<LiquidState> = TwoDimensionalArray(CHUNK_SIZE, CHUNK_SIZE)
|
||||
|
||||
protected val entities = HashSet<Entity>()
|
||||
val entitiesAccess = Collections.unmodifiableSet(entities)
|
||||
val entitiesAccess: Set<Entity> = Collections.unmodifiableSet(entities)
|
||||
|
||||
protected abstract fun onEntityAdded(entity: Entity)
|
||||
protected abstract fun onEntityTransferedToThis(entity: Entity, otherChunk: This)
|
||||
@ -396,8 +434,8 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
val EMPTY = object : IMutableTileChunk {
|
||||
override val pos = ChunkPos(0, 0)
|
||||
|
||||
override fun get(x: Int, y: Int): ChunkTile? = null
|
||||
override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? = null
|
||||
override fun get(x: Int, y: Int): TileState? = null
|
||||
override fun set(x: Int, y: Int, tile: TileDefinition?): TileState? = null
|
||||
}
|
||||
|
||||
private val aabbBase = AABB(
|
||||
|
@ -26,7 +26,7 @@ interface ITileGetter : ITileMap {
|
||||
/**
|
||||
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
|
||||
*/
|
||||
operator fun get(x: Int, y: Int): ChunkTile?
|
||||
operator fun get(x: Int, y: Int): TileState?
|
||||
|
||||
/**
|
||||
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
|
||||
@ -38,8 +38,8 @@ interface ITileGetter : ITileMap {
|
||||
*
|
||||
* Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка
|
||||
*/
|
||||
val posToTile: Iterator<Pair<Vector2i, ChunkTile?>> get() {
|
||||
return object : Iterator<Pair<Vector2i, ChunkTile?>> {
|
||||
val posToTile: Iterator<Pair<Vector2i, TileState?>> get() {
|
||||
return object : Iterator<Pair<Vector2i, TileState?>> {
|
||||
private var x = 0
|
||||
private var y = 0
|
||||
|
||||
@ -49,7 +49,7 @@ interface ITileGetter : ITileMap {
|
||||
return idx() < CHUNK_SIZE * CHUNK_SIZE
|
||||
}
|
||||
|
||||
override fun next(): Pair<Vector2i, ChunkTile?> {
|
||||
override fun next(): Pair<Vector2i, TileState?> {
|
||||
if (!hasNext()) {
|
||||
throw IllegalStateException("Already iterated everything!")
|
||||
}
|
||||
@ -121,7 +121,7 @@ interface ITileSetter : ITileMap {
|
||||
/**
|
||||
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
|
||||
*/
|
||||
operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile?
|
||||
operator fun set(x: Int, y: Int, tile: TileDefinition?): TileState?
|
||||
/**
|
||||
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
|
||||
*/
|
||||
|
@ -22,7 +22,7 @@ open class TileView(
|
||||
open val bottomLeft: ITileChunk?,
|
||||
open val bottomRight: ITileChunk?,
|
||||
) : ITileChunk {
|
||||
override fun get(x: Int, y: Int): ChunkTile? {
|
||||
override fun get(x: Int, y: Int): TileState? {
|
||||
if (x in 0 ..CHUNK_SIZE_FF) {
|
||||
if (y in 0 ..CHUNK_SIZE_FF) {
|
||||
return center[x, y]
|
||||
@ -75,7 +75,7 @@ class MutableTileView(
|
||||
override val bottomLeft: IMutableTileChunk?,
|
||||
override val bottomRight: IMutableTileChunk?,
|
||||
) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
|
||||
override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? {
|
||||
override fun set(x: Int, y: Int, tile: TileDefinition?): TileState? {
|
||||
if (x in 0 .. CHUNK_SIZE_FF) {
|
||||
if (y in 0 .. CHUNK_SIZE_FF) {
|
||||
return center.set(x, y, tile)
|
||||
|
@ -323,7 +323,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
)
|
||||
}
|
||||
|
||||
fun getTile(pos: Vector2i): ChunkTile? {
|
||||
fun getTile(pos: Vector2i): TileState? {
|
||||
return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
return chunk
|
||||
}
|
||||
|
||||
fun getBackgroundTile(pos: Vector2i): ChunkTile? {
|
||||
fun getBackgroundTile(pos: Vector2i): TileState? {
|
||||
return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,11 @@ package ru.dbotthepony.kstarbound.world.phys
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
|
||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
|
||||
import ru.dbotthepony.kstarbound.world.ChunkTile
|
||||
import ru.dbotthepony.kstarbound.world.TileState
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
|
||||
class RectTileFlooderDepthFirst(
|
||||
private val tiles: Array<ChunkTile?>,
|
||||
private val tiles: Array<TileState?>,
|
||||
private val seen: BooleanArray,
|
||||
rootx: Int,
|
||||
rooty: Int
|
||||
|
@ -2,11 +2,11 @@ package ru.dbotthepony.kstarbound.world.phys
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
|
||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
|
||||
import ru.dbotthepony.kstarbound.world.ChunkTile
|
||||
import ru.dbotthepony.kstarbound.world.TileState
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
|
||||
class RectTileFlooderSizeFirst(
|
||||
private val tiles: Array<ChunkTile?>,
|
||||
private val tiles: Array<TileState?>,
|
||||
private val seen: BooleanArray,
|
||||
private val rootx: Int,
|
||||
private val rooty: Int
|
||||
|
@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.world.phys
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
|
||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
|
||||
import ru.dbotthepony.kstarbound.world.ChunkTile
|
||||
import ru.dbotthepony.kstarbound.world.TileState
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
private data class TileExposure(
|
||||
@ -14,7 +14,7 @@ private data class TileExposure(
|
||||
)
|
||||
|
||||
private class TileFlooder(
|
||||
private val tiles: Array<ChunkTile?>,
|
||||
private val tiles: Array<TileState?>,
|
||||
private val seen: BooleanArray,
|
||||
rootx: Int,
|
||||
rooty: Int
|
||||
|
@ -0,0 +1,38 @@
|
||||
|
||||
#version 460
|
||||
|
||||
uniform sampler2D _texture;
|
||||
uniform vec4 _color;
|
||||
|
||||
in vec2 _uv_out;
|
||||
in float _hsv_vertex;
|
||||
out vec4 _color_out;
|
||||
|
||||
// https://gist.github.com/983/e170a24ae8eba2cd174f
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(_texture, _uv_out);
|
||||
|
||||
vec3 hsv2 = rgb2hsv(vec3(texel[0], texel[1], texel[2]));
|
||||
hsv2[0] += _hsv_vertex / 360;
|
||||
vec4 rgb2 = vec4(hsv2rgb(hsv2), texel[3]);
|
||||
|
||||
_color_out = _color * rgb2;
|
||||
}
|
16
src/main/resources/shaders/vertex/texture_hsv.glsl
Normal file
16
src/main/resources/shaders/vertex/texture_hsv.glsl
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 _pos;
|
||||
layout (location = 1) in vec2 _uv_in;
|
||||
layout (location = 2) in float _hsv_in;
|
||||
|
||||
out vec2 _uv_out;
|
||||
out float _hsv_vertex;
|
||||
uniform mat4 _transform;
|
||||
|
||||
void main() {
|
||||
_uv_out = _uv_in;
|
||||
_hsv_vertex = _hsv_in;
|
||||
gl_Position = _transform * vec4(_pos, 1.0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user