wip refactor skeletal

This commit is contained in:
Moritz Zwerger 2023-10-10 19:55:51 +02:00
parent 4351b1567a
commit c5ece6b11b
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
34 changed files with 466 additions and 717 deletions

18
doc/rendering/Entities.md Normal file
View File

@ -0,0 +1,18 @@
# Entity rendering
Entity rendering is a quite hard topic.
## Classes
- Player renderer
- player model
- feature renderer:
- armor
- stuck arrows
## Skeletal models
- SkeletalModel (raw data)
- BakedSkeletalModel (baked mesh)
- SkeletalInstance (renders baked mesh, contains transform values)
- Wrapper (indirectly accesses skeletal instance transforms)

View File

@ -0,0 +1,63 @@
{
"elements": {
"body": {
"from": [-8, 0, -8],
"to": [8, 16, 8],
"rotation": [-180, 0, 270],
"texture": "minosoft:dummy_texture",
"transform": false,
"faces": {
"down": {"uv": [0, 0, 16, 16]},
"up": {"uv": [0, 0, 16, 16]},
"north": {"uv": [0, 0, 16, 16]},
"south": {"uv": [0, 0, 16, 16]},
"west": {"uv": [0, 0, 16, 16]},
"east": {"uv": [0, 0, 16, 16], "texture": "minosoft:second_texture"}
},
"children": {
"head": {
"pivot": [0, 16, 0],
"origin": [0, 5, 0],
"from": [-5, 0, -5],
"to": [5, 10, 5],
"faces": {
"up": {"uv": [0, 0, 16, 16]},
"north": {"uv": [0, 0, 16, 16]},
"south": {"uv": [0, 0, 16, 16]},
"west": {"uv": [0, 0, 16, 16]},
"east": {"uv": [0, 0, 16, 16]}
}
}
}
}
},
"textures": {
"minosoft:dummy_texture": {
"resolution": [128, 128]
},
"minosoft:second_texture": {
"resolution": [1024, 1024]
}
},
"animations": {
"open": {
"loop": "hold",
"length": 0.3,
"animators": [
{
"element": "body.head",
"keyframes": [
{
"type": "rotate",
"interpolation": "sine",
"keyframes": {
"0.0": [0, 0, 0],
"0.3": [-90, 0, 0]
}
}
]
}
]
}
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -16,9 +16,9 @@ package de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player
import de.bixilon.kotlinglm.func.deg import de.bixilon.kotlinglm.func.deg
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.entities.entities.player.Arms import de.bixilon.minosoft.data.entities.entities.player.Arms
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.AnimationLoops import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.AnimationLoops
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.KeyframeChannels import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.animator.keyframes.KeyframeChannels
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.outliner.SkeletalOutliner
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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,7 +14,7 @@
package de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player package de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player
import de.bixilon.kutil.math.interpolation.FloatInterpolation.interpolateLinear import de.bixilon.kutil.math.interpolation.FloatInterpolation.interpolateLinear
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.CustomSkeletalAnimation import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.CustomSkeletalAnimation
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
abstract class ExtremitiesAnimator( abstract class ExtremitiesAnimator(

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -15,9 +15,9 @@ package de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player
import de.bixilon.kotlinglm.func.deg import de.bixilon.kotlinglm.func.deg
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.AnimationLoops import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.AnimationLoops
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.KeyframeChannels import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.animator.keyframes.KeyframeChannels
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.outliner.SkeletalOutliner
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos

View File

@ -24,8 +24,8 @@ import de.bixilon.minosoft.gui.rendering.entity.EntityRenderer
import de.bixilon.minosoft.gui.rendering.entity.models.SkeletalEntityModel import de.bixilon.minosoft.gui.rendering.entity.models.SkeletalEntityModel
import de.bixilon.minosoft.gui.rendering.models.loader.ModelLoader.Companion.bbModel import de.bixilon.minosoft.gui.rendering.models.loader.ModelLoader.Companion.bbModel
import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.SkeletalAnimation
import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.elements.SkeletalElement
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback 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.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -16,10 +16,10 @@ package de.bixilon.minosoft.gui.rendering.framebuffer.world.overlay.overlays.arm
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.entities.entities.player.Arms import de.bixilon.minosoft.data.entities.entities.player.Arms
import de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player.PlayerModel import de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player.PlayerModel
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.AnimationLoops import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.AnimationLoops
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.SkeletalAnimation
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.KeyframeChannels import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.animations.animator.keyframes.KeyframeChannels
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner import de.bixilon.minosoft.gui.rendering.skeletal.model.legacy.outliner.SkeletalOutliner
class FirstPersonArmAnimator(private val player: PlayerModel) : SkeletalAnimation { class FirstPersonArmAnimator(private val player: PlayerModel) : SkeletalAnimation {
override val name: String = "" override val name: String = ""

View File

@ -14,28 +14,22 @@
package de.bixilon.minosoft.gui.rendering.gui.mesh package de.bixilon.minosoft.gui.rendering.gui.mesh
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties
import de.bixilon.minosoft.gui.rendering.system.base.texture.TexturePart import de.bixilon.minosoft.gui.rendering.system.base.texture.TexturePart
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderIdentifiable import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
interface GUIVertexConsumer { interface GUIVertexConsumer {
val order: IntArray val order: IntArray
fun addVertex(position: Vec2, texture: ShaderIdentifiable?, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) fun addVertex(position: Vec2, texture: ShaderTexture?, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?)
fun addVertex(position: Vec2i, texture: ShaderIdentifiable?, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
addVertex(Vec2(position), texture, uv, tint, options) fun addQuad(start: Vec2, end: Vec2, texture: ShaderTexture?, uvStart: Vec2 = UV_START, uvEnd: Vec2 = UV_END, tint: RGBColor, options: GUIVertexOptions?) {
} val uvStart = texture?.transformUV(uvStart) ?: uvStart
val uvEnd = texture?.transformUV(uvEnd) ?: uvEnd
fun addQuad(start: Vec2, end: Vec2, texture: Texture?, uvStart: Vec2 = UV_START, uvEnd: Vec2 = UV_END, tint: RGBColor, options: GUIVertexOptions?) {
val uvStart = texture?.renderData?.transformUV(uvStart) ?: uvStart
val uvEnd = texture?.renderData?.transformUV(uvEnd) ?: uvEnd
addQuad(start, end, texture as ShaderIdentifiable?, uvStart, uvEnd, tint, options)
}
fun addQuad(start: Vec2, end: Vec2, texture: ShaderIdentifiable?, uvStart: Vec2 = UV_START, uvEnd: Vec2 = UV_END, tint: RGBColor, options: GUIVertexOptions?) {
val positions = arrayOf( val positions = arrayOf(
start, start,
Vec2(end.x, start.y), Vec2(end.x, start.y),

View File

@ -59,7 +59,7 @@ class SkeletalManager(
} }
private companion object { private companion object {
private const val TRANSFORMS = 32 private const val TRANSFORMS = 128
private const val MAT4_SIZE = 4 * 4 private const val MAT4_SIZE = 4 * 4
} }
} }

View File

@ -0,0 +1,45 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.bake
import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalMesh
import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance
data class BakedSkeletalModel(
val mesh: SkeletalMesh,
val transforms: MutableMap<String, SkeletalTransform>,
// TODO: animations
) {
private var state = SkeletalModelStates.DECLARED
fun load() {
if (state != SkeletalModelStates.DECLARED) throw IllegalStateException("Can not load model!")
mesh.load()
state = SkeletalModelStates.LOADED
}
fun unload() {
if (state != SkeletalModelStates.LOADED) throw IllegalStateException("Can not unload model!")
mesh.unload()
state = SkeletalModelStates.UNLOADED
}
fun createInstance(context: RenderContext, position: Vec3, transform: Mat4 = Mat4()): SkeletalInstance {
}
}

View File

@ -0,0 +1,45 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.bake
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalVertexConsumer
import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement
import de.bixilon.minosoft.gui.rendering.skeletal.model.textures.SkeletalTextureInstance
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import java.util.concurrent.atomic.AtomicInteger
data class SkeletalBakeContext(
val offset: Vec3 = Vec3.EMPTY,
val rotation: Vec3 = Vec3.EMPTY,
val inflate: Float = 0.0f,
val transparency: Boolean = true,
val texture: ResourceLocation? = null,
val textures: Map<ResourceLocation, SkeletalTextureInstance>,
val transform: AtomicInteger = AtomicInteger(),
val consumer: SkeletalVertexConsumer,
) {
fun copy(element: SkeletalElement): SkeletalBakeContext {
val offset = this.offset + element.pivot
val rotation = this.rotation + element.rotation
val inflate = this.inflate + element.inflate
val transparency = this.transparency && element.transparency
val texture = element.texture ?: texture
return copy(offset = offset, rotation = rotation, inflate = inflate, transparency = transparency, texture = texture)
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,11 +11,10 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.baked package de.bixilon.minosoft.gui.rendering.skeletal.bake
enum class SkeletalModelStates { enum class SkeletalModelStates {
DECLARED, DECLARED,
PRE_LOADED,
LOADED, LOADED,
UNLOADED, UNLOADED,
; ;

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,9 +11,9 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.resolution package de.bixilon.minosoft.gui.rendering.skeletal.bake
data class SkeletalResolution( class SkeletalTransform(
val width: Int = 16, val id: Int,
val height: Int = 16, val children: Map<String, SkeletalTransform>,
) )

View File

@ -1,101 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.baked
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement.Companion.BLOCK_SIZE
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.shader.ShaderTexture
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import java.util.*
class BakedSkeletalModel(
val model: SkeletalModel,
val textures: Int2ObjectOpenHashMap<ShaderTexture>,
) {
lateinit var mesh: SkeletalMesh
var state: SkeletalModelStates = SkeletalModelStates.DECLARED
private set
private fun calculateOutlinerMapping(): Map<UUID, Int> {
val mapping: Object2IntOpenHashMap<UUID> = Object2IntOpenHashMap()
var offset = 0
fun addOutliner(child: Any, parentId: Int) {
if (child is UUID) {
mapping[child] = parentId
return
}
if (child !is SkeletalOutliner) {
throw IllegalArgumentException()
}
if (mapping.containsKey(child.uuid)) {
return
}
val id = offset++
mapping[child.uuid] = id
for (childChild in child.children) {
addOutliner(childChild, id)
}
}
for (outliner in this.model.outliner) {
addOutliner(outliner, -1)
}
return mapping
}
fun preload(context: RenderContext) {
check(state == SkeletalModelStates.DECLARED) { "Can not preload model in $state" }
val mesh = SkeletalMesh(context, 1000)
val outlinerMapping = calculateOutlinerMapping()
for (element in model.elements) {
element.bake(model, textures, outlinerMapping, mesh)
}
this.mesh = mesh
state = SkeletalModelStates.PRE_LOADED
}
fun load() {
check(state == SkeletalModelStates.PRE_LOADED) { "Can not load model in state: $state" }
mesh.load()
state = SkeletalModelStates.LOADED
}
fun unload() {
check(state == SkeletalModelStates.LOADED) { "Can not unload model in state $state" }
mesh.unload()
state = SkeletalModelStates.UNLOADED
}
companion object {
fun Vec3.fromBlockCoordinates(): Vec3 {
return Vec3(this.x.toBlockCoordinate(), this.y / BLOCK_SIZE, this.z.toBlockCoordinate())
}
inline fun Float.toBlockCoordinate(): Float {
return this / BLOCK_SIZE + 0.5f
}
}
}

View File

@ -13,136 +13,8 @@
package de.bixilon.minosoft.gui.rendering.skeletal.instance package de.bixilon.minosoft.gui.rendering.skeletal.instance
import de.bixilon.kotlinglm.func.rad import de.bixilon.minosoft.gui.rendering.skeletal.bake.BakedSkeletalModel
import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.renderer.drawable.DeltaDrawable
import de.bixilon.minosoft.gui.rendering.skeletal.baked.BakedSkeletalModel
import de.bixilon.minosoft.gui.rendering.skeletal.baked.SkeletalModelStates
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import java.util.*
class SkeletalInstance( class SkeletalInstance(
val context: RenderContext,
val model: BakedSkeletalModel, val model: BakedSkeletalModel,
position: Vec3 = Vec3.EMPTY, )
transform: Mat4 = Mat4(),
) {
private var baseTransform = Mat4().translateAssign(position) * transform
private var previousBaseTransform = baseTransform
private var animations: MutableList<SkeletalAnimationInstance> = mutableListOf()
private var transforms: List<Mat4> = emptyList()
private var lastDraw = -1L
var light: Int = 0xFF
fun getAnimation(name: String): SkeletalAnimation? {
for (animation in model.model.animations) {
if (animation.name != name) {
continue
}
return animation
}
return null
}
fun playAnimation(animation: SkeletalAnimation) {
val instance = SkeletalAnimationInstance(animation)
animations.removeAll { it.animation == animation }
animations += instance
}
fun playAnimation(name: String) {
playAnimation(getAnimation(name) ?: throw IllegalArgumentException("Can not find animation $name"))
}
fun clearAnimation() {
animations.clear()
}
fun draw() {
context.skeletal.draw(this, light)
}
fun updatePosition(position: Vec3d, rotation: EntityRotation) {
val matrix = Mat4()
.translateAssign(Vec3(position - context.camera.offset.offset))
.rotateAssign((EntityRotation.HALF_CIRCLE_DEGREE - rotation.yaw).rad, Y_ROTATION_VECTOR)
.translateAssign(CENTER_OFFSET) // move to bottom center
if (baseTransform != matrix) {
baseTransform = matrix
}
}
fun calculateTransforms(base: Mat4 = this.baseTransform): List<Mat4> {
val time = millis()
if (animations.isNotEmpty()) {
val iterator = animations.iterator()
for (animation in iterator) {
animation.draw(time)
if (animation.canClear()) {
iterator.remove()
}
}
}
if (animations.isEmpty()) {
if (this.transforms.isNotEmpty() && base === previousBaseTransform) {
return this.transforms
}
}
val delta = time - lastDraw
for (instance in animations) {
val animation = instance.animation
if (animation is DeltaDrawable) {
animation.draw(delta)
}
}
val transforms: MutableList<Mat4> = mutableListOf()
for (outliner in model.model.outliner) {
calculateTransform(base, animations, outliner, transforms)
}
this.transforms = transforms
this.previousBaseTransform = base
return transforms
}
private fun calculateTransform(transform: Mat4, animations: List<SkeletalAnimationInstance>, outliner: Any /* UUID or SkeletalOutliner */, transforms: MutableList<Mat4>) {
if (outliner is UUID) {
return
}
check(outliner is SkeletalOutliner)
val skeletalTransform = Mat4(transform)
for (animation in this.animations) {
skeletalTransform *= animation.animation.calculateTransform(outliner, animation.time)
}
transforms += skeletalTransform
for (child in outliner.children) {
calculateTransform(skeletalTransform, animations, child, transforms)
}
}
fun unload() {
val model = model
if (model.state == SkeletalModelStates.LOADED) {
model.unload()
}
}
companion object {
private val CENTER_OFFSET = Vec3(-0.5, 0.0f, -0.5)
private val Y_ROTATION_VECTOR = Vec3(0, 1, 0)
}
}

View File

@ -13,38 +13,47 @@
package de.bixilon.minosoft.gui.rendering.skeletal.model package de.bixilon.minosoft.gui.rendering.skeletal.model
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.skeletal.baked.BakedSkeletalModel import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalMesh
import de.bixilon.minosoft.gui.rendering.skeletal.bake.BakedSkeletalModel
import de.bixilon.minosoft.gui.rendering.skeletal.bake.SkeletalTransform
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation
import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement
import de.bixilon.minosoft.gui.rendering.skeletal.model.meta.SkeletalMeta
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner
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.skeletal.model.textures.SkeletalTexture
import de.bixilon.minosoft.gui.rendering.skeletal.model.textures.SkeletalTextureInstance
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
data class SkeletalModel( data class SkeletalModel(
val meta: SkeletalMeta = SkeletalMeta(), val elements: Map<String, SkeletalElement>,
val name: String = "empty", val textures: Map<ResourceLocation, SkeletalTexture>,
val resolution: SkeletalResolution = SkeletalResolution(), val animations: Map<ResourceLocation, SkeletalAnimation>,
val elements: List<SkeletalElement> = emptyList(),
val outliner: List<SkeletalOutliner> = emptyList(),
val textures: List<SkeletalTexture> = emptyList(),
val animations: List<SkeletalAnimation> = emptyList(),
) { ) {
val loadedTextures: MutableMap<ResourceLocation, SkeletalTextureInstance> = mutableMapOf()
fun bake(context: RenderContext, textureOverride: Map<Int, ShaderTexture>): BakedSkeletalModel { fun load(context: RenderContext) {
val textures: Int2ObjectOpenHashMap<ShaderTexture> = Int2ObjectOpenHashMap() for ((name, properties) in this.textures) {
for (entry in this.textures) { if (!properties.load) continue
val override = textureOverride[entry.id] context.textures.staticTextures.createTexture(name.texture())
if (override != null) {
textures[entry.id] = override
continue
}
val texture = context.textures.staticTextures.createTexture(entry.resourceLocation)
textures[entry.id] = texture
} }
return BakedSkeletalModel(this, textures) }
fun bake(context: RenderContext, override: Map<ResourceLocation, ShaderTexture>): BakedSkeletalModel {
val mesh = SkeletalMesh(context, 1000)
val textures: MutableMap<ResourceLocation, SkeletalTextureInstance> = this.loadedTextures.toMutableMap()
for ((name, texture) in override) {
textures[name]?.texture = texture
}
val baked: MutableMap<String, SkeletalTransform> = mutableMapOf()
for ((name, element) in elements) {
baked[name] = element.bake(mesh, textures) ?: continue
}
return BakedSkeletalModel(mesh, baked)
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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,78 +13,11 @@
package de.bixilon.minosoft.gui.rendering.skeletal.model.animations package de.bixilon.minosoft.gui.rendering.skeletal.model.animations
import com.fasterxml.jackson.databind.annotation.JsonDeserialize import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.AnimationLoops
import de.bixilon.kotlinglm.func.rad import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.SkeletalAnimator
import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.skeletal.baked.BakedSkeletalModel.Companion.fromBlockCoordinates
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.KeyframeChannels
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY_INSTANCE
@JsonDeserialize(`as` = StaticSkeletalAnimation::class) class SkeletalAnimation(
interface SkeletalAnimation { val loop: AnimationLoops,
val name: String val length: Float,
val loop: AnimationLoops val animators: List<SkeletalAnimator>
val length: Float )
fun get(channel: KeyframeChannels, outliner: SkeletalOutliner, time: Float): Vec3?
private fun tweakTime(time: Float): Float {
when (loop) {
AnimationLoops.LOOP -> return time % length
AnimationLoops.ONCE -> {
if (time > length) {
return 0.0f
}
}
AnimationLoops.HOLD -> {
if (time > length) {
return length
}
}
}
return time
}
fun calculateTransform(outliner: SkeletalOutliner, animationTime: Float): Mat4 {
val transform = Mat4()
val tweakedTime = tweakTime(animationTime)
val rotation = get(KeyframeChannels.ROTATION, outliner, tweakedTime)
if (rotation != null && rotation != Vec3.EMPTY_INSTANCE) {
transform.translateAssign(outliner.origin.fromBlockCoordinates())
transform.rotateAssign(-rotation.x.rad, Vec3(1, 0, 0))
transform.rotateAssign(-rotation.y.rad, Vec3(0, 1, 0))
transform.rotateAssign(-rotation.z.rad, Vec3(0, 0, 1))
transform.translateAssign(-outliner.origin.fromBlockCoordinates())
}
val scale = get(KeyframeChannels.SCALE, outliner, tweakedTime)
if (scale != null && (scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f)) {
transform.scaleAssign(scale)
}
val position = get(KeyframeChannels.POSITION, outliner, tweakedTime)
if (position != null && position != Vec3.EMPTY_INSTANCE) {
transform[3, 0] += position.x
transform[3, 1] += position.y
transform[3, 2] += position.z
}
return transform
}
fun canClear(animationTime: Float): Boolean {
if (loop == AnimationLoops.LOOP) {
return false
}
if (animationTime < length) {
return false
}
if (loop == AnimationLoops.ONCE) {
return true
}
// ToDo: Check HOLD
return false
}
}

View File

@ -1,35 +0,0 @@
/*
* 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.animations
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.SkeletalAnimator
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.KeyframeChannels
import de.bixilon.minosoft.gui.rendering.skeletal.model.outliner.SkeletalOutliner
import java.util.*
data class StaticSkeletalAnimation(
val uuid: UUID,
override val name: String,
override val loop: AnimationLoops = AnimationLoops.LOOP,
override val length: Float,
val animators: Map<UUID, SkeletalAnimator>,
) : SkeletalAnimation {
override fun get(channel: KeyframeChannels, outliner: SkeletalOutliner, time: Float): Vec3? {
val animator = animators[outliner.uuid] ?: return null
return animator.get(channel, time)
}
}

View File

@ -1,55 +0,0 @@
/*
* 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.animations.animator
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.KeyframeChannels
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes.SkeletalKeyframe
data class SkeletalAnimator(
val name: String,
val type: String, // ToDo: enum
val keyframes: List<SkeletalKeyframe>,
) {
fun get(channel: KeyframeChannels, time: Float): Vec3? {
var firstKeyframe: SkeletalKeyframe? = null
var secondKeyframe: SkeletalKeyframe? = null
for (keyframe in keyframes) {
if (keyframe.channel != channel) {
continue
}
if (firstKeyframe == null) {
firstKeyframe = keyframe
continue
}
if (time <= keyframe.time) {
if (secondKeyframe != null) {
firstKeyframe = secondKeyframe
}
secondKeyframe = keyframe
continue
}
break
}
if (firstKeyframe == null || secondKeyframe == null) {
return null
}
val delta = (time - firstKeyframe.time) / (secondKeyframe.time - firstKeyframe.time)
return firstKeyframe.interpolateDataWith(secondKeyframe.dataPoints, delta)
}
}

View File

@ -1,41 +0,0 @@
/*
* 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.animations.animator.keyframes
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateLinear
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateSine
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.toVec3
import java.util.*
data class SkeletalKeyframe(
val channel: KeyframeChannels,
val dataPoints: List<Map<String, Any>>,
val uuid: UUID,
val time: Float,
val interpolation: KeyframeInterpolations = KeyframeInterpolations.LINEAR,
) {
fun interpolateDataWith(other: List<Map<String, Any>>, delta: Float): Vec3 {
val value1 = dataPoints[0].toVec3()
val value2 = other[0].toVec3()
return when (interpolation) {
KeyframeInterpolations.LINEAR -> interpolateLinear(delta, value1, value2)
KeyframeInterpolations.SINE -> interpolateSine(delta, value1, value2)
// ToDo
else -> interpolateSine(delta, value1, value2)
}
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,7 +11,7 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.animations package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators
enum class AnimationLoops { enum class AnimationLoops {
ONCE, ONCE,

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,11 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators
enum class KeyframeChannels { import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.keyframes.SkeletalKeyframe
ROTATION,
POSITION, data class SkeletalAnimator(
SCALE, val element: String,
; val keyframes: List<SkeletalKeyframe>,
} )

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,12 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animator.keyframes package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.keyframes
enum class KeyframeInterpolations { enum class KeyframeInterpolation {
NONE,
LINEAR, LINEAR,
CATMULLROM,
STEP,
SINE, SINE,
; ;
} }

View File

@ -0,0 +1,27 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.animations.animators.keyframes
import de.bixilon.kotlinglm.vec3.Vec3
data class RotateKeyframe(
val interpolation: KeyframeInterpolation = KeyframeInterpolation.NONE,
val keyframes: Map<Float, Vec3>,
) : SkeletalKeyframe {
override val type get() = TYPE
companion object {
const val TYPE = "rotate"
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,10 +11,17 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.meta package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.keyframes
enum class ModelFormats { import de.bixilon.kotlinglm.vec3.Vec3
FREE,
SKIN, data class ScaleKeyframe(
; val interpolation: KeyframeInterpolation = KeyframeInterpolation.NONE,
val keyframes: Map<Float, Vec3>,
) : SkeletalKeyframe {
override val type get() = TYPE
companion object {
const val TYPE = "scale"
}
} }

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,25 +11,19 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.instance package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.keyframes
import de.bixilon.minosoft.gui.rendering.skeletal.model.animations.SkeletalAnimation import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
class SkeletalAnimationInstance( @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
var animation: SkeletalAnimation, @JsonSubTypes(
) { JsonSubTypes.Type(value = RotateKeyframe::class, name = RotateKeyframe.TYPE),
var time = 0.0f JsonSubTypes.Type(value = TintKeyframe::class, name = TintKeyframe.TYPE),
var lastFrame = -1L JsonSubTypes.Type(value = TranslateKeyframe::class, name = TranslateKeyframe.TYPE),
JsonSubTypes.Type(value = ScaleKeyframe::class, name = ScaleKeyframe.TYPE),
)
fun draw(time: Long) { interface SkeletalKeyframe {
if (lastFrame > 0L) { val type: String
val delta = time - lastFrame
this.time += delta / 1000.0f
}
lastFrame = time
}
fun canClear(): Boolean {
return animation.canClear(time)
}
} }

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,21 +11,25 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.animations package de.bixilon.minosoft.gui.rendering.skeletal.model.animations.animators.keyframes
import de.bixilon.minosoft.gui.rendering.renderer.drawable.DeltaDrawable import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
abstract class CustomSkeletalAnimation(override val name: String) : SkeletalAnimation, DeltaDrawable { data class TintKeyframe(
protected var lastTick = -1L val interpolation: KeyframeInterpolation = KeyframeInterpolation.NONE,
val channel: ColorChannel = ColorChannel.RGB,
val keyframes: Map<Float, RGBColor>,
) : SkeletalKeyframe {
override val type get() = TYPE
open fun tick() = Unit enum class ColorChannel {
RGB,
// TODO: hsv?
;
}
override fun draw(millis: Long) { companion object {
if (millis - lastTick > ProtocolDefinition.TICK_TIME) { const val TYPE = "tint"
tick()
lastTick = millis
}
} }
} }

View File

@ -0,0 +1,27 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.animations.animators.keyframes
import de.bixilon.kotlinglm.vec3.Vec3
data class TranslateKeyframe(
val interpolation: KeyframeInterpolation = KeyframeInterpolation.NONE,
val keyframes: Map<Float, Vec3>,
) : SkeletalKeyframe {
override val type get() = TYPE
companion object {
const val TYPE = "translate"
}
}

View File

@ -13,45 +13,53 @@
package de.bixilon.minosoft.gui.rendering.skeletal.model.elements package de.bixilon.minosoft.gui.rendering.skeletal.model.elements
import com.sun.marlin.MarlinConst.BLOCK_SIZE
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalVertexConsumer import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.skeletal.model.SkeletalModel import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalMesh
import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.faces.SkeletalFace import de.bixilon.minosoft.gui.rendering.skeletal.bake.SkeletalBakeContext
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture import de.bixilon.minosoft.gui.rendering.skeletal.bake.SkeletalTransform
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY import de.bixilon.minosoft.gui.rendering.skeletal.model.textures.SkeletalTextureInstance
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.ONE
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import java.util.*
data class SkeletalElement( data class SkeletalElement(
val name: String, val from: Vec3,
val rescale: Boolean = false, val to: Vec3,
val visible: Boolean = true, val origin: Vec3 = (from - to) / 2.0f,
val from: Vec3 = Vec3.EMPTY, val pivot: Vec3 = Vec3.EMPTY,
val to: Vec3 = Vec3.ONE,
val rotation: Vec3 = Vec3.EMPTY, val rotation: Vec3 = Vec3.EMPTY,
val origin: Vec3 = Vec3.EMPTY,
val uvOffset: Vec2 = Vec2.EMPTY,
val faces: Map<Directions, SkeletalFace> = emptyMap(),
val uuid: UUID,
val inflate: Float = 0.0f, val inflate: Float = 0.0f,
val transparency: Boolean = true, val transparency: Boolean = true,
val enabled: Boolean = true,
val texture: ResourceLocation? = null,
val transform: Boolean = true,
val faces: Map<Directions, SkeletalFace>,
val children: Map<String, SkeletalElement>,
) { ) {
fun bake(model: SkeletalModel, textures: Int2ObjectOpenHashMap<ShaderTexture>, outlinerMapping: Map<UUID, Int>, consumer: SkeletalVertexConsumer) { fun bake(mesh: SkeletalMesh, textures: Map<ResourceLocation, SkeletalTextureInstance>): SkeletalTransform? {
if (!visible) { if (!enabled) return null
return
}
val outlinerId = outlinerMapping[uuid] ?: 0 val context = SkeletalBakeContext(textures = textures, consumer = mesh)
return bake(context)
}
private fun bake(context: SkeletalBakeContext): SkeletalTransform? {
if (!enabled) return null
val context = context.copy(this)
val transform = if (!this.transform) context.transform.get() else context.transform.getAndIncrement()
val transforms: MutableMap<String, SkeletalTransform> = mutableMapOf()
val inflate = (inflate / BLOCK_SIZE) / 2
for ((direction, face) in faces) { for ((direction, face) in faces) {
face.bake(model, this, direction, inflate, outlinerId, textures, consumer) face.bake(context, direction, this, transform)
} }
for ((name, child) in children) {
transforms[name] = child.bake(context) ?: continue
}
if (!this.transform) return null
return SkeletalTransform(transform, transforms)
} }
} }

View File

@ -0,0 +1,67 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.elements
import de.bixilon.kotlinglm.GLM
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.models.block.element.face.FaceUV
import de.bixilon.minosoft.gui.rendering.models.block.legacy.ModelBakeUtil
import de.bixilon.minosoft.gui.rendering.skeletal.bake.SkeletalBakeContext
import de.bixilon.minosoft.gui.rendering.system.base.MeshUtil.buffer
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotateAssign
data class SkeletalFace(
val uv: FaceUV,
val texture: ResourceLocation? = null,
) {
fun bake(context: SkeletalBakeContext, direction: Directions, element: SkeletalElement, transform: Int) {
val positions = direction.getPositions(context.offset + (element.from / 16.0f - context.inflate), context.offset + (element.to / 16.0f + context.inflate))
val texture = context.textures[texture ?: context.texture ?: throw IllegalStateException("element has no texture set!")] ?: throw IllegalStateException("Texture not found!")
val texturePositions = ModelBakeUtil.getTextureCoordinates(uv.start / texture.properties.resolution, uv.end / texture.properties.resolution)
val origin = element.origin / 16.0f
element.rotation.let {
val rad = -GLM.radians(it)
for ((index, position) in positions.withIndex()) {
val out = Vec3(position)
out.rotateAssign(rad[0], Axes.X, origin, false)
out.rotateAssign(rad[1], Axes.Y, origin, false)
out.rotateAssign(rad[2], Axes.Z, origin, false)
positions[index] = out
}
}
var flags = 0
if (context.transparency) {
flags = flags or 0x01
}
val transform = transform.buffer()
val textureShaderId = texture.texture.shaderId.buffer()
val floatFlags = flags.buffer()
for (index in 0 until context.consumer.order.size step 2) {
val indexPosition = positions[context.consumer.order[index]].array
val transformedUV = texture.texture.transformUV(texturePositions[context.consumer.order[index + 1]])
context.consumer.addVertex(indexPosition, transformedUV, transform, textureShaderId, floatFlags)
}
}
}

View File

@ -1,78 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.elements.faces
import de.bixilon.kotlinglm.GLM
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec4.Vec4
import de.bixilon.kotlinglm.vec4.swizzle.xy
import de.bixilon.kotlinglm.vec4.swizzle.zw
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.models.block.legacy.ModelBakeUtil
import de.bixilon.minosoft.gui.rendering.skeletal.SkeletalVertexConsumer
import de.bixilon.minosoft.gui.rendering.skeletal.baked.BakedSkeletalModel.Companion.fromBlockCoordinates
import de.bixilon.minosoft.gui.rendering.skeletal.model.SkeletalModel
import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement
import de.bixilon.minosoft.gui.rendering.system.base.MeshUtil.buffer
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotateAssign
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
data class SkeletalFace(
val uv: Vec4,
val texture: Int,
val transparency: Boolean = true,
) {
val uvStart = uv.xy
val uvEnd = uv.zw
fun bake(model: SkeletalModel, element: SkeletalElement, direction: Directions, inflate: Float, outlinerId: Int, textures: Int2ObjectOpenHashMap<ShaderTexture>, consumer: SkeletalVertexConsumer) {
val positions = direction.getPositions(element.from.fromBlockCoordinates() - inflate, element.to.fromBlockCoordinates() + inflate)
val uvDivider = Vec2(model.resolution.width, model.resolution.height)
val texturePositions = ModelBakeUtil.getTextureCoordinates(uvStart / uvDivider, uvEnd / uvDivider)
val origin = element.origin.fromBlockCoordinates()
element.rotation.let {
val rad = -GLM.radians(it)
for ((index, position) in positions.withIndex()) {
val out = Vec3(position)
out.rotateAssign(rad[0], Axes.X, origin, element.rescale)
out.rotateAssign(rad[1], Axes.Y, origin, element.rescale)
out.rotateAssign(rad[2], Axes.Z, origin, element.rescale)
positions[index] = out
}
}
var flags = 0
if (element.transparency && transparency) {
flags = flags or 0x01
}
val texture = textures[texture]!!
val transform = outlinerId.buffer()
val textureShaderId = texture.shaderId.buffer()
val floatFlags = flags.buffer()
for (index in 0 until consumer.order.size step 2) {
val indexPosition = positions[consumer.order[index]].array
val transformedUV = texture.transformUV(texturePositions[consumer.order[index + 1]])
consumer.addVertex(indexPosition, transformedUV, transform, textureShaderId, floatFlags)
}
}
}

View File

@ -1,45 +0,0 @@
/*
* 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.outliner
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.TextNode
import com.fasterxml.jackson.module.kotlin.convertValue
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.util.json.Jackson
import java.util.*
class SkeletalOutliner(
val name: String,
val origin: Vec3 = Vec3.EMPTY,
val uuid: UUID,
children: List<JsonNode>,
) {
val children: List<Any> // List<UUID | SkeletalOutliner>
init {
val _children: MutableList<Any> = mutableListOf()
for (child in children) {
_children += if (child is TextNode) {
Jackson.MAPPER.convertValue<UUID>(child)
} else {
Jackson.MAPPER.convertValue<SkeletalOutliner>(child)
}
}
this.children = _children
}
}

View File

@ -13,18 +13,9 @@
package de.bixilon.minosoft.gui.rendering.skeletal.model.textures package de.bixilon.minosoft.gui.rendering.skeletal.model.textures
import de.bixilon.minosoft.data.registries.identified.Namespaces import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import java.util.*
// ToDo: This does not follow the specification
data class SkeletalTexture( data class SkeletalTexture(
val path: String, val resolution: Vec2i,
val name: String, val load: Boolean = true,
val namespace: String = Namespaces.DEFAULT, )
val id: Int,
val uuid: UUID,
) {
val resourceLocation = ResourceLocation(namespace, path).texture()
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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 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.
* *
@ -11,9 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.skeletal.model.meta package de.bixilon.minosoft.gui.rendering.skeletal.model.textures
data class SkeletalMeta( import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
val formatVersion: String = "4.0",
val modelFormat: ModelFormats = ModelFormats.FREE, data class SkeletalTextureInstance(
val properties: SkeletalTexture,
var texture: ShaderTexture,
) )