improve dynamic texture change callbacks, improve skeletal system, improve player rendering

This commit is contained in:
Bixilon 2022-06-09 11:59:23 +02:00
parent 00dca558a6
commit c444b9f25f
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
15 changed files with 115 additions and 35 deletions

View File

@ -86,9 +86,8 @@ abstract class Entity(
open val uuid: UUID?
get() = connection.world.entities.getUUID(this)
@JvmField
@Deprecated(message = "Use connection.version")
protected val versionId: Int = connection.version.versionId
@Deprecated(message = "Use connection.version", replaceWith = ReplaceWith("connection.version.versionId"))
protected val versionId: Int get() = connection.version.versionId
open var _attachedEntity: Int? = null
var vehicle: Entity? = null

View File

@ -20,13 +20,19 @@ import de.bixilon.minosoft.gui.rendering.entity.models.SkeletalEntityModel
import de.bixilon.minosoft.gui.rendering.models.ModelLoader.Companion.bbModel
import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance
import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
import de.bixilon.minosoft.protocol.packets.c2s.play.SettingsC2SP
import de.bixilon.minosoft.util.KUtil.toResourceLocation
open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : SkeletalEntityModel<PlayerEntity>(renderer, player) {
open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : SkeletalEntityModel<PlayerEntity>(renderer, player), DynamicStateChangeCallback {
open val skinParts: Set<SettingsC2SP.SkinParts> = player.getSkinParts()
private var skin: DynamicTexture? = null
protected var refreshModel = false
override val instance = createModel()
private var _instance: SkeletalInstance? = null
override var instance = createModel()
private fun createModel(): SkeletalInstance {
val unbaked = renderWindow.modelLoader.entities.loadUnbakedModel(BB_MODEL)
@ -41,12 +47,32 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
}
elements += element
}
val texture = renderWindow.textureManager.getSkin(entity)
val model = unbaked.copy(elements = elements).bake(renderWindow, mutableMapOf(0 to texture))
val skin = renderWindow.textureManager.getSkin(entity)
skin.usages.incrementAndGet()
this.skin?.usages?.decrementAndGet()
this.skin = skin
skin.callbacks += this
val model = unbaked.copy(elements = elements).bake(renderWindow, mutableMapOf(0 to skin))
return SkeletalInstance(renderWindow, Vec3i(), model)
}
override fun prepareAsync() {
if (refreshModel) {
_instance = instance
instance = createModel()
refreshModel = false
}
super.prepareAsync()
}
override fun prepare() {
_instance?.unload()
_instance = null
super.prepare()
}
private fun SkeletalElement.skinCopy(parts: Set<SettingsC2SP.SkinParts>, part: SettingsC2SP.SkinParts): SkeletalElement {
if (part in parts) {
return this
@ -54,6 +80,12 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
return this.copy(visible = false)
}
override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) {
if (skin === texture) {
refreshModel = true
}
}
companion object {
private val BB_MODEL = "minecraft:entities/player/steve".toResourceLocation().bbModel()

View File

@ -23,6 +23,7 @@ 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.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
@ -36,14 +37,14 @@ open class DynamicImageElement(
size: Vec2i = Vec2i.EMPTY,
tint: RGBColor = ChatColors.WHITE,
parent: Element? = null,
) : Element(guiRenderer, GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX * 6) {
) : Element(guiRenderer, GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX * 6), DynamicStateChangeCallback {
var texture: DynamicTexture? = null
set(value) {
field?.usages?.decrementAndGet()
field?.onStateChange = null
field?.callbacks?.remove(this)
value?.usages?.incrementAndGet()
value?.onStateChange = { forceApply() }
value?.callbacks?.add(this)
field = value
cacheUpToDate = false
}
@ -101,4 +102,10 @@ open class DynamicImageElement(
protected fun finalize() {
texture?.usages?.decrementAndGet()
}
override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) {
if (texture === this.texture) {
forceApply()
}
}
}

View File

@ -19,11 +19,12 @@ import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.unbaked.ModelBakeUtil
import de.bixilon.minosoft.gui.rendering.models.unbaked.element.UnbakedElement
import de.bixilon.minosoft.gui.rendering.models.unbaked.element.UnbakedElement.Companion.BLOCK_RESOLUTION
import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalMesh
import de.bixilon.minosoft.gui.rendering.skeletal.model.SkeletalModel
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderTexture
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotateAssign
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
@ -35,7 +36,6 @@ class BakedSkeletalModel(
) {
lateinit var mesh: SkeletalMesh
private fun calculateOutlinerMapping(): Map<UUID, Int> {
val mapping: Object2IntOpenHashMap<UUID> = Object2IntOpenHashMap()
@ -64,7 +64,7 @@ class BakedSkeletalModel(
}
fun loadMesh(renderWindow: RenderWindow) {
if (this::mesh.isInitialized) {
if (this::mesh.isInitialized && mesh.state == Mesh.MeshStates.LOADED) {
return
}
val mesh = SkeletalMesh(renderWindow, 1000)
@ -76,7 +76,7 @@ class BakedSkeletalModel(
if (!element.visible) {
continue
}
val inflate = (element.inflate * 0.001f)
val inflate = (element.inflate / BLOCK_RESOLUTION)
for ((direction, face) in element.faces) {
val positions = direction.getPositions(element.from.fromBlockCoordinates() - inflate, element.to.fromBlockCoordinates() + inflate)
@ -107,10 +107,14 @@ class BakedSkeletalModel(
this.mesh = mesh
}
fun unload() {
mesh.unload()
}
companion object {
fun Vec3.fromBlockCoordinates(): Vec3 {
return Vec3(this.x / UnbakedElement.BLOCK_RESOLUTION + 0.5f, this.y / UnbakedElement.BLOCK_RESOLUTION, this.z / UnbakedElement.BLOCK_RESOLUTION + 0.5f)
return Vec3(this.x / BLOCK_RESOLUTION + 0.5f, this.y / BLOCK_RESOLUTION, this.z / BLOCK_RESOLUTION + 0.5f)
}
}
}

View File

@ -125,4 +125,8 @@ class SkeletalInstance(
calculateTransform(skeletalTransform, animations, child, transforms)
}
}
fun unload() {
model.unload()
}
}

View File

@ -13,7 +13,6 @@
package de.bixilon.minosoft.gui.rendering.skeletal.model
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.skeletal.baked.BakedSkeletalModel
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation
@ -23,14 +22,11 @@ import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutline
import de.bixilon.minosoft.gui.rendering.skeletal.model.resolution.SkeletalResolution
import de.bixilon.minosoft.gui.rendering.skeletal.model.textures.SkeletalTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderTexture
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
data class SkeletalModel(
val meta: SkeletalMeta = SkeletalMeta(),
val name: String = "empty",
val geometryName: String = name,
val visibleBox: Vec3 = Vec3.EMPTY,
val resolution: SkeletalResolution = SkeletalResolution(),
val elements: List<SkeletalElement> = emptyList(),
val outliner: List<SkeletalOutliner> = emptyList(),

View File

@ -22,7 +22,6 @@ data class StaticSkeletalAnimation(
val uuid: UUID,
override val name: String,
override val loop: AnimationLoops = AnimationLoops.LOOP,
val override: Boolean = false,
override val length: Float,
val animators: Map<UUID, SkeletalAnimator>,
) : SkeletalAnimation {

View File

@ -26,11 +26,8 @@ data class SkeletalElement(
val name: String,
val rescale: Boolean = false,
val visible: Boolean = true,
val locked: Boolean = false,
val from: Vec3 = Vec3.EMPTY,
val to: Vec3 = Vec3.ONE,
val autouv: Int = 0,
val color: Int = 0,
val rotation: Vec3 = Vec3.EMPTY,
val origin: Vec3 = Vec3.EMPTY,
val uvOffset: Vec2 = Vec2.EMPTY,

View File

@ -0,0 +1,20 @@
/*
* 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.skeletal.model.meta
enum class ModelFormats {
FREE,
SKIN,
;
}

View File

@ -15,6 +15,5 @@ package de.bixilon.minosoft.gui.rendering.skeletal.model.meta
data class SkeletalMeta(
val formatVersion: String = "4.0",
val modelFormat: String = "free", // ToDo: Enum
val boxUV: Boolean = true,
val modelFormat: ModelFormats = ModelFormats.FREE,
)

View File

@ -22,14 +22,8 @@ import java.util.*
data class SkeletalTexture(
val path: String,
val name: String,
val folder: String = "",
val namespace: String = ProtocolDefinition.DEFAULT_NAMESPACE,
val id: Int,
val particle: Boolean = true,
val renderMode: String = "normal", // ToDo: enum
val visible: Boolean = true,
val mode: String = "bitmap", // ToDo: enum
val saved: Boolean = true,
val uuid: UUID,
) {
val resourceLocation = ResourceLocation(namespace, path).texture()

View File

@ -17,6 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.player.LocalPlayerEntity
import de.bixilon.minosoft.data.player.properties.PlayerProperties
import de.bixilon.minosoft.data.player.properties.textures.PlayerTexture.Companion.isSteve
import de.bixilon.minosoft.data.registries.ResourceLocation
@ -56,7 +57,6 @@ abstract class TextureManager {
}
fun loadDefaultSkins(connection: PlayConnection) {
// ToDo: For testing purposes only, they will be moved to static textures
steveTexture = dynamicTextures.pushBuffer(UUID(0L, 0L)) { connection.assetsManager["minecraft:entity/steve".toResourceLocation().texture()].readTexture().second }.apply { usages.incrementAndGet() }
alexTexture = dynamicTextures.pushBuffer(UUID(1L, 0L)) { connection.assetsManager["minecraft:entity/alex".toResourceLocation().texture()].readTexture().second }.apply { usages.incrementAndGet() }
skin = getSkin(connection.account.supportsSkins, connection.account.uuid, connection.account.properties).apply { usages.incrementAndGet() }
@ -86,6 +86,9 @@ abstract class TextureManager {
}
fun getSkin(player: PlayerEntity): DynamicTexture {
if (player is LocalPlayerEntity) {
return skin
}
return getSkin(true, player.uuid ?: return steveTexture, player.tabListItem.properties)
}
}

View File

@ -0,0 +1,19 @@
/*
* 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
interface DynamicStateChangeCallback {
fun onStateChange(texture: DynamicTexture, state: DynamicTextureState)
}

View File

@ -23,5 +23,5 @@ interface DynamicTexture : ShaderTexture {
val state: DynamicTextureState
var onStateChange: (() -> Unit)?
var callbacks: MutableSet<DynamicStateChangeCallback>
}

View File

@ -14,6 +14,8 @@
package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kutil.collections.CollectionUtil.synchronizedSetOf
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
import java.nio.ByteBuffer
@ -25,12 +27,17 @@ class OpenGLDynamicTexture(
shaderId: Int,
) : DynamicTexture {
var data: Array<ByteBuffer>? = null
override var onStateChange: (() -> Unit)? = null
override var callbacks: MutableSet<DynamicStateChangeCallback> = synchronizedSetOf()
override val usages = AtomicInteger()
override var state: DynamicTextureState = DynamicTextureState.WAITING
set(value) {
if (field == value) {
return
}
field = value
onStateChange?.invoke()
for (callback in callbacks) {
callback.onStateChange(this, value)
}
}
override var shaderId: Int = shaderId