on-the-fly sprite assembly WIP

This commit is contained in:
minjaesong
2019-01-19 04:34:50 +09:00
parent 4c89c1d4c5
commit 971f7d4a40
18 changed files with 341 additions and 62 deletions

View File

@@ -15,6 +15,13 @@ data class Skeleton(val name: String, val joints: List<Joint>) {
override fun toString() = "$name=$joints"
}
/**
* @param name You know it
* @param delay Delay between each frame in seconds
* @param row STARTS AT ONE! Row in the final spritesheet, also act as the animation index.
* @param frames number of frames this animation has
* @param skeleton list of joints to be transformed
*/
data class Animation(val name: String, val delay: Float, val row: Int, val frames: Int, val skeleton: Skeleton) {
override fun toString() = "$name delay: $delay, row: $row, frames: $frames, skeleton: ${skeleton.name}"
}
@@ -45,6 +52,12 @@ class ADProperties {
lateinit var baseFilename: String; private set
lateinit var extension: String; private set
var frameWidth: Int = -1; private set
var frameHeight: Int = -1; private set
var originX: Int = -1; private set
var originY: Int = -1; private set
val origin: ADPropertyObject.Vector2i
get() = ADPropertyObject.Vector2i(originX, originY)
private val animFrameSuffixRegex = Regex("""_[0-9]+""")
@@ -67,6 +80,10 @@ class ADProperties {
continueLoad()
}
constructor(javaProp: Properties) {
this.javaProp.putAll(javaProp.toMap())
}
private fun continueLoad() {
javaProp.keys.forEach { propName ->
val propsStr = javaProp.getProperty(propName as String)
@@ -76,8 +93,13 @@ class ADProperties {
}
// set reserved values for the animation: filename, extension
baseFilename = get("SPRITESHEET")[0].variable
extension = get("EXTENSION")[0].variable
baseFilename = get("SPRITESHEET")[0].name
extension = get("EXTENSION")[0].name
val frameSizeVec = get("CONFIG").linearSearchBy { it.name == "SIZE" }!!.input as ADPropertyObject.Vector2i
frameWidth = frameSizeVec.x
frameHeight = frameSizeVec.y
originX = (get("CONFIG").linearSearchBy { it.name == "ORIGINX" }!!.input as Float).toInt()
originY = frameHeight - 1
var maxColFinder = -1
var maxRowFinder = -1
@@ -110,7 +132,7 @@ class ADProperties {
// and thus, uses whatever the "input" used by the SKELETON is a skeleton
val propsHashMap = HashMap<String, Any?>()
list.forEach {
propsHashMap[it.variable.toUpperCase()] = it.input
propsHashMap[it.name.toUpperCase()] = it.input
}
// if it is indeed anim, populate animations list
@@ -144,11 +166,11 @@ class ADProperties {
val frameName = "${t}_$fc"
val prop = get(frameName)
var emptyList = prop.size == 1 && prop[0].variable.isEmpty()
var emptyList = prop.size == 1 && prop[0].name.isEmpty()
val transformList = if (!emptyList) {
List(prop.size) { index ->
val jointNameToSearch = prop[index].variable.toUpperCase()
val jointNameToSearch = prop[index].name.toUpperCase()
val joint = if (jointNameToSearch == "ALL")
ALL_JOINT
else
@@ -196,7 +218,7 @@ class ADProperties {
private fun getAnimNameFromFrame(s: String) = s.substring(0 until s.lastIndexOf('_'))
private fun List<ADPropertyObject>.toJoints() = List(this.size) {
Joint(this[it].variable.toUpperCase(), this[it].input!! as ADPropertyObject.Vector2i)
Joint(this[it].name.toUpperCase(), this[it].input!! as ADPropertyObject.Vector2i)
}
}
@@ -211,7 +233,7 @@ class ADProperties {
class ADPropertyObject(propertyRaw: String) {
/** If the input is like ```UPPER_TORSO``` (that is, not a variable-input pair), this holds the string UPPER_TORSO. */
val variable: String
val name: String
val input: Any?
get() = when (type) {
ADPropertyType.IVEC2 -> field!! as Vector2i
@@ -226,7 +248,7 @@ class ADPropertyObject(propertyRaw: String) {
val propPair = propertyRaw.split(variableInputSepRegex)
if (isADvariable(propertyRaw)) {
variable = propPair[0]
name = propPair[0]
val inputStr = propPair[1]
if (isADivec2(inputStr)) {
@@ -243,7 +265,7 @@ class ADPropertyObject(propertyRaw: String) {
}
}
else {
variable = propertyRaw
name = propertyRaw
input = null
type = ADPropertyType.NAME_ONLY
}
@@ -287,6 +309,6 @@ class ADPropertyObject(propertyRaw: String) {
}
override fun toString(): String {
return "$variable ${input ?: ""}: ${type.toString().toLowerCase()}"
return "$name ${input ?: ""}: ${type.toString().toLowerCase()}"
}
}

View File

@@ -86,6 +86,7 @@ These values must exist so that the file can be parsed successfully.
|---|---|---|
|SPRITESHEET|properties: NAME_ONLY|Base file name of the images|
|EXTENSION|properties: NAME_ONLY|Extension of the base file|
|CONFIG|properties: 2 variables|Frame size and origin-x position, 0 being left|
#### Animation

View File

@@ -15,14 +15,14 @@ import java.io.File
*/
object AssembleSheetPixmap {
operator fun invoke(properties: ADProperties, assembleConfig: AssembleConfig = AssembleConfig()): Pixmap {
val canvas = Pixmap(properties.cols * assembleConfig.fw, properties.rows * assembleConfig.fh, Pixmap.Format.RGBA8888)
operator fun invoke(properties: ADProperties): Pixmap {
val canvas = Pixmap(properties.cols * properties.frameWidth, properties.rows * properties.frameHeight, Pixmap.Format.RGBA8888)
canvas.blending = Pixmap.Blending.SourceOver
// actually draw
properties.transforms.forEach { t, _ ->
drawThisFrame(t, canvas, properties, assembleConfig)
drawThisFrame(t, canvas, properties)
}
return canvas
@@ -30,8 +30,7 @@ object AssembleSheetPixmap {
private fun drawThisFrame(frameName: String,
canvas: Pixmap,
properties: ADProperties,
assembleConfig: AssembleConfig
properties: ADProperties
) {
val theAnim = properties.getAnimByFrameName(frameName)
val skeleton = theAnim.skeleton.joints.reversed()
@@ -56,23 +55,23 @@ object AssembleSheetPixmap {
AppLoader.printdbg(this, "Frame to draw: $frameName (R$animRow C$animFrame)")
drawFrame(animRow, animFrame, canvas, bodyparts, transformList, assembleConfig)
drawFrame(animRow, animFrame, canvas, properties, bodyparts, transformList)
bodyparts.forEach { it?.dispose() }
}
private fun drawFrame(row: Int, column: Int,
canvas: Pixmap,
props: ADProperties,
bodyparts: Array<Pixmap?>,
transformList: List<Pair<String, ADPropertyObject.Vector2i>>,
assembleConfig: AssembleConfig
transformList: List<Pair<String, ADPropertyObject.Vector2i>>
) {
val tmpFrame = Pixmap(assembleConfig.fw, assembleConfig.fh, Pixmap.Format.RGBA8888)
val tmpFrame = Pixmap(props.frameWidth, props.frameHeight, Pixmap.Format.RGBA8888)
bodyparts.forEachIndexed { index, image ->
if (image != null) {
val imgCentre = AssembleFrameBase.getCentreOf(image)
val drawPos = transformList[index].second.invertY() + assembleConfig.origin - imgCentre
val drawPos = transformList[index].second.invertY() + props.origin - imgCentre
tmpFrame.drawPixmap(image, drawPos.x, drawPos.y)
}
@@ -80,8 +79,8 @@ object AssembleSheetPixmap {
canvas.drawPixmap(
tmpFrame,
(column - 1) * assembleConfig.fw,
(row - 1) * assembleConfig.fh
(column - 1) * props.frameWidth,
(row - 1) * props.frameHeight
)
tmpFrame.dispose()
@@ -90,13 +89,6 @@ object AssembleSheetPixmap {
}
/**
* @param fw Frame Width
* @param fh Frame Height
* @param origin Int vector of origin point, (0,0) being TOP-LEFT
*/
data class AssembleConfig(val fw: Int = 48, val fh: Int = 56, val origin: ADPropertyObject.Vector2i = ADPropertyObject.Vector2i(29, fh - 1))
object AssembleFrameBase {
/**
* Returns joints list with tranform applied.

View File

@@ -94,7 +94,7 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
}
panelCode.font = Font(Font.MONOSPACED, Font.PLAIN, 11)
panelCode.font = Font(Font.MONOSPACED, Font.PLAIN, 12)
panelAnimationsList.model = DefaultListModel()
panelBodypartsList.model = DefaultListModel()
@@ -172,6 +172,8 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
// populate stats
(panelStatList.model as DefaultListModel).addElement("Spritesheet rows: ${adProperties.rows}")
(panelStatList.model as DefaultListModel).addElement("Spritesheet columns: ${adProperties.cols}")
(panelStatList.model as DefaultListModel).addElement("Frame size: ${adProperties.frameWidth}, ${adProperties.frameHeight}")
(panelStatList.model as DefaultListModel).addElement("Origin position: ${adProperties.originX}, ${adProperties.originY}")
}
catch (fehler: Throwable) {
displayError("ERROR_PARSE_FAIL", fehler)
@@ -289,6 +291,7 @@ class SpriteAssemblerPreview: Game() {
image = AssembleSheetPixmap(prop)
}
// TODO rename to requestAssembly
fun requestAssemblyTest(prop: ADProperties) {
doAssemble = true
assembleProp = prop

View File

@@ -1,5 +1,8 @@
# complete file name is: SPRITESHEET + bodypart name + EXTENSION
SPRITESHEET=mods/basegame/sprites/sprite_assembler_test_assets/test_
EXTENSION=.tga
# defines frame size and origin point. Origin point is given as: (originx, size.y - 1)
CONFIG=SIZE 48,56;ORIGINX 29
# note to self: don't implement skeleton hierarchy: there's too many exceptions
# besides, you have "ALL" key.
@@ -29,5 +32,4 @@ ANIM_RUN_4=ALL 0,1;LEG_REST_RIGHT 0,1;FOOT_RIGHT 0,1;LEG_REST_LEFT 0,-1;FOOT_LEF
ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND
ANIM_IDLE_1=
! ANIM_IDLE_1 will not make any transformation
ANIM_IDLE_2=UPPER_TORSO 0,-1;HEAD 0,-1;HAIR 0,-1;HELD_ITEM 0,-1;ARM_REST_LEFT 0,-1;ARM_REST_RIGHT 0,-1;HAIR_FORE 0,-1;HEADGEAR 0,-1
! HAND_REST is deliberately left out -- it's bit unsettling to see held item keeps moving
ANIM_IDLE_2=UPPER_TORSO 0,-1;HEAD 0,-1;HAIR 0,-1;HELD_ITEM 0,-1;ARM_REST_LEFT 0,-1;ARM_REST_RIGHT 0,-1;HAND_REST_LEFT 0,-1;HAND_REST_RIGHT 0,-1;HAIR_FORE 0,-1;HEADGEAR 0,-1