wip dynamic textures

This commit is contained in:
Bixilon 2022-05-02 10:39:58 +02:00
parent b7f7ba21ec
commit 75b1bebd6b
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
25 changed files with 501 additions and 132 deletions

View File

@ -38,9 +38,7 @@ import de.bixilon.minosoft.gui.rendering.stats.ExperimentalRenderStats
import de.bixilon.minosoft.gui.rendering.stats.RenderStats
import de.bixilon.minosoft.gui.rendering.system.base.IntegratedBufferTypes
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem
import de.bixilon.minosoft.gui.rendering.system.window.BaseWindow
import de.bixilon.minosoft.gui.rendering.system.window.GLFWWindow
import de.bixilon.minosoft.gui.rendering.tint.TintManager
import de.bixilon.minosoft.gui.rendering.util.ScreenshotTaker
import de.bixilon.minosoft.gui.rendering.world.LightMap
@ -60,8 +58,8 @@ class RenderWindow(
private val profile = connection.profiles.rendering
val preferQuads = profile.advanced.preferQuads
val window: BaseWindow = GLFWWindow(this, connection)
val renderSystem: RenderSystem = OpenGLRenderSystem(this)
val window = BaseWindow.createWindow(this)
val renderSystem = RenderSystem.createRenderSystem(this)
val camera = Camera(this)
val inputHandler = RenderWindowInputHandler(this)
@ -151,7 +149,7 @@ class RenderWindow(
// Init stage
val initLatch = CountUpAndDownLatch(1, latch)
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Generating font and gathering textures (${stopwatch.labTime()})..." }
textureManager.loadDefaultTextures()
textureManager.loadDefaultTextures(connection.assetsManager)
font = FontLoader.load(this, initLatch)

View File

@ -21,6 +21,7 @@ import de.bixilon.minosoft.gui.rendering.font.renderer.ChatComponentRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMeshCache
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
@ -28,9 +29,9 @@ import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
class WorldGUIConsumer(val mesh: SingleWorldMesh, val transform: Mat4, val light: Int) : GUIVertexConsumer {
override val order: Array<Pair<Int, Int>> get() = mesh.order
override fun addVertex(position: Vec2t<*>, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
override fun addVertex(position: Vec2t<*>, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
val transformed = transform.fastTimes(position.x.toFloat() / ChatComponentRenderer.TEXT_BLOCK_RESOLUTION, -position.y.toFloat() / ChatComponentRenderer.TEXT_BLOCK_RESOLUTION)
mesh.addVertex(transformed, uv, texture, tint.rgb, light)
mesh.addVertex(transformed, uv, texture as AbstractTexture, tint.rgb, light)
}
override fun addCache(cache: GUIMeshCache) {

View File

@ -0,0 +1,82 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.gui.elements.primitive
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
open class DynamicImageElement(
guiRenderer: GUIRenderer,
texture: DynamicTexture?,
uvStart: Vec2 = Vec2.EMPTY,
uvEnd: Vec2 = Vec2(1.0f, 1.0f),
size: Vec2i = Vec2i.EMPTY,
tint: RGBColor = ChatColors.WHITE,
) : Element(guiRenderer, GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX * 6) {
var texture: DynamicTexture? = texture
set(value) {
field = value
cacheUpToDate = false
}
var uvStart: Vec2 = uvStart
set(value) {
field = value
cacheUpToDate = false
}
var uvEnd: Vec2 = uvEnd
set(value) {
field = value
cacheUpToDate = false
}
override var size: Vec2i
get() = super.size
set(value) {
super.size = value
cacheUpToDate = false
}
override var prefSize: Vec2i
get() = size
set(value) {
size = value
}
var tint: RGBColor = tint
set(value) {
field = value
cacheUpToDate = false
}
init {
this.size = size
}
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
consumer.addQuad(offset, offset + size, texture ?: return, uvStart, uvEnd, tint, options)
}
override fun forceSilentApply() = Unit
override fun silentApply(): Boolean = false
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.player.tab.TabListItem
@ -25,6 +26,7 @@ import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments.Compa
import de.bixilon.minosoft.gui.rendering.gui.elements.Pollable
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.AtlasImageElement
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ColorElement
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.DynamicImageElement
import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
@ -38,6 +40,7 @@ class TabListEntryElement(
val item: TabListItem,
width: Int,
) : Element(guiRenderer), Pollable, Comparable<TabListEntryElement> {
init {
_parent = tabList
}
@ -45,6 +48,7 @@ class TabListEntryElement(
// ToDo: Skin
private val background: ColorElement
private val skinElement = DynamicImageElement(guiRenderer, guiRenderer.renderWindow.textureManager.alexTexture, uvStart = Vec2(0.125), uvEnd = Vec2(0.25), size = Vec2i(8, 8))
private val nameElement = TextElement(guiRenderer, "", background = false, parent = this)
private lateinit var pingElement: AtlasImageElement
@ -77,8 +81,10 @@ class TabListEntryElement(
}
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
skinElement
background.render(offset, consumer, options)
nameElement.render(offset, consumer, options)
skinElement.render(offset, consumer, options)
nameElement.render(offset + Vec2i(skinElement.size.x + PADDING, PADDING), consumer, options)
pingElement.render(offset + Vec2i(HorizontalAlignments.RIGHT.getOffset(maxSize.x, pingElement.size.x + PADDING), PADDING), consumer, options)
}
@ -96,7 +102,7 @@ class TabListEntryElement(
nameElement.text = displayName
this.prefSize = Vec2i((PADDING * 2) + nameElement.prefSize.x + INNER_MARGIN + pingElement.prefSize.x, HEIGHT)
this.prefSize = Vec2i((PADDING * 3) + skinElement.prefSize.x + nameElement.prefSize.x + INNER_MARGIN + pingElement.prefSize.x, HEIGHT)
background.size = size
cacheUpToDate = false
}

View File

@ -18,7 +18,7 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2t
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.orthoTimes
@ -31,7 +31,7 @@ class GUIMesh(
data: DirectArrayFloatList,
) : Mesh(renderWindow, GUIMeshStruct, initialCacheSize = 40000, clearOnLoad = false, data = data), GUIVertexConsumer {
override fun addVertex(position: Vec2t<*>, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
override fun addVertex(position: Vec2t<*>, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
addVertex(data, matrix, position, texture, uv, tint, options)
}
@ -50,7 +50,7 @@ class GUIMesh(
companion object {
fun addVertex(data: AbstractFloatList, matrix: Mat4, position: Vec2t<*>, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
fun addVertex(data: AbstractFloatList, matrix: Mat4, position: Vec2t<*>, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
val outPosition = matrix orthoTimes position
var color = tint.rgba
@ -69,7 +69,7 @@ class GUIMesh(
data.add(outPosition.y)
data.add(uv.x)
data.add(uv.y)
data.add(Float.fromBits(texture.renderData.shaderTextureId))
data.add(Float.fromBits(texture.shaderId))
data.add(Float.fromBits(color))
}
}

View File

@ -18,7 +18,7 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec2.Vec2t
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.util.collections.floats.AbstractFloatList
import de.bixilon.minosoft.util.collections.floats.HeapArrayFloatList
@ -41,7 +41,7 @@ class GUIMeshCache(
}
}
override fun addVertex(position: Vec2t<*>, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
override fun addVertex(position: Vec2t<*>, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
GUIMesh.addVertex(data, matrix, position, texture, uv, tint, options)
revision++
}

View File

@ -17,14 +17,14 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2t
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLike
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
interface GUIVertexConsumer {
val order: Array<Pair<Int, Int>>
fun addVertex(position: Vec2t<*>, texture: AbstractTexture, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?)
fun addVertex(position: Vec2t<*>, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?)
fun addQuad(start: Vec2t<*>, end: Vec2t<*>, texture: AbstractTexture, uvStart: Vec2 = Vec2(0.0f, 0.0f), uvEnd: Vec2 = Vec2(1.0f, 1.0f), tint: RGBColor, options: GUIVertexOptions?) {
fun addQuad(start: Vec2t<*>, end: Vec2t<*>, texture: ShaderIdentifiable, uvStart: Vec2 = Vec2(0.0f, 0.0f), uvEnd: Vec2 = Vec2(1.0f, 1.0f), tint: RGBColor, options: GUIVertexOptions?) {
val positions = arrayOf(
start,
Vec2(end.x, start.y),

View File

@ -17,6 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.text.Colors
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.buffer.frame.Framebuffer
import de.bixilon.minosoft.gui.rendering.system.base.buffer.uniform.FloatUniformBuffer
import de.bixilon.minosoft.gui.rendering.system.base.buffer.uniform.IntUniformBuffer
@ -24,6 +25,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.FloatVertexBu
import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.PrimitiveTypes
import de.bixilon.minosoft.gui.rendering.system.base.shader.Shader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import java.nio.ByteBuffer
@ -118,4 +120,12 @@ interface RenderSystem {
fun polygonOffset(factor: Float, unit: Float)
companion object {
fun createRenderSystem(renderWindow: RenderWindow): RenderSystem {
return OpenGLRenderSystem(renderWindow)
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture
interface ShaderIdentifiable {
val shaderId: Int
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -13,13 +13,21 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture
import de.bixilon.kutil.latch.CountUpAndDownLatch
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.PNGTexture
interface StaticTextureArray : TextureArray {
val textures: MutableMap<ResourceLocation, AbstractTexture>
val animator: SpriteAnimator
val state: TextureArrayStates
override fun get(resourceLocation: ResourceLocation): AbstractTexture? {
fun get(resourceLocation: ResourceLocation): AbstractTexture? {
return textures[resourceLocation]
}
fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, default: () -> AbstractTexture = { PNGTexture(resourceLocation, generateMipMaps = mipmaps) }): AbstractTexture
fun preLoad(latch: CountUpAndDownLatch)
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,21 +14,10 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture
import de.bixilon.kutil.latch.CountUpAndDownLatch
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.shader.Shader
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.PNGTexture
interface TextureArray {
val animator: SpriteAnimator
val state: TextureArrayStates
operator fun get(resourceLocation: ResourceLocation): AbstractTexture?
fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, default: () -> AbstractTexture = { PNGTexture(resourceLocation, generateMipMaps = mipmaps) }): AbstractTexture
fun preLoad(latch: CountUpAndDownLatch)
fun load(latch: CountUpAndDownLatch)
fun use(shader: Shader, arrayName: String = "uTextures")
fun use(shader: Shader, name: String = "uTextures")
}

View File

@ -15,24 +15,44 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.uuid.UUIDUtil.toUUID
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLikeTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.readTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.util.KUtil.toResourceLocation
abstract class TextureManager {
abstract var staticTextures: StaticTextureArray
abstract val staticTextures: StaticTextureArray
abstract val dynamicTextures: DynamicTextureArray
lateinit var debugTexture: AbstractTexture
private set
lateinit var whiteTexture: TextureLikeTexture
private set
lateinit var steveTexture: DynamicTexture
private set
lateinit var alexTexture: DynamicTexture
private set
fun loadDefaultTextures() {
fun loadDefaultTextures(assetsManager: AssetsManager) {
if (this::debugTexture.isInitialized) {
throw IllegalStateException("Already initialized!")
}
debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
whiteTexture = TextureLikeTexture(texture = staticTextures.createTexture(ResourceLocation("minosoft:textures/white.png")), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
loadDefaultSkins(assetsManager)
}
private fun loadDefaultSkins(assetsManager: AssetsManager) {
// ToDo: For testing purposes only, they will be moved to static textures
steveTexture = dynamicTextures.push("3780a46b-a725-4b22-8366-01056c698386".toUUID()) { assetsManager["minecraft:entity/steve".toResourceLocation().texture()].readTexture().second }
alexTexture = dynamicTextures.push("3780a46b-a725-4b22-8366-01056c698386".toUUID()) { assetsManager["minecraft:entity/alex".toResourceLocation().texture()].readTexture().second }
}
}

View File

@ -0,0 +1,23 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
interface DynamicTexture : ShaderIdentifiable {
val uuid: UUID
val usages: AtomicInteger
}

View File

@ -0,0 +1,26 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureArray
import java.nio.ByteBuffer
import java.util.*
interface DynamicTextureArray : TextureArray {
val size: Int
fun push(identifier: UUID, data: () -> ByteBuffer): DynamicTexture
fun remove(texture: DynamicTexture)
fun remove(uuid: UUID)
}

View File

@ -17,15 +17,14 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureArray
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
import example.jonathan2520.SRGBAverager
import org.lwjgl.BufferUtils
import java.nio.ByteBuffer
interface AbstractTexture {
interface AbstractTexture : ShaderIdentifiable {
val resourceLocation: ResourceLocation
var textureArrayUV: Vec2
@ -45,61 +44,15 @@ interface AbstractTexture {
fun load(assetsManager: AssetsManager)
override val shaderId: Int
get() = renderData.shaderTextureId
fun generateMipMaps(data: ByteBuffer = this.data!!): Array<ByteBuffer> {
val images: MutableList<ByteBuffer> = mutableListOf()
images += data
if (!generateMipMaps) {
return images.toTypedArray()
return arrayOf(data)
}
var currentData = data
for (i in 1 until OpenGLTextureArray.MAX_MIPMAP_LEVELS) {
val mipMapSize = Vec2i(size.x shr i, size.y shr i)
if (mipMapSize.x <= 0 || mipMapSize.y <= 0) {
break
}
currentData = generateMipmap(currentData, Vec2i(size.x shr (i - 1), size.y shr (i - 1)))
images += currentData
}
return images.toTypedArray()
}
private fun generateMipmap(origin: ByteBuffer, oldSize: Vec2i): ByteBuffer {
// No Vec2i: performance reasons
val oldSizeX = oldSize.x
val newSizeX = oldSizeX shr 1
val buffer = BufferUtils.createByteBuffer(origin.capacity() shr 1)
buffer.limit(buffer.capacity())
fun getRGB(x: Int, y: Int): Int {
return origin.getInt((y * oldSizeX + x) * 4)
}
fun setRGB(x: Int, y: Int, color: Int) {
buffer.putInt((y * newSizeX + x) * 4, color)
}
for (y in 0 until (oldSize.y shr 1)) {
for (x in 0 until newSizeX) {
val xOffset = x * 2
val yOffset = y * 2
val output = SRGBAverager.average(
getRGB(xOffset + 0, yOffset + 0),
getRGB(xOffset + 1, yOffset + 0),
getRGB(xOffset + 0, yOffset + 1),
getRGB(xOffset + 1, yOffset + 1),
)
setRGB(x, y, output)
}
}
buffer.rewind()
return buffer
return OpenGLTextureUtil.generateMipMaps(data, size)
}
}

View File

@ -43,6 +43,7 @@ class MemoryTexture(
init {
val data = BufferUtils.createByteBuffer(size.x * size.y * PNGDecoder.Format.RGBA.numComponents)
this.data = data
generator?.let {
var index = 0

View File

@ -19,14 +19,9 @@ import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.readTexture
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
import de.matthiasmann.twl.utils.PNGDecoder
import org.lwjgl.BufferUtils
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.DataOutputStream
import java.nio.ByteBuffer
import javax.imageio.ImageIO
class PNGTexture(
@ -57,26 +52,9 @@ class PNGTexture(
return
}
val decoder = PNGDecoder(assetsManager[resourceLocation])
val data = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents)
try {
decoder.decode(data, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA)
} catch (exception: Throwable) {
// ToDo: This somehow crashes with some resource packs
// exception.printStackTrace()
val image: BufferedImage = ImageIO.read(assetsManager[resourceLocation])
val rgb = image.getRGB(0, 0, image.width, image.height, null, 0, image.width)
val (size, data) = assetsManager[resourceLocation].readTexture()
val byteOutput = ByteArrayOutputStream()
val dataOutput = DataOutputStream(byteOutput)
for (color in rgb) {
dataOutput.writeInt(color shl 8)
}
data.put(byteOutput.toByteArray())
}
size = Vec2i(decoder.width, decoder.height)
this.size = size
transparency = TextureTransparencies.OPAQUE
for (i in 0 until data.limit() / 4) {
val alpha = data[i * 4 + 3].toInt() and 0xFF

View File

@ -151,16 +151,9 @@ class OpenGLTextureArray(
@Synchronized
private fun loadSingleArray(resolution: Int, textures: List<AbstractTexture>): Int {
val textureId = glGenTextures()
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT)
// glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, MAX_MIPMAP_LEVELS - 1)
val textureId = OpenGLTextureUtil.createTextureArray()
for (level in 0 until MAX_MIPMAP_LEVELS) {
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
}
@ -199,7 +192,7 @@ class OpenGLTextureArray(
}
override fun use(shader: Shader, arrayName: String) {
override fun use(shader: Shader, name: String) {
shader.use()
for ((index, textureId) in textureIds.withIndex()) {
@ -208,7 +201,7 @@ class OpenGLTextureArray(
}
glActiveTexture(GL_TEXTURE0 + index)
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
shader.setTexture("$arrayName[$index]", index)
shader.setTexture("$name[$index]", index)
}
}
@ -216,6 +209,5 @@ class OpenGLTextureArray(
companion object {
const val TEXTURE_MAX_RESOLUTION = 1024
val TEXTURE_RESOLUTION_ID_MAP = intArrayOf(16, 32, 64, 128, 256, 512, TEXTURE_MAX_RESOLUTION) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split
const val MAX_MIPMAP_LEVELS = 5
}
}

View File

@ -16,7 +16,10 @@ package de.bixilon.minosoft.gui.rendering.system.opengl.texture
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.texture.StaticTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic.OpenGLDynamicTextureArray
class OpenGLTextureManager(val renderWindow: RenderWindow) : TextureManager() {
override var staticTextures: StaticTextureArray = OpenGLTextureArray(renderWindow)
override val staticTextures: StaticTextureArray = OpenGLTextureArray(renderWindow)
override val dynamicTextures: DynamicTextureArray = OpenGLDynamicTextureArray(renderWindow, resolution = 64)
}

View File

@ -0,0 +1,127 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.opengl.texture
import de.bixilon.kotlinglm.vec2.Vec2i
import de.matthiasmann.twl.utils.PNGDecoder
import example.jonathan2520.SRGBAverager
import org.lwjgl.BufferUtils
import org.lwjgl.opengl.GL11.*
import org.lwjgl.opengl.GL12.GL_TEXTURE_MAX_LEVEL
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.DataOutputStream
import java.io.InputStream
import java.nio.ByteBuffer
import javax.imageio.ImageIO
object OpenGLTextureUtil {
const val MAX_MIPMAP_LEVELS = 5
fun createTextureArray(): Int {
val textureId = glGenTextures()
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT)
// glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, MAX_MIPMAP_LEVELS - 1)
return textureId
}
fun generateMipMaps(data: ByteBuffer, size: Vec2i): Array<ByteBuffer> {
val images: MutableList<ByteBuffer> = mutableListOf()
images += data
var currentData = data
for (i in 1 until MAX_MIPMAP_LEVELS) {
val mipMapSize = Vec2i(size.x shr i, size.y shr i)
if (mipMapSize.x <= 0 || mipMapSize.y <= 0) {
break
}
currentData = generateMipmap(currentData, Vec2i(size.x shr (i - 1), size.y shr (i - 1)))
images += currentData
}
return images.toTypedArray()
}
private fun generateMipmap(origin: ByteBuffer, oldSize: Vec2i): ByteBuffer {
// No Vec2i: performance reasons
val oldSizeX = oldSize.x
val newSizeX = oldSizeX shr 1
val buffer = BufferUtils.createByteBuffer(origin.capacity() shr 1)
buffer.limit(buffer.capacity())
fun getRGB(x: Int, y: Int): Int {
return origin.getInt((y * oldSizeX + x) * 4)
}
fun setRGB(x: Int, y: Int, color: Int) {
buffer.putInt((y * newSizeX + x) * 4, color)
}
for (y in 0 until (oldSize.y shr 1)) {
for (x in 0 until newSizeX) {
val xOffset = x * 2
val yOffset = y * 2
val output = SRGBAverager.average(
getRGB(xOffset + 0, yOffset + 0),
getRGB(xOffset + 1, yOffset + 0),
getRGB(xOffset + 0, yOffset + 1),
getRGB(xOffset + 1, yOffset + 1),
)
setRGB(x, y, output)
}
}
buffer.rewind()
return buffer
}
private fun InputStream.readFallbackTexture(): Pair<Vec2i, ByteBuffer> {
// ToDo: This somehow crashes with some resource packs
val image: BufferedImage = ImageIO.read(this)
val rgb = image.getRGB(0, 0, image.width, image.height, null, 0, image.width)
val byteOutput = ByteArrayOutputStream()
val dataOutput = DataOutputStream(byteOutput)
for (color in rgb) {
dataOutput.writeInt(color shl 8)
}
return Pair(Vec2i(image.width, image.height), ByteBuffer.wrap(byteOutput.toByteArray()))
}
fun InputStream.readTexture(): Pair<Vec2i, ByteBuffer> {
return try {
val decoder = PNGDecoder(this)
val data = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents)
decoder.decode(data, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA)
Pair(Vec2i(decoder.width, decoder.height), data)
} catch (exception: Throwable) {
readFallbackTexture()
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
class OpenGLDynamicTexture(
override val uuid: UUID,
override val shaderId: Int,
) : DynamicTexture {
override val usages = AtomicInteger()
}

View File

@ -0,0 +1,99 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.latch.CountUpAndDownLatch
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.system.base.shader.Shader
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil
import org.lwjgl.opengl.GL11.*
import org.lwjgl.opengl.GL12
import org.lwjgl.opengl.GL12.glTexImage3D
import org.lwjgl.opengl.GL12.glTexSubImage3D
import org.lwjgl.opengl.GL13.GL_TEXTURE0
import org.lwjgl.opengl.GL13.glActiveTexture
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
import java.nio.ByteBuffer
import java.util.*
class OpenGLDynamicTextureArray(
val renderWindow: RenderWindow,
val index: Int = 6,
val resolution: Int,
) : DynamicTextureArray {
private val textures: Array<OpenGLDynamicTexture?> = arrayOfNulls(32)
private var textureId = -1
override val size: Int
get() = 2
override fun remove(uuid: UUID) {
TODO("Not yet implemented")
}
override fun remove(texture: DynamicTexture) {
TODO("Not yet implemented")
}
override fun push(identifier: UUID, data: () -> ByteBuffer): OpenGLDynamicTexture {
val bytes = data()
check(bytes.limit() == resolution * resolution * 4) { "Texture must have a size of ${resolution}x${resolution}" }
val mipmaps = OpenGLTextureUtil.generateMipMaps(bytes, Vec2i(resolution, resolution))
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
for ((level, mipmap) in mipmaps.withIndex()) {
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, level, resolution, resolution, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap)
}
return OpenGLDynamicTexture(identifier, createShaderIdentifier(index = 0))
}
private fun createShaderIdentifier(array: Int = this.index, index: Int): Int {
return (array shl 28) or (index shl 12) or 0
}
override fun load(latch: CountUpAndDownLatch) {
val textureId = OpenGLTextureUtil.createTextureArray()
this.textureId = textureId
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL12.GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
}
for (i in textures.indices) {
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, i, resolution, resolution, 1, GL_RGBA, GL_UNSIGNED_BYTE, ByteBuffer.wrap(ByteArray(resolution * resolution * 4)))
}
}
this.textureId = textureId
}
override fun use(shader: Shader, name: String) {
shader.use()
glActiveTexture(GL_TEXTURE0 + index)
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
shader.setTexture("$name[$index]", index)
}
}

View File

@ -18,6 +18,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.config.profile.delegate.watcher.SimpleProfileDelegateWatcher.Companion.profileWatchRendering
import de.bixilon.minosoft.config.profile.profiles.rendering.RenderingProfile
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.matthiasmann.twl.utils.PNGDecoder
@ -92,5 +93,10 @@ interface BaseWindow {
get() = Vec2i(300, 100)
val DEFAULT_MAXIMUM_WINDOW_SIZE: Vec2i
get() = Vec2i(-1, -1)
fun createWindow(renderWindow: RenderWindow): BaseWindow {
return GLFWWindow(renderWindow)
}
}
}

View File

@ -45,7 +45,7 @@ import java.nio.ByteBuffer
class GLFWWindow(
private val renderWindow: RenderWindow,
private val eventMaster: AbstractEventMaster,
private val eventMaster: AbstractEventMaster = renderWindow.connection,
) : BaseWindow {
private var mousePosition = Vec2d.EMPTY
private var skipNextMouseEvent = true

View File

@ -12,9 +12,11 @@
*/
uniform sampler2DArray uTextures[7];
uniform sampler2DArray uTextures[8];
vec4 getTexture(uint textureId, vec3 uv) { // ToDo: This method is just stupid and workarounds an opengl crash with mesa drivers
// ToDo: Those methods are just stupid and workaround an opengl crash with mesa drivers
vec4 getTexture(uint textureId, vec3 uv) {
#if defined __NVIDIA || defined __AMD
return texture(uTextures[textureId], uv);
#else
@ -25,12 +27,13 @@ vec4 getTexture(uint textureId, vec3 uv) { // ToDo: This method is just stupid a
case 4u: return texture(uTextures[4], uv);
case 5u: return texture(uTextures[5], uv);
case 6u: return texture(uTextures[6], uv);
case 7u: return texture(uTextures[7], uv);
}
return texture(uTextures[0], uv);
#endif
}
vec4 getTexture(uint textureId, vec3 uv, float mipmapLevel) { // ToDo: This method is just stupid and workarounds an opengl crash with mesa drivers
vec4 getTexture(uint textureId, vec3 uv, float mipmapLevel) {
#if defined __NVIDIA || defined __AMD
return textureLod(uTextures[textureId], uv, mipmapLevel);
#else
@ -41,6 +44,7 @@ vec4 getTexture(uint textureId, vec3 uv, float mipmapLevel) { // ToDo: This meth
case 4u: return textureLod(uTextures[4], uv, mipmapLevel);
case 5u: return textureLod(uTextures[5], uv, mipmapLevel);
case 6u: return textureLod(uTextures[6], uv, mipmapLevel);
case 7u: return textureLod(uTextures[7], uv, mipmapLevel);
}
return textureLod(uTextures[0], uv, mipmapLevel);
#endif