mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-20 22:26:24 -04:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa7a90ad02 | ||
|
|
5d1f0804d5 | ||
|
|
5b01754a82 | ||
|
|
6630ee942c | ||
|
|
3b7bd4d810 | ||
|
|
81278e2d16 | ||
|
|
3af66e0028 | ||
|
|
75ead2f665 |
+270
-61
@@ -11,7 +11,14 @@ package com.cobblemon.mod.common.client.render
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseTransitionAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockPoseAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
@@ -21,10 +28,13 @@ import com.cobblemon.mod.common.util.toRGBA
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.mojang.blaze3d.platform.NativeImage
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture
|
||||
import kotlin.math.floor
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.joml.Vector2d
|
||||
import org.joml.Vector3f
|
||||
import org.joml.Vector4f
|
||||
|
||||
@@ -53,7 +63,7 @@ class VaryingRenderableResolver(
|
||||
}
|
||||
|
||||
fun getResolvedTexture(state: PosableState): ResourceLocation {
|
||||
return getVariationValue(state) { texture }?.invoke(state)
|
||||
return getVariationValue(state) { texture }?.getTexture(state)
|
||||
?: throw IllegalStateException("Unable to find a texture for $name with aspects ${state.currentAspects.joinToString()}. This shouldn't be possible if you've defined the fallback variation.")
|
||||
}
|
||||
|
||||
@@ -180,6 +190,13 @@ class ModelAssetVariation(
|
||||
}
|
||||
}
|
||||
|
||||
/** Needed a custom one, so I can make it take in a dynamic texture instead of resource location */
|
||||
class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTextureStateShard(Runnable {
|
||||
RenderSystem.setShaderTexture(0, texture.id)
|
||||
}, Runnable {
|
||||
texture.close() // Cleanup
|
||||
})
|
||||
|
||||
/**
|
||||
* Given the animation seconds, returns a texture to use. Only implemented
|
||||
* by [StaticModelTextureSupplier] and [AnimatedModelTextureSupplier].
|
||||
@@ -187,12 +204,17 @@ class ModelAssetVariation(
|
||||
* @author Hiroku
|
||||
* @since February 6th, 2023
|
||||
*/
|
||||
fun interface ModelTextureSupplier {
|
||||
operator fun invoke(state: PosableState): ResourceLocation
|
||||
interface ModelTextureSupplier {
|
||||
operator fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard
|
||||
fun getTexture(state: PosableState): ResourceLocation
|
||||
}
|
||||
|
||||
class StaticModelTextureSupplier(val texture: ResourceLocation): ModelTextureSupplier {
|
||||
override fun invoke(state: PosableState): ResourceLocation {
|
||||
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
|
||||
return RenderStateShard.TextureStateShard(texture, false, false)
|
||||
}
|
||||
|
||||
override fun getTexture(state: PosableState): ResourceLocation {
|
||||
return texture
|
||||
}
|
||||
}
|
||||
@@ -203,80 +225,196 @@ class AnimatedModelTextureSupplier(
|
||||
val frames: List<ResourceLocation>,
|
||||
val interpolation: Boolean
|
||||
): ModelTextureSupplier {
|
||||
override fun invoke(state: PosableState): ResourceLocation {
|
||||
val frameIndex = floor(state.animationSeconds * fps).toInt()
|
||||
return if (frameIndex >= frames.size && !loop) {
|
||||
frames.last()
|
||||
} else {
|
||||
frames[frameIndex % frames.size]
|
||||
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
|
||||
var frameIndex = state.animationSeconds * fps
|
||||
if (frameIndex > frames.size - 1.0f && !loop) frameIndex = frames.size.toFloat() - 1.0f
|
||||
val i = frameIndex % 1.0
|
||||
|
||||
if (interpolation && i != 0.0) {
|
||||
val o = floor(frameIndex).toInt() % frames.size
|
||||
val s = if (o == frames.size - 1) 0 else o + 1
|
||||
|
||||
val resourceManager = Minecraft.getInstance().resourceManager
|
||||
val texture1 = NativeImage.read(resourceManager.getResourceOrThrow(frames[o]).open())
|
||||
val texture2 = NativeImage.read(resourceManager.getResourceOrThrow(frames[s]).open())
|
||||
|
||||
val image = getInterpolatedTexture(texture1, texture2, i)
|
||||
val newTexture = DynamicTexture(image)
|
||||
newTexture.setFilter(false, false)
|
||||
return DynamicStateShard(newTexture)
|
||||
}
|
||||
else {
|
||||
val frame = floor(frameIndex).toInt() % frames.size
|
||||
return RenderStateShard.TextureStateShard(frames[frame], false, false)
|
||||
}
|
||||
}
|
||||
|
||||
fun interpolatedTexture(state: PosableState): DynamicTexture? {
|
||||
override fun getTexture(state: PosableState): ResourceLocation {
|
||||
val frameIndex = floor(state.animationSeconds * fps).toInt()
|
||||
return frames[frameIndex]
|
||||
}
|
||||
}
|
||||
|
||||
class KeyframableTextureSupplier(val textures: MutableList<ResourceLocation>) : ModelTextureSupplier {
|
||||
var layerName = ""
|
||||
|
||||
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
|
||||
val resourceManager = Minecraft.getInstance().resourceManager
|
||||
|
||||
val frameIndex = floor(state.animationSeconds * fps).toInt()
|
||||
try {
|
||||
if (frameIndex >= frames.size && !loop) {
|
||||
return DynamicTexture(NativeImage.read(resourceManager.getResourceOrThrow(frames.last()).open()))
|
||||
if (state.primaryAnimation?.animation is PoseTransitionAnimation) {
|
||||
val poseTransition = state.primaryAnimation!!.animation as PoseTransitionAnimation
|
||||
|
||||
val (animation1, startTime1) = getMainAnimFromPose(poseTransition.beforePose, state, true)
|
||||
val (animation2, startTime2) = getMainAnimFromPose(poseTransition.afterPose, state, true)
|
||||
|
||||
val texture2 = if (animation2 != null) {
|
||||
getImageFromAnimation(animation2, state.animationSeconds - startTime2)
|
||||
} else {
|
||||
val image = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
|
||||
getEmptyTexture(image.width, image.height)
|
||||
}
|
||||
val (interpType, cubicValues) = poseTransition.beforePose.texturePhaseOut
|
||||
?: poseTransition.afterPose.texturePhaseIn
|
||||
|
||||
if (interpType == TextureInterpolationType.STEP) {
|
||||
val newTexture = DynamicTexture(texture2)
|
||||
newTexture.setFilter(false, false)
|
||||
return DynamicStateShard(newTexture)
|
||||
}
|
||||
|
||||
val texture1 = if (animation1 != null) {
|
||||
getImageFromAnimation(animation1, state.animationSeconds - startTime1)
|
||||
} else {
|
||||
val image = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
|
||||
getEmptyTexture(image.width, image.height)
|
||||
}
|
||||
var i =
|
||||
((state.animationSeconds - poseTransition.startTime) / (poseTransition.endTime - poseTransition.startTime)).toDouble() //Linear
|
||||
if (interpType == TextureInterpolationType.BEZIER) { //Switch to Bezier
|
||||
i = resolveCubicValues(cubicValues, i)
|
||||
}
|
||||
|
||||
val newImage = getInterpolatedTexture(texture1, texture2, i)
|
||||
val newTexture = DynamicTexture(newImage)
|
||||
newTexture.setFilter(false, false)
|
||||
return DynamicStateShard(newTexture)
|
||||
|
||||
} else {
|
||||
var animation = if ((state.primaryAnimation?.animation as? BedrockActiveAnimation)?.animation?.cobblemonTextures?.get(layerName) != null) {
|
||||
(state.primaryAnimation?.animation as? BedrockActiveAnimation)?.animation
|
||||
} else null
|
||||
var startTime = 0F
|
||||
|
||||
if (animation == null) {
|
||||
val data = getMainAnimFromPose(state.currentPose?.let(state.currentModel!!.poses::get), state)
|
||||
animation = data.first
|
||||
startTime = data.second
|
||||
}
|
||||
|
||||
if (animation != null) {
|
||||
val image = getImageFromAnimation(animation, state.animationSeconds - startTime)
|
||||
val newTexture = DynamicTexture(image)
|
||||
newTexture.setFilter(false, false)
|
||||
return DynamicStateShard(newTexture)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
return null
|
||||
}
|
||||
val o = frameIndex % frames.size
|
||||
val i = state.animationSeconds * fps - frameIndex
|
||||
|
||||
val image = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
|
||||
val newTexture = DynamicTexture(getEmptyTexture(image.width, image.height))
|
||||
newTexture.setFilter(false, false)
|
||||
return DynamicStateShard(newTexture)
|
||||
}
|
||||
|
||||
override fun getTexture(state: PosableState): ResourceLocation {
|
||||
return textures[0]
|
||||
}
|
||||
|
||||
private fun getMainAnimFromPose(pose: Pose?, state: PosableState, transitionAnimation: Boolean = false): Pair<BedrockAnimation?, Float> {
|
||||
var animation: BedrockAnimation? = null
|
||||
var startTime = 0F
|
||||
|
||||
pose?.animations?.filterIsInstance<BedrockPoseAnimation>()?.filter{ transitionAnimation || state.shouldIdleRun(it, 0F) }?.forEach {
|
||||
if (it.animation.cobblemonTextures[layerName] != null) {
|
||||
animation = it.animation
|
||||
}
|
||||
}
|
||||
|
||||
if (state.primaryAnimation == null && state.currentModel!!.context.request(RenderContext.DO_QUIRKS) != false) {
|
||||
pose?.quirks?.forEach { quirk ->
|
||||
val quirkData = quirk.getOrCreateData(state)
|
||||
quirkData.animations.filterIsInstance<BedrockActiveAnimation>().forEach {
|
||||
if (it.animation.cobblemonTextures[layerName] != null) {
|
||||
startTime = it.startedSeconds
|
||||
animation = it.animation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(animation, startTime)
|
||||
}
|
||||
|
||||
private fun getImageFromAnimation(animation: BedrockAnimation, animationSeconds: Float): NativeImage {
|
||||
val resourceManager = Minecraft.getInstance().resourceManager
|
||||
|
||||
val keyframeMap = animation.cobblemonTextures[layerName]
|
||||
if (keyframeMap.isNullOrEmpty()) {
|
||||
val image1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
|
||||
return getEmptyTexture(image1.width, image1.height)
|
||||
}
|
||||
|
||||
var adjustedTime = animationSeconds
|
||||
if (animation.shouldLoop) adjustedTime %= animation.animationLength.toFloat()
|
||||
val keyIndex = keyframeMap.keys.indexOfLast { it <= adjustedTime }
|
||||
val keyframe1 = when(keyIndex) {
|
||||
-1 -> keyframeMap.firstEntry()
|
||||
else -> keyframeMap.entries.toMutableList()[keyIndex]
|
||||
}
|
||||
val keyframe2 = when {
|
||||
keyIndex + 1 >= keyframeMap.size -> keyframeMap.lastEntry()
|
||||
else -> keyframeMap.entries.toMutableList()[keyIndex+1]
|
||||
}
|
||||
|
||||
val texture1 : NativeImage
|
||||
val texture2 : NativeImage
|
||||
var texture2 : NativeImage? = null
|
||||
|
||||
try { //Try getting images of the frames so we can use them to make a new one
|
||||
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(frames[o]).open())
|
||||
val s = if (o == frames.size - 1) 0 else o + 1
|
||||
texture2 = NativeImage.read(resourceManager.getResourceOrThrow(frames[s]).open())
|
||||
} catch (e : Exception) {
|
||||
return null
|
||||
if (keyframe1.value.textureSlot == -1 && (keyframe2.value.textureSlot == -1 || keyframe1.value.interpolationType == TextureInterpolationType.STEP)) { // If both -1 we just return empty texture or if Step
|
||||
val image1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[0]).open())
|
||||
return getEmptyTexture(image1.width, image1.height)
|
||||
}
|
||||
|
||||
for (x in 0..<texture1.width) {
|
||||
for (y in 0..<texture1.height) {
|
||||
val color1 = texture1.getPixelRGBA(x,y).toRGBA()
|
||||
val color2 = texture2.getPixelRGBA(x,y).toRGBA()
|
||||
|
||||
var newRed : Int
|
||||
var newGreen : Int
|
||||
var newBlue : Int
|
||||
var newAlpha : Int
|
||||
|
||||
//Usually full transparent pixel is black, so this makes it copy the other texture colours to look better, however considering if we should allow to disable this feature
|
||||
if (color1.w == 0F && color2.w == 1F) {
|
||||
newRed = floor(color2.x * 255).toInt()
|
||||
newGreen = floor(color2.y * 255).toInt()
|
||||
newBlue = floor(color2.z * 255).toInt()
|
||||
} else if (color1.w == 1F && color2.w == 0F) {
|
||||
newRed = floor(color1.x * 255).toInt()
|
||||
newGreen = floor(color1.y * 255).toInt()
|
||||
newBlue = floor(color1.z * 255).toInt()
|
||||
} else {
|
||||
newRed = floor((color1.x + (color2.x - color1.x) * i) * 255).toInt()
|
||||
newGreen = floor((color1.y + (color2.y - color1.y) * i) * 255).toInt()
|
||||
newBlue = floor((color1.z + (color2.z - color1.z) * i) * 255).toInt()
|
||||
}
|
||||
|
||||
newAlpha = floor((color1.w + (color2.w - color1.w) * i) * 255).toInt()
|
||||
|
||||
val finalColor = (newAlpha shl 24) or (newRed shl 16) or (newGreen shl 8) or newBlue
|
||||
|
||||
texture1.setPixelRGBA(x,y,finalColor) //We aren't using texture 1 anymore so it's easier to override it
|
||||
}
|
||||
var inverseInterp = false
|
||||
if (keyframe1.value.textureSlot == -1) {
|
||||
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe2.value.textureSlot]).open())
|
||||
inverseInterp = true
|
||||
} else if (keyframe2.value.textureSlot == -1) {
|
||||
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe1.value.textureSlot]).open())
|
||||
} else {
|
||||
texture1 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe1.value.textureSlot]).open())
|
||||
texture2 = NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe2.value.textureSlot]).open())
|
||||
}
|
||||
val newTexture = DynamicTexture(texture1)
|
||||
newTexture.setFilter(false, false)
|
||||
return newTexture
|
||||
|
||||
val i = (adjustedTime - keyframe1.key.toFloat()) / (keyframe2.key - keyframe1.key)
|
||||
if (keyframe1.value.textureSlot != keyframe2.value.textureSlot && keyframe1.value.interpolationType == TextureInterpolationType.LINEAR) {
|
||||
var interp = i
|
||||
if (inverseInterp) interp = 1 - i
|
||||
return getInterpolatedTexture(texture1, texture2, interp)
|
||||
} else if (keyframe1.value.textureSlot != keyframe2.value.textureSlot && keyframe1.value.interpolationType == TextureInterpolationType.BEZIER) {
|
||||
var cubicInterp = resolveCubicValues(keyframe1.value.cubicLerpFunctionValues, i)
|
||||
if (inverseInterp) cubicInterp = 1 - cubicInterp
|
||||
return getInterpolatedTexture(texture1, texture2, cubicInterp)
|
||||
}
|
||||
|
||||
return NativeImage.read(resourceManager.getResourceOrThrow(textures[keyframe1.value.textureSlot]).open())
|
||||
}
|
||||
}
|
||||
|
||||
class VariableModelTextureSupplier : ModelTextureSupplier {
|
||||
override fun invoke(state: PosableState): ResourceLocation {
|
||||
override fun invoke(state: PosableState): RenderStateShard.EmptyTextureStateShard {
|
||||
return RenderStateShard.TextureStateShard(getTexture(state), false, false)
|
||||
}
|
||||
|
||||
override fun getTexture(state: PosableState): ResourceLocation {
|
||||
return state.runtime.environment.variable.map["texture"]?.asString()?.asIdentifierDefaultingNamespace()
|
||||
?: cobblemonResource("textures/npcs/default.png")
|
||||
}
|
||||
@@ -295,4 +433,75 @@ class ModelLayer {
|
||||
enum class SpriteType {
|
||||
PORTRAIT,
|
||||
PROFILE
|
||||
}
|
||||
|
||||
private fun getInterpolatedTexture(texture1: NativeImage, texture2: NativeImage?, i: Double): NativeImage {
|
||||
for (x in 0..<texture1.width) {
|
||||
for (y in 0..<texture1.height) {
|
||||
val color1 = texture1.getPixelRGBA(x,y).toRGBA()
|
||||
val color2 = texture2?.getPixelRGBA(x,y)?.toRGBA() ?: Vector4f(0F,0F,0F,0F)
|
||||
|
||||
var newRed : Int
|
||||
var newGreen : Int
|
||||
var newBlue : Int
|
||||
|
||||
//Usually full transparent pixel is black, so this makes it copy the other texture colours to look better, however considering if we should allow to disable this feature
|
||||
if (color1.w == 0F && color2.w > 0F) {
|
||||
newRed = floor(color2.x * 255).toInt()
|
||||
newGreen = floor(color2.y * 255).toInt()
|
||||
newBlue = floor(color2.z * 255).toInt()
|
||||
} else if (color1.w > 0F && color2.w == 0F) {
|
||||
newRed = floor(color1.x * 255).toInt()
|
||||
newGreen = floor(color1.y * 255).toInt()
|
||||
newBlue = floor(color1.z * 255).toInt()
|
||||
} else {
|
||||
newRed = floor((color1.x + (color2.x - color1.x) * i) * 255).toInt()
|
||||
newGreen = floor((color1.y + (color2.y - color1.y) * i) * 255).toInt()
|
||||
newBlue = floor((color1.z + (color2.z - color1.z) * i) * 255).toInt()
|
||||
}
|
||||
|
||||
val newAlpha = floor((color1.w + (color2.w - color1.w) * i) * 255).toInt()
|
||||
|
||||
val finalColor = (newAlpha shl 24) or (newRed shl 16) or (newGreen shl 8) or newBlue
|
||||
|
||||
texture1.setPixelRGBA(x,y,finalColor) //We aren't using texture 1 anymore so it's easier to override it
|
||||
}
|
||||
}
|
||||
return texture1
|
||||
}
|
||||
|
||||
private fun getEmptyTexture(width: Int, height: Int): NativeImage {
|
||||
return NativeImage(width, height, false)
|
||||
}
|
||||
|
||||
private fun resolveCubicValues(clampedCubicValues : MutableList<Double>, i: Double): Double {
|
||||
for (value in 0..<clampedCubicValues.size) {
|
||||
clampedCubicValues[value] = Math.clamp(clampedCubicValues[value],0.0,1.0)
|
||||
}
|
||||
|
||||
val points = mutableListOf<Vector2d>()
|
||||
for (o in 0..200) {
|
||||
val t = o/200.0
|
||||
val omt = (1.0-t)
|
||||
|
||||
val point = Vector2d()
|
||||
point.x = 3.0 * omt * omt * t * clampedCubicValues[0] + 3.0 * omt * t * t * clampedCubicValues[2] + t * t * t
|
||||
point.y = 3.0 * omt * omt * t * clampedCubicValues[1] + 3.0 * omt * t * t * clampedCubicValues[3] + t * t * t
|
||||
|
||||
points.add(point)
|
||||
}
|
||||
|
||||
var beforePoint = Vector2d(0.0,0.0)
|
||||
var afterPoint = Vector2d(1.0, 1.0)
|
||||
|
||||
for (point in points) {
|
||||
if (point.x > beforePoint.x && point.x < i) {
|
||||
beforePoint = point
|
||||
}
|
||||
if (point.x < afterPoint.x && point.x > i) {
|
||||
afterPoint = point
|
||||
}
|
||||
}
|
||||
|
||||
return beforePoint.y + (afterPoint.y - beforePoint.y) * ((i - beforePoint.x) / (afterPoint.x - beforePoint.x))
|
||||
}
|
||||
+35
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.SingleBoneLookAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.SimpleQuirk
|
||||
@@ -177,4 +178,38 @@ class JsonPose(model: PosableModel, json: JsonObject) {
|
||||
val transitions = json.get("transitions")?.takeIf { it is JsonObject }?.asJsonObject?.entrySet()?.map { (key, value) ->
|
||||
JsonPoseTransition(key, value.asString.asExpressionLike())
|
||||
} ?: emptyList()
|
||||
|
||||
val texturePhaseIn = json.get("texturePhaseIn")?.takeIf { it is JsonObject }?.asJsonObject?.let { theObject ->
|
||||
val interpolationType = when(theObject.get("interpolation")?.asString) {
|
||||
"step" -> TextureInterpolationType.STEP
|
||||
"linear" -> TextureInterpolationType.LINEAR
|
||||
"bezier" -> TextureInterpolationType.BEZIER
|
||||
else -> TextureInterpolationType.STEP
|
||||
}
|
||||
val cubicValues = (theObject.get("cubicValues")?.asString ?: "0.0, 0.0, 1.0, 1.0").let { values ->
|
||||
val list = mutableListOf<Double>()
|
||||
values.split(",").forEach {
|
||||
list.add(it.toDouble())
|
||||
}
|
||||
list
|
||||
}
|
||||
Pair(interpolationType, cubicValues)
|
||||
} ?: Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0))
|
||||
|
||||
val texturePhaseOut = json.get("texturePhaseOut")?.takeIf { it is JsonObject }?.asJsonObject?.let { theObject ->
|
||||
val interpolationType = when(theObject.get("interpolation")?.asString) {
|
||||
"step" -> TextureInterpolationType.STEP
|
||||
"linear" -> TextureInterpolationType.LINEAR
|
||||
"bezier" -> TextureInterpolationType.BEZIER
|
||||
else -> TextureInterpolationType.STEP
|
||||
}
|
||||
val cubicValues = (theObject.get("cubicValues")?.asString ?: "0.0, 0.0, 1.0, 1.0").let { values ->
|
||||
val list = mutableListOf<Double>()
|
||||
values.split(",").forEach {
|
||||
list.add(it.toDouble())
|
||||
}
|
||||
list
|
||||
}
|
||||
Pair(interpolationType, cubicValues)
|
||||
}
|
||||
}
|
||||
+23
-23
@@ -19,11 +19,13 @@ import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.KeyframableTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.ModelLayer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.*
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockPoseAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.ModelFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
|
||||
@@ -276,7 +278,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
onTransitionedInto: (PosableState) -> Unit = {},
|
||||
animations: Array<PoseAnimation> = emptyArray(),
|
||||
transformedParts: Array<ModelPartTransformation> = emptyArray(),
|
||||
quirks: Array<ModelQuirk<*>> = emptyArray()
|
||||
quirks: Array<ModelQuirk<*>> = emptyArray(),
|
||||
texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>> = Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0)),
|
||||
texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>? = null
|
||||
): Pose {
|
||||
return Pose(
|
||||
poseType.name,
|
||||
@@ -288,7 +292,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
namedAnimations,
|
||||
animations,
|
||||
transformedParts,
|
||||
quirks
|
||||
quirks,
|
||||
texturePhaseIn,
|
||||
texturePhaseOut
|
||||
).also {
|
||||
poses[poseType.name] = it
|
||||
}
|
||||
@@ -304,7 +310,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
onTransitionedInto: (PosableState) -> Unit = {},
|
||||
animations: Array<PoseAnimation> = emptyArray(),
|
||||
transformedParts: Array<ModelPartTransformation> = emptyArray(),
|
||||
quirks: Array<ModelQuirk<*>> = emptyArray()
|
||||
quirks: Array<ModelQuirk<*>> = emptyArray(),
|
||||
texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>> = Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0)),
|
||||
texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>? = null
|
||||
): Pose {
|
||||
return Pose(
|
||||
poseName,
|
||||
@@ -316,7 +324,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
namedAnimations,
|
||||
animations,
|
||||
transformedParts,
|
||||
quirks
|
||||
quirks,
|
||||
texturePhaseIn,
|
||||
texturePhaseOut
|
||||
).also {
|
||||
poses[poseName] = it
|
||||
}
|
||||
@@ -332,7 +342,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
onTransitionedInto: (PosableState) -> Unit = {},
|
||||
animations: Array<PoseAnimation> = emptyArray(),
|
||||
transformedParts: Array<ModelPartTransformation> = emptyArray(),
|
||||
quirks: Array<ModelQuirk<*>> = emptyArray()
|
||||
quirks: Array<ModelQuirk<*>> = emptyArray(),
|
||||
texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>> = Pair(TextureInterpolationType.STEP, mutableListOf(0.0, 0.0, 1.0, 1.0)),
|
||||
texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>? = null
|
||||
): Pose {
|
||||
return Pose(
|
||||
poseName,
|
||||
@@ -344,7 +356,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
namedAnimations,
|
||||
animations,
|
||||
transformedParts,
|
||||
quirks
|
||||
quirks,
|
||||
texturePhaseIn,
|
||||
texturePhaseOut
|
||||
).also {
|
||||
poses[poseName] = it
|
||||
}
|
||||
@@ -412,13 +426,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
|
||||
fun registerRelevantPart(pairing: Pair<String, ModelPart>) = registerRelevantPart(pairing.first, pairing.second)
|
||||
|
||||
/** Needed a custom one, so I can make it take in a dynamic texture instead of resource location */
|
||||
class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTextureStateShard(Runnable {
|
||||
RenderSystem.setShaderTexture(0, texture.id)
|
||||
}, Runnable {
|
||||
texture.close() // Cleanup
|
||||
})
|
||||
|
||||
/** Renders the model. Assumes rotations have been set. Will simply render the base model and then any extra layers. */
|
||||
fun render(
|
||||
context: RenderContext,
|
||||
@@ -453,16 +460,9 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
val provider = bufferProvider
|
||||
if (provider != null) {
|
||||
for (layer in currentLayers) {
|
||||
var renderLayer : RenderType
|
||||
if (layer.texture is AnimatedModelTextureSupplier && layer.texture.interpolation) {
|
||||
//Handle Interpolation
|
||||
val texture = layer.texture.interpolatedTexture(currentState ?: FloatingState()) ?: continue
|
||||
renderLayer = makeLayer(DynamicStateShard(texture), layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
}
|
||||
else {
|
||||
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
|
||||
renderLayer = getLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
}
|
||||
(layer.texture as? KeyframableTextureSupplier)?.layerName = layer.name
|
||||
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
|
||||
val renderLayer = makeLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
val consumer = provider.getBuffer(renderLayer)
|
||||
val tint = layer.tint
|
||||
val tintRed = (tint.x * r2 * 255).toInt()
|
||||
|
||||
+3
-1
@@ -90,7 +90,9 @@ class PoseAdapter(
|
||||
animations = pose.idleAnimations,
|
||||
transformedParts = pose.transformedParts,
|
||||
quirks = pose.quirks.toTypedArray(),
|
||||
namedAnimations = pose.animations
|
||||
namedAnimations = pose.animations,
|
||||
texturePhaseIn = pose.texturePhaseIn,
|
||||
texturePhaseOut = pose.texturePhaseOut
|
||||
).also {
|
||||
it.transitions.putAll(
|
||||
pose.transitions
|
||||
|
||||
+13
-1
@@ -132,7 +132,9 @@ data class BedrockAnimation(
|
||||
val shouldLoop: Boolean,
|
||||
val animationLength: Double,
|
||||
val effects: List<BedrockEffectKeyframe>,
|
||||
val boneTimelines: Map<String, BedrockBoneTimeline>
|
||||
val boneTimelines: Map<String, BedrockBoneTimeline>,
|
||||
//This is custom data added by our custom BB plugin
|
||||
val cobblemonTextures: MutableMap<String, TreeMap<Double, CobblemonBedrockTextureKeyframe>>
|
||||
) {
|
||||
fun checkForErrors() {
|
||||
boneTimelines.forEach { (_, timeline) ->
|
||||
@@ -396,4 +398,14 @@ enum class InterpolationType {
|
||||
|
||||
enum class Transformation {
|
||||
POSITION, ROTATION, SCALE
|
||||
}
|
||||
|
||||
data class CobblemonBedrockTextureKeyframe(
|
||||
val textureSlot: Int,
|
||||
val cubicLerpFunctionValues: MutableList<Double>,
|
||||
val interpolationType: TextureInterpolationType
|
||||
)
|
||||
|
||||
enum class TextureInterpolationType {
|
||||
STEP, LINEAR, BEZIER
|
||||
}
|
||||
+22
-1
@@ -20,6 +20,7 @@ import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import java.lang.reflect.Type
|
||||
import java.util.TreeMap
|
||||
|
||||
/**
|
||||
* Gson adapter for converting bedrock/blockbench json data into a friendlier object model.
|
||||
@@ -89,7 +90,27 @@ object BedrockAnimationAdapter : JsonDeserializer<BedrockAnimation> {
|
||||
)
|
||||
}
|
||||
|
||||
return BedrockAnimation(shouldLoop, animationLength, effects, boneTimelines)
|
||||
//Custom Data reading added by our custom BB plugin
|
||||
val cobblemonTextures = mutableMapOf<String, TreeMap<Double, CobblemonBedrockTextureKeyframe>>()
|
||||
json["cobblemon_texture"]?.asJsonObject?.entrySet()?.forEach { (texture, timeline) ->
|
||||
val textureTimeline = TreeMap<Double, CobblemonBedrockTextureKeyframe>()
|
||||
timeline.asJsonObject.entrySet().forEach { (frame, values) ->
|
||||
val lerpFunctionValues = mutableListOf<Double>()
|
||||
values.asJsonObject["cobblemon_cubic_lerp_function"].asString.split(",").forEach {
|
||||
lerpFunctionValues.add(it.toDouble())
|
||||
}
|
||||
val interpolation = when(values.asJsonObject["cobblemon_interpolation"].asString) {
|
||||
"step" -> TextureInterpolationType.STEP
|
||||
"linear" -> TextureInterpolationType.LINEAR
|
||||
"bezier" -> TextureInterpolationType.BEZIER
|
||||
else -> TextureInterpolationType.STEP
|
||||
}
|
||||
textureTimeline[frame.toDouble()] = CobblemonBedrockTextureKeyframe(values.asJsonObject["cobblemon_texture_slot"].asInt, lerpFunctionValues, interpolation)
|
||||
}
|
||||
cobblemonTextures[texture] = textureTimeline
|
||||
}
|
||||
|
||||
return BedrockAnimation(shouldLoop, animationLength, effects, boneTimelines, cobblemonTextures)
|
||||
}
|
||||
else {
|
||||
throw IllegalStateException("animation json could not be parsed")
|
||||
|
||||
+5
-1
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.TextureInterpolationType
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
@@ -39,7 +40,10 @@ class Pose(
|
||||
val namedAnimations: MutableMap<String, ExpressionLike> = mutableMapOf(),
|
||||
val animations: Array<PoseAnimation>,
|
||||
val transformedParts: Array<ModelPartTransformation>,
|
||||
val quirks: Array<ModelQuirk<*>>
|
||||
val quirks: Array<ModelQuirk<*>>,
|
||||
/** These are very specific to the animated texture**/
|
||||
val texturePhaseIn: Pair<TextureInterpolationType, MutableList<Double>>,
|
||||
val texturePhaseOut: Pair<TextureInterpolationType, MutableList<Double>>?,
|
||||
) {
|
||||
fun isSuitable(state: PosableState) = condition?.invoke(state) ?: true
|
||||
|
||||
|
||||
+9
-4
@@ -8,10 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.ModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.StaticModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.VariableModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.*
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
@@ -47,6 +45,13 @@ object ModelTextureSupplierAdapter : JsonDeserializer<ModelTextureSupplier> {
|
||||
frames = frames,
|
||||
interpolation = interpolation
|
||||
)
|
||||
} else if (json.isJsonArray) {
|
||||
val jsonArray = json as JsonArray
|
||||
val textures = mutableListOf<ResourceLocation>()
|
||||
jsonArray.forEach {
|
||||
textures.add(ResourceLocation.parse(it.asString))
|
||||
}
|
||||
return KeyframableTextureSupplier(textures)
|
||||
} else {
|
||||
throw IllegalArgumentException("Invalid JSON provided for model texture, it was of type ${json::class.simpleName} instead of a String or Object.")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user