mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-11 17:48:45 +08:00
Add DressUp sample and drawable to bitmap rendering
This commit is contained in:
parent
5ed26e6ef5
commit
63681d86af
359
spine-android/app/src/main/assets/mix-and-match-pma.atlas
Normal file
359
spine-android/app/src/main/assets/mix-and-match-pma.atlas
Normal file
@ -0,0 +1,359 @@
|
||||
mix-and-match-pma.png
|
||||
size: 1024, 512
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
base-head
|
||||
bounds: 118, 70, 95, 73
|
||||
boy/arm-front
|
||||
bounds: 831, 311, 36, 115
|
||||
rotate: 90
|
||||
boy/backpack
|
||||
bounds: 249, 357, 119, 153
|
||||
boy/backpack-pocket
|
||||
bounds: 628, 193, 34, 62
|
||||
rotate: 90
|
||||
boy/backpack-strap-front
|
||||
bounds: 330, 263, 38, 88
|
||||
rotate: 90
|
||||
boy/backpack-up
|
||||
bounds: 482, 171, 21, 70
|
||||
boy/body
|
||||
bounds: 845, 413, 97, 132
|
||||
rotate: 90
|
||||
boy/boot-ribbon-front
|
||||
bounds: 234, 304, 9, 11
|
||||
boy/collar
|
||||
bounds: 471, 243, 73, 29
|
||||
rotate: 90
|
||||
boy/ear
|
||||
bounds: 991, 352, 19, 23
|
||||
rotate: 90
|
||||
boy/eye-back-low-eyelid
|
||||
bounds: 66, 72, 17, 6
|
||||
boy/eye-back-pupil
|
||||
bounds: 694, 279, 8, 9
|
||||
rotate: 90
|
||||
boy/eye-back-up-eyelid
|
||||
bounds: 460, 101, 23, 5
|
||||
rotate: 90
|
||||
boy/eye-back-up-eyelid-back
|
||||
bounds: 979, 414, 19, 10
|
||||
rotate: 90
|
||||
boy/eye-front-low-eyelid
|
||||
bounds: 1015, 203, 22, 7
|
||||
rotate: 90
|
||||
boy/eye-front-pupil
|
||||
bounds: 309, 50, 9, 9
|
||||
boy/eye-front-up-eyelid
|
||||
bounds: 991, 373, 31, 6
|
||||
boy/eye-front-up-eyelid-back
|
||||
bounds: 107, 76, 26, 9
|
||||
rotate: 90
|
||||
boy/eye-iris-back
|
||||
bounds: 810, 260, 17, 17
|
||||
boy/eye-iris-front
|
||||
bounds: 902, 230, 18, 18
|
||||
boy/eye-white-back
|
||||
bounds: 599, 179, 20, 12
|
||||
boy/eye-white-front
|
||||
bounds: 544, 183, 27, 13
|
||||
boy/eyebrow-back
|
||||
bounds: 1002, 225, 20, 11
|
||||
rotate: 90
|
||||
boy/eyebrow-front
|
||||
bounds: 975, 234, 25, 11
|
||||
boy/hair-back
|
||||
bounds: 629, 289, 122, 81
|
||||
rotate: 90
|
||||
boy/hair-bangs
|
||||
bounds: 505, 180, 70, 37
|
||||
rotate: 90
|
||||
boy/hair-side
|
||||
bounds: 979, 435, 25, 43
|
||||
rotate: 90
|
||||
boy/hand-backfingers
|
||||
bounds: 858, 183, 19, 21
|
||||
boy/hand-front-fingers
|
||||
bounds: 879, 183, 19, 21
|
||||
boy/hat
|
||||
bounds: 218, 121, 93, 56
|
||||
boy/leg-front
|
||||
bounds: 85, 104, 31, 158
|
||||
boy/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl-blue-cape/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl-spring-dress/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
boy/mouth-smile
|
||||
bounds: 1015, 258, 29, 7
|
||||
rotate: 90
|
||||
boy/nose
|
||||
bounds: 323, 79, 17, 10
|
||||
boy/pompom
|
||||
bounds: 979, 462, 48, 43
|
||||
rotate: 90
|
||||
boy/zip
|
||||
bounds: 922, 231, 14, 23
|
||||
rotate: 90
|
||||
girl-blue-cape/back-eyebrow
|
||||
bounds: 527, 106, 18, 12
|
||||
rotate: 90
|
||||
girl-blue-cape/body-dress
|
||||
bounds: 2, 264, 109, 246
|
||||
girl-blue-cape/body-ribbon
|
||||
bounds: 576, 193, 50, 38
|
||||
girl-blue-cape/cape-back
|
||||
bounds: 113, 317, 134, 193
|
||||
girl-blue-cape/cape-back-up
|
||||
bounds: 504, 305, 123, 106
|
||||
girl-blue-cape/cape-ribbon
|
||||
bounds: 396, 118, 50, 18
|
||||
rotate: 90
|
||||
girl-blue-cape/cape-shoulder-back
|
||||
bounds: 420, 243, 49, 59
|
||||
girl-blue-cape/cape-shoulder-front
|
||||
bounds: 2, 2, 62, 76
|
||||
girl-blue-cape/cape-up-front
|
||||
bounds: 118, 145, 98, 117
|
||||
girl-blue-cape/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl-spring-dress/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl-blue-cape/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl-spring-dress/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl-blue-cape/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl-spring-dress/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl-blue-cape/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl-spring-dress/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl-blue-cape/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl-spring-dress/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl-blue-cape/eye-iris-back
|
||||
bounds: 323, 105, 17, 17
|
||||
girl-blue-cape/eye-iris-front
|
||||
bounds: 467, 107, 18, 18
|
||||
girl-blue-cape/eye-white-back
|
||||
bounds: 621, 175, 20, 16
|
||||
girl-spring-dress/eye-white-back
|
||||
bounds: 621, 175, 20, 16
|
||||
girl-blue-cape/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl-spring-dress/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl-blue-cape/front-eyebrow
|
||||
bounds: 309, 101, 18, 12
|
||||
rotate: 90
|
||||
girl-blue-cape/hair-back
|
||||
bounds: 712, 317, 117, 98
|
||||
girl-blue-cape/hair-bangs
|
||||
bounds: 313, 170, 91, 40
|
||||
rotate: 90
|
||||
girl-blue-cape/hair-head-side-back
|
||||
bounds: 544, 198, 30, 52
|
||||
girl-blue-cape/hair-head-side-front
|
||||
bounds: 466, 127, 41, 42
|
||||
girl-blue-cape/hair-side
|
||||
bounds: 175, 2, 36, 71
|
||||
rotate: 90
|
||||
girl-blue-cape/hand-front-fingers
|
||||
bounds: 902, 207, 19, 21
|
||||
girl-spring-dress/hand-front-fingers
|
||||
bounds: 902, 207, 19, 21
|
||||
girl-blue-cape/leg-front
|
||||
bounds: 519, 413, 30, 158
|
||||
rotate: 90
|
||||
girl-blue-cape/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl-spring-dress/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl-blue-cape/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl-spring-dress/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl-blue-cape/sleeve-back
|
||||
bounds: 416, 95, 42, 29
|
||||
girl-blue-cape/sleeve-front
|
||||
bounds: 249, 303, 52, 119
|
||||
rotate: 90
|
||||
girl-spring-dress/arm-front
|
||||
bounds: 829, 292, 17, 111
|
||||
rotate: 90
|
||||
girl-spring-dress/back-eyebrow
|
||||
bounds: 309, 81, 18, 12
|
||||
rotate: 90
|
||||
girl-spring-dress/body-up
|
||||
bounds: 66, 2, 64, 66
|
||||
girl-spring-dress/cloak-down
|
||||
bounds: 758, 227, 50, 50
|
||||
girl-spring-dress/cloak-up
|
||||
bounds: 628, 229, 64, 58
|
||||
girl-spring-dress/eye-iris-back
|
||||
bounds: 342, 105, 17, 17
|
||||
girl-spring-dress/eye-iris-front
|
||||
bounds: 487, 107, 18, 18
|
||||
girl-spring-dress/front-eyebrow
|
||||
bounds: 323, 91, 18, 12
|
||||
girl-spring-dress/hair-back
|
||||
bounds: 370, 417, 147, 93
|
||||
girl-spring-dress/hair-bangs
|
||||
bounds: 829, 250, 91, 40
|
||||
girl-spring-dress/hair-head-side-back
|
||||
bounds: 509, 126, 30, 52
|
||||
girl-spring-dress/hair-head-side-front
|
||||
bounds: 816, 206, 41, 42
|
||||
girl-spring-dress/hair-side
|
||||
bounds: 248, 2, 36, 71
|
||||
rotate: 90
|
||||
girl-spring-dress/leg-front
|
||||
bounds: 831, 381, 30, 158
|
||||
rotate: 90
|
||||
girl-spring-dress/neck
|
||||
bounds: 85, 70, 20, 32
|
||||
girl-spring-dress/shoulder-ribbon
|
||||
bounds: 175, 44, 36, 24
|
||||
girl-spring-dress/skirt
|
||||
bounds: 2, 80, 182, 81
|
||||
rotate: 90
|
||||
girl-spring-dress/underskirt
|
||||
bounds: 519, 445, 175, 65
|
||||
girl/arm-front
|
||||
bounds: 712, 279, 36, 115
|
||||
rotate: 90
|
||||
girl/back-eyebrow
|
||||
bounds: 309, 61, 18, 12
|
||||
rotate: 90
|
||||
girl/bag-base
|
||||
bounds: 694, 219, 62, 58
|
||||
girl/bag-strap-front
|
||||
bounds: 370, 304, 12, 96
|
||||
rotate: 90
|
||||
girl/bag-top
|
||||
bounds: 765, 175, 49, 50
|
||||
girl/body
|
||||
bounds: 370, 318, 97, 132
|
||||
rotate: 90
|
||||
girl/boot-ribbon-front
|
||||
bounds: 323, 64, 13, 13
|
||||
girl/eye-iris-back
|
||||
bounds: 361, 105, 17, 17
|
||||
girl/eye-iris-front
|
||||
bounds: 507, 106, 18, 18
|
||||
girl/eye-white-back
|
||||
bounds: 665, 175, 20, 16
|
||||
girl/front-eyebrow
|
||||
bounds: 343, 91, 18, 12
|
||||
girl/hair-back
|
||||
bounds: 696, 417, 147, 93
|
||||
girl/hair-bangs
|
||||
bounds: 922, 247, 91, 40
|
||||
girl/hair-flap-down-front
|
||||
bounds: 415, 171, 70, 65
|
||||
rotate: 90
|
||||
girl/hair-head-side-back
|
||||
bounds: 991, 381, 30, 52
|
||||
girl/hair-head-side-front
|
||||
bounds: 859, 206, 41, 42
|
||||
girl/hair-patch
|
||||
bounds: 132, 2, 66, 41
|
||||
rotate: 90
|
||||
girl/hair-side
|
||||
bounds: 692, 181, 36, 71
|
||||
rotate: 90
|
||||
girl/hair-strand-back-1
|
||||
bounds: 948, 289, 58, 74
|
||||
rotate: 90
|
||||
girl/hair-strand-back-2
|
||||
bounds: 355, 170, 91, 58
|
||||
rotate: 90
|
||||
girl/hair-strand-back-3
|
||||
bounds: 215, 40, 92, 79
|
||||
girl/hair-strand-front-1
|
||||
bounds: 234, 263, 38, 94
|
||||
rotate: 90
|
||||
girl/hair-strand-front-2
|
||||
bounds: 576, 233, 70, 50
|
||||
rotate: 90
|
||||
girl/hair-strand-front-3
|
||||
bounds: 313, 124, 44, 81
|
||||
rotate: 90
|
||||
girl/hand-front-fingers
|
||||
bounds: 923, 208, 19, 21
|
||||
girl/hat
|
||||
bounds: 218, 179, 93, 82
|
||||
girl/leg-front
|
||||
bounds: 831, 349, 30, 158
|
||||
rotate: 90
|
||||
girl/pompom
|
||||
bounds: 416, 126, 48, 43
|
||||
girl/scarf
|
||||
bounds: 113, 264, 119, 51
|
||||
girl/scarf-back
|
||||
bounds: 502, 252, 72, 51
|
||||
girl/zip
|
||||
bounds: 816, 179, 19, 25
|
||||
BIN
spine-android/app/src/main/assets/mix-and-match-pma.png
Normal file
BIN
spine-android/app/src/main/assets/mix-and-match-pma.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 369 KiB |
BIN
spine-android/app/src/main/assets/mix-and-match-pro.skel
Normal file
BIN
spine-android/app/src/main/assets/mix-and-match-pro.skel
Normal file
Binary file not shown.
@ -0,0 +1,191 @@
|
||||
package com.esotericsoftware.spine
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.esotericsoftware.spine.android.AndroidSkeletonDrawable
|
||||
import com.esotericsoftware.spine.android.SkeletonRenderer
|
||||
import com.esotericsoftware.spine.android.SpineController
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DressUp(nav: NavHostController) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val thumbnailSize = 150f
|
||||
|
||||
val drawable = remember {
|
||||
AndroidSkeletonDrawable.fromAsset(
|
||||
"mix-and-match-pma.atlas",
|
||||
"mix-and-match-pro.skel",
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
val renderer = remember {
|
||||
SkeletonRenderer()
|
||||
}
|
||||
|
||||
val customSkin = remember {
|
||||
mutableStateOf<Skin?>(null)
|
||||
}
|
||||
|
||||
val skinImages = remember {
|
||||
mutableStateMapOf<String, ImageBitmap>()
|
||||
}
|
||||
|
||||
val selectedSkins = remember {
|
||||
mutableStateMapOf<String, Boolean>()
|
||||
}
|
||||
|
||||
val controller = remember {
|
||||
SpineController.Builder()
|
||||
.setOnInitialized {
|
||||
it.animationState.setAnimation(0, "dance", true)
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
fun toggleSkin(skinName: String) {
|
||||
selectedSkins[skinName] = !(selectedSkins[skinName] ?: false)
|
||||
drawable.skeleton.setSkin("default")
|
||||
customSkin.value = Skin("custom-skin");
|
||||
for (skinName2 in selectedSkins.keys) {
|
||||
if (selectedSkins[skinName2] == true) {
|
||||
val skin = drawable.skeletonData.findSkin(skinName)
|
||||
if (skin != null) customSkin.value?.addSkin(skin)
|
||||
}
|
||||
}
|
||||
val customSkinValue = customSkin.value
|
||||
if (customSkinValue != null) {
|
||||
drawable.skeleton.setSkin(customSkinValue)
|
||||
}
|
||||
drawable.skeleton.setSlotsToSetupPose()
|
||||
}
|
||||
|
||||
val localDensity = LocalDensity.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
for (skin in drawable.skeletonData.getSkins()) {
|
||||
if (skin.getName() == "default") continue
|
||||
val skeleton = drawable.skeleton
|
||||
skeleton.setSkin(skin)
|
||||
skeleton.setToSetupPose()
|
||||
skeleton.update(0f)
|
||||
skeleton.updateWorldTransform(Skeleton.Physics.update)
|
||||
skinImages[skin.getName()] = drawable.renderToBitmap(
|
||||
renderer,
|
||||
with(localDensity) { thumbnailSize.dp.toPx() },
|
||||
with(localDensity) { thumbnailSize.dp.toPx() },
|
||||
0xffffffff.toInt()
|
||||
).asImageBitmap()
|
||||
selectedSkins[skin.getName()] = false
|
||||
}
|
||||
toggleSkin("full-skins/girl");
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(text = Destination.DressUp.title) },
|
||||
navigationIcon = {
|
||||
IconButton({ nav.navigateUp() }) {
|
||||
Icon(
|
||||
Icons.Rounded.ArrowBack,
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.width(thumbnailSize.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
Column {
|
||||
skinImages.keys.forEach { skinName ->
|
||||
Box(modifier = Modifier
|
||||
.clickable {
|
||||
toggleSkin(skinName)
|
||||
}
|
||||
.then(
|
||||
if (selectedSkins[skinName] == true) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.grayScale()
|
||||
}
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
painter = BitmapPainter(skinImages[skinName]!!),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AndroidView(
|
||||
// factory = { ctx ->
|
||||
// SpineView(ctx).apply {
|
||||
// loadFromDrawable(drawable, controller)
|
||||
// }
|
||||
// },
|
||||
// modifier = Modifier.padding(paddingValues)
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Modifier.grayScale(): Modifier {
|
||||
val saturationMatrix = ColorMatrix().apply { setToSaturation(0f) }
|
||||
val saturationFilter = ColorFilter.colorMatrix(saturationMatrix)
|
||||
val paint = Paint().apply { colorFilter = saturationFilter }
|
||||
|
||||
return drawWithCache {
|
||||
val canvasBounds = Rect(Offset.Zero, size)
|
||||
onDrawWithContent {
|
||||
drawIntoCanvas {
|
||||
it.saveLayer(canvasBounds, paint)
|
||||
drawContent()
|
||||
it.restore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,6 +62,7 @@ fun AppContent() {
|
||||
Destination.PlayPause,
|
||||
Destination.AnimationStateEvents,
|
||||
Destination.DebugRendering,
|
||||
Destination.DressUp,
|
||||
Destination.IKFollowing,
|
||||
Destination.Physics
|
||||
),
|
||||
@ -94,6 +95,12 @@ fun AppContent() {
|
||||
DebugRendering(navController)
|
||||
}
|
||||
|
||||
composable(
|
||||
Destination.DressUp.route
|
||||
) {
|
||||
DressUp(navController)
|
||||
}
|
||||
|
||||
composable(
|
||||
Destination.IKFollowing.route
|
||||
) {
|
||||
@ -143,6 +150,7 @@ sealed class Destination(val route: String, val title: String) {
|
||||
data object PlayPause : Destination("playPause", "Play/Pause")
|
||||
data object DebugRendering: Destination("debugRendering", "Debug Renderer")
|
||||
data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener")
|
||||
data object DressUp : Destination("dressUp", "Dress Up")
|
||||
data object IKFollowing : Destination("ikFollowing", "IK Following")
|
||||
data object Physics: Destination("physics", "Physics (drag anywhere)")
|
||||
}
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
package com.esotericsoftware.spine.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.esotericsoftware.spine.AnimationState;
|
||||
import com.esotericsoftware.spine.AnimationStateData;
|
||||
import com.esotericsoftware.spine.Skeleton;
|
||||
@ -11,8 +17,6 @@ import com.esotericsoftware.spine.android.utils.SkeletonDataUtils;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
import kotlin.NotImplementedError;
|
||||
|
||||
public class AndroidSkeletonDrawable {
|
||||
|
||||
private final AndroidTextureAtlas atlas;
|
||||
@ -81,4 +85,34 @@ public class AndroidSkeletonDrawable {
|
||||
SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl);
|
||||
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
||||
}
|
||||
|
||||
public Bitmap renderToBitmap(SkeletonRenderer renderer, float width, float height, int bgColor) {
|
||||
Vector2 offset = new Vector2(0, 0);
|
||||
Vector2 size = new Vector2(0, 0);
|
||||
FloatArray floatArray = new FloatArray();
|
||||
|
||||
getSkeleton().getBounds(offset, size, floatArray);
|
||||
|
||||
RectF bounds = new RectF(offset.x, offset.y, offset.x + size.x, offset.y + size.y);
|
||||
float scale = (1 / (bounds.width() > bounds.height() ? bounds.width() / width : bounds.height() / height));
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setColor(bgColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
|
||||
// Draw background
|
||||
canvas.drawRect(0, 0, width, height, paint);
|
||||
|
||||
// Transform canvas
|
||||
canvas.translate(width / 2, height / 2);
|
||||
canvas.scale(scale, -scale);
|
||||
canvas.translate(-(bounds.left + bounds.width() / 2), -(bounds.top + bounds.height() / 2));
|
||||
|
||||
renderer.render(canvas, renderer.render(skeleton));
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,6 +208,12 @@ public class SkeletonRenderer {
|
||||
public void render (Canvas canvas, Array<RenderCommand> commands) {
|
||||
for (int i = 0; i < commands.size; i++) {
|
||||
RenderCommand command = commands.get(i);
|
||||
|
||||
// TODO Fix issue with dressup rendering
|
||||
if (command.blendMode == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, command.vertices.size, command.vertices.items, 0, command.uvs.items, 0,
|
||||
command.colors.items, 0, command.indices.items, 0, command.indices.size, command.texture.getPaint(command.blendMode));
|
||||
}
|
||||
|
||||
@ -124,6 +124,11 @@ public class SpineView extends View implements Choreographer.FrameCallback {
|
||||
loadFrom(() -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl));
|
||||
}
|
||||
|
||||
public void loadFromDrawable(AndroidSkeletonDrawable drawable, SpineController controller) {
|
||||
this.controller = controller;
|
||||
loadFrom(() -> drawable);
|
||||
}
|
||||
|
||||
private void loadFrom(AndroidSkeletonDrawableLoader loader) {
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
Thread backgroundThread = new Thread(() -> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user