Skip to content

Commit

Permalink
feat: include gif-shaders in emojy-rp
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Oct 28, 2024
1 parent 8ff84c1 commit c41185c
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 18 deletions.
195 changes: 195 additions & 0 deletions core/src/main/kotlin/com/mineinabyss/emojy/EmojyGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.mineinabyss.emojy.config.Gif
import com.mineinabyss.emojy.config.Gifs
import com.mineinabyss.idofront.font.Space
import com.mineinabyss.idofront.font.Space.Companion.toNumber
import com.mineinabyss.idofront.messaging.broadcastVal
import com.mineinabyss.idofront.resourcepacks.ResourcePacks
import net.kyori.adventure.key.Key
import team.unnamed.creative.ResourcePack
Expand All @@ -18,6 +19,7 @@ import team.unnamed.creative.texture.Texture
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.io.path.Path
import kotlin.math.ceil
import kotlin.math.sqrt

Expand Down Expand Up @@ -59,6 +61,199 @@ object EmojyGenerator {
}
Font.font(emojyConfig.spaceFont, spaceProvider).addTo(resourcePack)

generateGifShaderFiles(resourcePack)

resourcePack.packMeta(48, "")

File("C:\\Users\\Sivert\\AppData\\Roaming\\ModrinthApp\\profiles\\MineInAbyss - 1.21\\resourcepacks\\pack").deleteRecursively()
MinecraftResourcePackWriter.minecraft().writeToDirectory(File("C:\\Users\\Sivert\\AppData\\Roaming\\ModrinthApp\\profiles\\MineInAbyss - 1.21\\resourcepacks\\pack"), resourcePack)

MinecraftResourcePackWriter.minecraft().writeToZipFile(emojy.plugin.dataFolder.resolve("pack.zip"), resourcePack)
}

private fun generateGifShaderFiles(resourcePack: ResourcePack) {
val fsh = Writable.stringUtf8(
"""
#version 150
#moj_import <fog.glsl>
uniform sampler2D Sampler0;
uniform vec4 ColorModulator;
uniform float FogStart, FogEnd;
uniform vec4 FogColor;
in float vertexDistance;
in vec4 vertexColor;
in vec2 texCoord0;
out vec4 fragColor;
void main() {
vec4 v = texture(Sampler0, texCoord0) * vertexColor * ColorModulator;
if (v.w == 0 ) {
discard;
}
fragColor = linear_fog(v, vertexDistance, FogStart, FogEnd, FogColor);
}
""".trimIndent()
)
val vsh = Writable.stringUtf8(
"""
#version 150
in vec3 Position;
in vec4 Color;
in vec2 UV0;
in ivec2 UV2;
uniform sampler2D Sampler0, Sampler2;
uniform mat4 ModelViewMat, ProjMat;
uniform float GameTime;
out float vertexDistance;
out vec4 vertexColor;
out vec2 texCoord0;
void main() {
gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.);
vertexDistance = length((ModelViewMat * vec4(Position, 1.)).xyz);
texCoord0 = UV0;
if ( Color.xyz == vec3( 254 ) / 255.0 ) {
vec2 dimensions = textureSize( Sampler0, 0 );
vec2 texShift = 1 / dimensions;
// Just in case the texture is not its own image
// Otherwise we could just fetch the pixel at 0, 0
ivec2 quadrantUV = ivec2( UV0 * dimensions );
vec4 quadrant = texelFetch( Sampler0, quadrantUV, 0 );
vec2 newUV0 = UV0;
if ( quadrant.a == ( 149.0 / 255.0 ) ) {
vec4 infoPix1 = vec4( 0 );
vec4 infoPix2 = vec4( 0 );
vertexColor = vec4( 1 );
if ( quadrant.r == 1.0 / 255.0 ) {
infoPix1 = texelFetch( Sampler0, quadrantUV + ivec2( 1, 0 ), 0 );
infoPix2 = texelFetch( Sampler0, quadrantUV + ivec2( 0, 1 ), 0 );
newUV0 = newUV0 + ( quadrant.gb * 255 + 1 ) / dimensions;
} else if ( quadrant.r == 0.0 / 255.0 ) {
infoPix1 = texelFetch( Sampler0, quadrantUV - ivec2( 1, 0 ), 0 );
infoPix2 = texelFetch( Sampler0, quadrantUV + ivec2( 0, 1 ), 0 );
newUV0 = newUV0 + ( quadrant.gb * 255 + vec2( -1, 1 ) ) / dimensions;
} else if ( quadrant.r == 3.0 / 255.0 ) {
infoPix1 = texelFetch( Sampler0, quadrantUV - ivec2( 1, 0 ), 0 );
infoPix2 = texelFetch( Sampler0, quadrantUV - ivec2( 0, 1 ), 0 );
newUV0 = newUV0 + ( quadrant.gb * 255 - 1 ) / dimensions;
} else if ( quadrant.r == 2.0 / 255.0 ) {
infoPix1 = texelFetch( Sampler0, quadrantUV + ivec2( 1, 0 ), 0 );
infoPix2 = texelFetch( Sampler0, quadrantUV - ivec2( 0, 1 ), 0 );
newUV0 = newUV0 + ( quadrant.gb * 255 + vec2( 1, -1 ) ) / dimensions;
} else {
vertexColor = Color * texelFetch( Sampler2, UV2 / 16, 0 );
return;
}
// Get timing info
float totalTime = infoPix1.r * 256 + infoPix1.g;
float startTime = infoPix1.b * 256 + infoPix1.a;
float endTime = infoPix2.r * 256 + infoPix2.g;
float lower = startTime / totalTime;
float upper = endTime / totalTime;
float total = totalTime / 4705.882352941176;
float whole = 0;
float time = modf( GameTime / total, whole );
vertexColor = vec4( time >= lower && time < upper );
texCoord0 = newUV0;
} else {
vertexColor = Color * texelFetch( Sampler2, UV2 / 16, 0 );
}
} else if ( Color.xyz == vec3( floor( 254 / 4. ) / 255. ) ) {
// Get rid of shadows
vertexColor = vec4( 0 );
} else {
vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);
}
}
""".trimIndent()
)
val json = Writable.stringUtf8(
"""
{
"blend": {
"func": "add",
"srcrgb": "srcalpha",
"dstrgb": "1-srcalpha"
},
"vertex": "rendertype_text",
"fragment": "rendertype_text",
"attributes": [
"Position",
"Color",
"UV0",
"UV2"
],
"samplers": [
{
"name": "Sampler0"
},
{
"name": "Sampler2"
}
],
"uniforms": [
{
"name": "ModelViewMat",
"type": "matrix4x4",
"count": 16,
"values": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
},
{
"name": "ProjMat",
"type": "matrix4x4",
"count": 16,
"values": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
},
{
"name": "GameTime",
"type": "float",
"count": 1,
"values": [ 0 ]
},
{
"name": "ColorModulator",
"type": "float",
"count": 4,
"values": [ 1, 1, 1, 1 ]
},
{
"name": "FogStart",
"type": "float",
"count": 1,
"values": [ 0 ]
},
{
"name": "FogEnd",
"type": "float",
"count": 1,
"values": [ 1 ]
},
{
"name": "FogColor",
"type": "float",
"count": 4,
"values": [ 0, 0, 0, 0 ]
}
]
}
""".trimIndent()
)

resourcePack.unknownFile("assets/minecraft/shaders/core/rendertype_text.json", json)
resourcePack.unknownFile("assets/minecraft/shaders/core/rendertype_text.fsh", fsh)
resourcePack.unknownFile("assets/minecraft/shaders/core/rendertype_text.vsh", vsh)
}
}
35 changes: 17 additions & 18 deletions core/src/main/kotlin/com/mineinabyss/emojy/config/Gif.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.mineinabyss.emojy.EmojyGenerator.gifFolder
import com.mineinabyss.emojy.emojy
import com.mineinabyss.emojy.emojyConfig
import com.mineinabyss.emojy.spaceComponent
import com.mineinabyss.idofront.messaging.*
import com.mineinabyss.idofront.serialization.KeySerializer
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.mineinabyss.idofront.textcomponents.serialize
Expand All @@ -26,6 +27,7 @@ import team.unnamed.creative.base.Writable
import team.unnamed.creative.font.Font
import team.unnamed.creative.font.FontProvider
import team.unnamed.creative.texture.Texture
import java.awt.AlphaComposite
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
Expand Down Expand Up @@ -61,15 +63,15 @@ data class Gif(
SHADER, OBFUSCATION
}

private fun unicode(index: Int): Char = Character.toChars(PRIVATE_USE_FIRST + index).first()
private fun unicode(index: Int): String = Character.toChars(PRIVATE_USE_FIRST + index).first().toString()

private fun unicode(): String {
return when (type) {
GifType.SHADER -> (0 until frameCount).joinToString("") { unicode(it).toString() }
.miniMsg().font(font).color(TextColor.fromHexString("#FEFEFE")).serialize()
GifType.SHADER -> Component.text((0 until frameCount).joinToString(unicode(frameCount)) { unicode(it) })
.font(font).color(TextColor.fromHexString("#FEFEFE")).serialize()

GifType.OBFUSCATION -> unicode(0).toString().miniMsg()
.decorate(TextDecoration.OBFUSCATED).font(font).color(NamedTextColor.WHITE).serialize()
GifType.OBFUSCATION -> Component.text(unicode(0), NamedTextColor.WHITE)
.decorate(TextDecoration.OBFUSCATED).font(font).serialize()
}
}

Expand All @@ -92,14 +94,14 @@ data class Gif(

fun font() = Font.font(font, fontProvider(), gifAdvance())
private fun gifAdvance() =
FontProvider.space().advance(unicode(frameCount() + 1).toString(), -(height * aspectRatio + 1).roundToInt())
FontProvider.space().advance(unicode(frameCount), -(ascent * aspectRatio).roundToInt())
.build()

private fun fontProvider(): FontProvider {
// Construct the `chars` mapping in `["xyz", "xyz", "xyz"]` format
val charsGrid = (0 until bitmapRows).map { row ->
(0 until bitmapColumns).joinToString("") { col ->
unicode(row + col * bitmapColumns).toString()
unicode((row * bitmapColumns + col).apply { if (id.contains("neco")) println(this) })
}
}

Expand All @@ -125,42 +127,39 @@ data class Gif(
runCatching {
val gifFolder = gifFolder.resolve(id)
gifFolder.deleteRecursively()

GifConverter.splitGif(gifFile, frameCount()) // Keep individual frame creation
createSpritesheet(gifFolder).let {
ImageIO.write(it, "png", gifSpriteSheet)
} // Create the spritesheet based on frames
createSpritesheet(gifFolder)

Texture.texture(Key.key("${framePath.asString()}.png"), Writable.file(gifSpriteSheet)).addTo(resourcePack)
}.onFailure {
emojy.logger.d("Could not generate split gif for ${id}.gif: ${it.message}")
}
}

private fun createSpritesheet(gifFolder: File): BufferedImage {
private fun createSpritesheet(gifFolder: File) {
// List all frame files in the folder, ensuring they are sorted by filename
val frames = gifFolder.listFiles()
?.filter { it.isFile && it.extension == "png" }
?.sortedBy { it.nameWithoutExtension.toIntOrNull() } // Assumes frames are named in order (e.g., 1.png, 2.png)
?: throw IllegalStateException("No frame files found in $gifFolder")

val frameCount = frames.size

// Determine dimensions based on the first frame
val firstFrame = ImageIO.read(frames[0])
val (frameWidth, frameHeight) = firstFrame.width to firstFrame.height
val (frameWidth, frameHeight) = ImageIO.read(frames.first()).let { it.width to it.height }

// Create the spritesheet image
val spritesheet = BufferedImage(bitmapColumns * frameWidth, bitmapRows * frameHeight, BufferedImage.TYPE_INT_ARGB)
val graphics = spritesheet.createGraphics()
graphics.composite = AlphaComposite.Src // Ensure correct alpha handling

frames.forEachIndexed { index, frameFile ->
val x = (index % bitmapColumns) * frameWidth
val y = (index / bitmapColumns) * frameHeight
val frameImage = ImageIO.read(frameFile)
graphics.drawImage(frameImage, x, y, null)
graphics.drawImage(ImageIO.read(frameFile), x, y, null)
}
graphics.dispose()

return spritesheet
ImageIO.write(spritesheet, "png", gifSpriteSheet)

}
}

0 comments on commit c41185c

Please sign in to comment.