mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +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.PlayPause,
|
||||||
Destination.AnimationStateEvents,
|
Destination.AnimationStateEvents,
|
||||||
Destination.DebugRendering,
|
Destination.DebugRendering,
|
||||||
|
Destination.DressUp,
|
||||||
Destination.IKFollowing,
|
Destination.IKFollowing,
|
||||||
Destination.Physics
|
Destination.Physics
|
||||||
),
|
),
|
||||||
@ -94,6 +95,12 @@ fun AppContent() {
|
|||||||
DebugRendering(navController)
|
DebugRendering(navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
Destination.DressUp.route
|
||||||
|
) {
|
||||||
|
DressUp(navController)
|
||||||
|
}
|
||||||
|
|
||||||
composable(
|
composable(
|
||||||
Destination.IKFollowing.route
|
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 PlayPause : Destination("playPause", "Play/Pause")
|
||||||
data object DebugRendering: Destination("debugRendering", "Debug Renderer")
|
data object DebugRendering: Destination("debugRendering", "Debug Renderer")
|
||||||
data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener")
|
data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener")
|
||||||
|
data object DressUp : Destination("dressUp", "Dress Up")
|
||||||
data object IKFollowing : Destination("ikFollowing", "IK Following")
|
data object IKFollowing : Destination("ikFollowing", "IK Following")
|
||||||
data object Physics: Destination("physics", "Physics (drag anywhere)")
|
data object Physics: Destination("physics", "Physics (drag anywhere)")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
package com.esotericsoftware.spine.android;
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
import android.content.Context;
|
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.AnimationState;
|
||||||
import com.esotericsoftware.spine.AnimationStateData;
|
import com.esotericsoftware.spine.AnimationStateData;
|
||||||
import com.esotericsoftware.spine.Skeleton;
|
import com.esotericsoftware.spine.Skeleton;
|
||||||
@ -11,8 +17,6 @@ import com.esotericsoftware.spine.android.utils.SkeletonDataUtils;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import kotlin.NotImplementedError;
|
|
||||||
|
|
||||||
public class AndroidSkeletonDrawable {
|
public class AndroidSkeletonDrawable {
|
||||||
|
|
||||||
private final AndroidTextureAtlas atlas;
|
private final AndroidTextureAtlas atlas;
|
||||||
@ -81,4 +85,34 @@ public class AndroidSkeletonDrawable {
|
|||||||
SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl);
|
SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl);
|
||||||
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
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) {
|
public void render (Canvas canvas, Array<RenderCommand> commands) {
|
||||||
for (int i = 0; i < commands.size; i++) {
|
for (int i = 0; i < commands.size; i++) {
|
||||||
RenderCommand command = commands.get(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,
|
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));
|
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));
|
loadFrom(() -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadFromDrawable(AndroidSkeletonDrawable drawable, SpineController controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
loadFrom(() -> drawable);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadFrom(AndroidSkeletonDrawableLoader loader) {
|
private void loadFrom(AndroidSkeletonDrawableLoader loader) {
|
||||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
Thread backgroundThread = new Thread(() -> {
|
Thread backgroundThread = new Thread(() -> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user