diff --git a/spine-ue4/Config/DefaultEditor.ini b/spine-ue4/Config/DefaultEditor.ini new file mode 100644 index 000000000..6838df001 --- /dev/null +++ b/spine-ue4/Config/DefaultEditor.ini @@ -0,0 +1,5 @@ +[EditoronlyBP] +bAllowClassAndBlueprintPinMatching=true +bReplaceBlueprintWithClass=true +bDontLoadBlueprintOutsideEditor=true +bBlueprintIsNotBlueprintType=true diff --git a/spine-ue4/Config/DefaultEngine.ini b/spine-ue4/Config/DefaultEngine.ini new file mode 100644 index 000000000..244a1d163 --- /dev/null +++ b/spine-ue4/Config/DefaultEngine.ini @@ -0,0 +1,9 @@ +[URL] + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + + diff --git a/spine-ue4/Config/DefaultGame.ini b/spine-ue4/Config/DefaultGame.ini new file mode 100644 index 000000000..579ad9274 --- /dev/null +++ b/spine-ue4/Config/DefaultGame.ini @@ -0,0 +1,2 @@ +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=A32BDAC45F432EB90DF445842A670D58 diff --git a/spine-ue4/Content/Maps/example.umap b/spine-ue4/Content/Maps/example.umap new file mode 100644 index 000000000..77170e83c Binary files /dev/null and b/spine-ue4/Content/Maps/example.umap differ diff --git a/spine-ue4/Content/SpineBoy/Textures/NewPaperSprite.uasset b/spine-ue4/Content/SpineBoy/Textures/NewPaperSprite.uasset new file mode 100644 index 000000000..e3ff7e2c2 Binary files /dev/null and b/spine-ue4/Content/SpineBoy/Textures/NewPaperSprite.uasset differ diff --git a/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset b/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset new file mode 100644 index 000000000..f49942272 Binary files /dev/null and b/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset differ diff --git a/spine-ue4/Content/SpineBoy/spineboy.atlas b/spine-ue4/Content/SpineBoy/spineboy.atlas new file mode 100644 index 000000000..2454a1f47 --- /dev/null +++ b/spine-ue4/Content/SpineBoy/spineboy.atlas @@ -0,0 +1,216 @@ + +spineboy.png +size: 1024,1024 +format: RGBA8888 +filter: Linear,Linear +repeat: none +eye_indifferent + rotate: true + xy: 648, 629 + size: 93, 89 + orig: 93, 89 + offset: 0, 0 + index: -1 +eye_surprised + rotate: true + xy: 233, 179 + size: 93, 89 + orig: 93, 89 + offset: 0, 0 + index: -1 +front_bracer + rotate: false + xy: 245, 2 + size: 58, 80 + orig: 58, 80 + offset: 0, 0 + index: -1 +front_fist_closed + rotate: false + xy: 168, 45 + size: 75, 82 + orig: 75, 82 + offset: 0, 0 + index: -1 +front_fist_open + rotate: false + xy: 844, 646 + size: 86, 87 + orig: 86, 87 + offset: 0, 0 + index: -1 +front_foot + rotate: true + xy: 310, 326 + size: 126, 69 + orig: 126, 69 + offset: 0, 0 + index: -1 +front_foot_bend1 + rotate: true + xy: 951, 894 + size: 128, 70 + orig: 128, 70 + offset: 0, 0 + index: -1 +front_foot_bend2 + rotate: false + xy: 2, 33 + size: 108, 93 + orig: 108, 93 + offset: 0, 0 + index: -1 +front_shin + rotate: true + xy: 739, 735 + size: 82, 184 + orig: 82, 184 + offset: 0, 0 + index: -1 +front_thigh + rotate: false + xy: 381, 340 + size: 48, 112 + orig: 48, 112 + offset: 0, 0 + index: -1 +front_upper_arm + rotate: false + xy: 112, 29 + size: 54, 97 + orig: 54, 97 + offset: 0, 0 + index: -1 +goggles + rotate: false + xy: 156, 454 + size: 261, 166 + orig: 261, 166 + offset: 0, 0 + index: -1 +gun + rotate: false + xy: 739, 819 + size: 210, 203 + orig: 210, 203 + offset: 0, 0 + index: -1 +head + rotate: false + xy: 466, 724 + size: 271, 298 + orig: 271, 298 + offset: 0, 0 + index: -1 +hoverboard_board + rotate: true + xy: 2, 128 + size: 492, 152 + orig: 492, 152 + offset: 0, 0 + index: -1 +hoverboard_thruster + rotate: false + xy: 602, 558 + size: 60, 64 + orig: 60, 64 + offset: 0, 0 + index: -1 +hoverglow_small + rotate: true + xy: 156, 178 + size: 274, 75 + orig: 274, 75 + offset: 0, 0 + index: -1 +mouth_grind + rotate: true + xy: 951, 799 + size: 93, 59 + orig: 93, 59 + offset: 0, 0 + index: -1 +mouth_oooo + rotate: true + xy: 245, 84 + size: 93, 59 + orig: 93, 59 + offset: 0, 0 + index: -1 +mouth_smile + rotate: false + xy: 925, 738 + size: 93, 59 + orig: 93, 59 + offset: 0, 0 + index: -1 +muzzle + rotate: false + xy: 2, 622 + size: 462, 400 + orig: 462, 400 + offset: 0, 0 + index: -1 +neck + rotate: false + xy: 168, 2 + size: 36, 41 + orig: 36, 41 + offset: 0, 0 + index: -1 +rear_bracer + rotate: false + xy: 932, 664 + size: 56, 72 + orig: 56, 72 + offset: 0, 0 + index: -1 +rear_foot + rotate: false + xy: 487, 562 + size: 113, 60 + orig: 113, 60 + offset: 0, 0 + index: -1 +rear_foot_bend1 + rotate: true + xy: 419, 503 + size: 117, 66 + orig: 117, 66 + offset: 0, 0 + index: -1 +rear_foot_bend2 + rotate: false + xy: 739, 650 + size: 103, 83 + orig: 103, 83 + offset: 0, 0 + index: -1 +rear_shin + rotate: false + xy: 233, 274 + size: 75, 178 + orig: 75, 178 + offset: 0, 0 + index: -1 +rear_thigh + rotate: true + xy: 487, 495 + size: 65, 104 + orig: 65, 104 + offset: 0, 0 + index: -1 +rear_upper_arm + rotate: true + xy: 156, 129 + size: 47, 87 + orig: 47, 87 + offset: 0, 0 + index: -1 +torso + rotate: true + xy: 466, 624 + size: 98, 180 + orig: 98, 180 + offset: 0, 0 + index: -1 diff --git a/spine-ue4/Content/SpineBoy/spineboy.json b/spine-ue4/Content/SpineBoy/spineboy.json new file mode 100644 index 000000000..b63de2a04 --- /dev/null +++ b/spine-ue4/Content/SpineBoy/spineboy.json @@ -0,0 +1,2419 @@ +{ +"skeleton": { + "hash": "H7+xXqKKrnDC1fiM9L7F+WiY4ro", + "spine": "3.5.03-beta", + "width": 470.9, + "height": 731.49, + "fps": 30, + "images": "./images/" +}, +"bones": [ + { "name": "hip", "y": 247.47 }, + { "name": "torso", "parent": "hip", "length": 127.55, "rotation": 103.82, "x": -1.61, "y": 4.9, "color": "e0da19ff" }, + { "name": "front_upper_arm", "parent": "torso", "length": 69.45, "rotation": 168.37, "x": 103.75, "y": 19.32, "color": "00ff04ff" }, + { + "name": "front_bracer", + "parent": "front_upper_arm", + "length": 40.57, + "rotation": 18.29, + "x": 68.8, + "y": -0.68, + "color": "00ff04ff" + }, + { "name": "front_fist", "parent": "front_bracer", "length": 65.38, "rotation": 12.43, "x": 40.56, "y": 0.19, "color": "00ff04ff" }, + { "name": "front_thigh", "parent": "hip", "length": 74.8, "rotation": -95.51, "x": -17.45, "y": -11.64, "color": "00ff04ff" }, + { "name": "front_shin", "parent": "front_thigh", "length": 128.76, "rotation": -2.21, "x": 78.69, "y": 1.6, "color": "00ff04ff" }, + { "name": "front_foot", "parent": "front_shin", "length": 91.34, "rotation": 77.9, "x": 128.75, "y": -0.33, "color": "00ff04ff" }, + { "name": "rear_upper_arm", "parent": "torso", "length": 51.93, "rotation": -169.55, "x": 92.35, "y": -19.22, "color": "ff000dff" }, + { "name": "rear_bracer", "parent": "rear_upper_arm", "length": 34.55, "rotation": 23.15, "x": 51.35, "color": "ff000dff" }, + { "name": "gun", "parent": "rear_bracer", "length": 43.1, "rotation": 5.34, "x": 34.42, "y": -0.45, "color": "ff000dff" }, + { "name": "gunTip", "parent": "gun", "rotation": 6.83, "x": 201.04, "y": 52.13, "color": "ff000dff" }, + { "name": "neck", "parent": "torso", "length": 25.45, "rotation": -31.53, "x": 127.49, "y": -0.3, "color": "e0da19ff" }, + { "name": "head", "parent": "neck", "length": 263.57, "rotation": 23.18, "x": 27.66, "y": -0.25, "color": "e0da19ff" }, + { "name": "rear_thigh", "parent": "hip", "length": 85.71, "rotation": -72.54, "x": 8.91, "y": -5.62, "color": "ff000dff" }, + { "name": "rear_shin", "parent": "rear_thigh", "length": 121.87, "rotation": -19.83, "x": 86.1, "y": -1.32, "color": "ff000dff" }, + { "name": "rear_foot", "parent": "rear_shin", "length": 82.57, "rotation": 69.3, "x": 121.45, "y": -0.75, "color": "ff000dff" } +], +"slots": [ + { "name": "rear_upper_arm", "bone": "rear_upper_arm", "attachment": "rear_upper_arm" }, + { "name": "rear_bracer", "bone": "rear_bracer", "attachment": "rear_bracer" }, + { "name": "gun", "bone": "gun", "attachment": "gun" }, + { "name": "rear_foot", "bone": "rear_foot", "attachment": "rear_foot" }, + { "name": "rear_thigh", "bone": "rear_thigh", "attachment": "rear_thigh" }, + { "name": "rear_shin", "bone": "rear_shin", "attachment": "rear_shin" }, + { "name": "neck", "bone": "neck", "attachment": "neck" }, + { "name": "torso", "bone": "torso", "attachment": "torso" }, + { "name": "front_upper_arm", "bone": "front_upper_arm", "attachment": "front_upper_arm" }, + { "name": "head", "bone": "head", "attachment": "head" }, + { "name": "eye", "bone": "head", "attachment": "eye_indifferent" }, + { "name": "front_thigh", "bone": "front_thigh", "attachment": "front_thigh" }, + { "name": "front_foot", "bone": "front_foot", "attachment": "front_foot" }, + { "name": "front_shin", "bone": "front_shin", "attachment": "front_shin" }, + { "name": "mouth", "bone": "head", "attachment": "mouth_smile" }, + { "name": "goggles", "bone": "head", "attachment": "goggles" }, + { "name": "front_bracer", "bone": "front_bracer", "attachment": "front_bracer" }, + { "name": "front_fist", "bone": "front_fist", "attachment": "front_fist_closed" }, + { "name": "muzzle", "bone": "gunTip", "blend": "additive" }, + { "name": "head-bb", "bone": "head" } +], +"skins": { + "default": { + "eye": { + "eye_indifferent": { "x": 85.72, "y": -28.18, "rotation": -70.63, "width": 93, "height": 89 }, + "eye_surprised": { "x": 85.72, "y": -28.18, "rotation": -70.63, "width": 93, "height": 89 } + }, + "front_bracer": { + "front_bracer": { "x": 12.03, "y": -1.67, "rotation": 79.59, "width": 58, "height": 80 } + }, + "front_fist": { + "front_fist_closed": { "x": 35.49, "y": 6, "rotation": 67.16, "width": 75, "height": 82 }, + "front_fist_open": { "x": 39.56, "y": 7.76, "rotation": 67.16, "width": 86, "height": 87 } + }, + "front_foot": { + "front_foot": { "x": 29.51, "y": 7.83, "rotation": 18.68, "width": 126, "height": 69 }, + "front_foot_bend1": { "x": 29.51, "y": 7.83, "rotation": 18.68, "width": 128, "height": 70 }, + "front_foot_bend2": { "x": 16.07, "y": 13.83, "rotation": 18.68, "width": 108, "height": 93 } + }, + "front_shin": { + "front_shin": { "x": 55.11, "y": -3.54, "rotation": 96.59, "width": 82, "height": 184 } + }, + "front_thigh": { + "front_thigh": { "x": 42.47, "y": 4.44, "rotation": 84.86, "width": 48, "height": 112 } + }, + "front_upper_arm": { + "front_upper_arm": { "x": 28.3, "y": 7.37, "rotation": 97.89, "width": 54, "height": 97 } + }, + "goggles": { + "goggles": { "x": 97.07, "y": 6.54, "rotation": -70.63, "width": 261, "height": 166 } + }, + "gun": { + "gun": { "x": 77.3, "y": 16.4, "rotation": 60.82, "width": 210, "height": 203 } + }, + "head": { + "head": { "x": 128.95, "y": 0.29, "rotation": -70.63, "width": 271, "height": 298 } + }, + "head-bb": { + "head": { + "type": "boundingbox", + "vertexCount": 6, + "vertices": [ -19.13999, -70.3, 40.79999, -118.06999, 257.77, -115.61, 285.16, 57.18, 120.76999, 164.95, -5.05999, 76.94 ] + } + }, + "mouth": { + "mouth_grind": { "x": 23.68, "y": -32.23, "rotation": -70.63, "width": 93, "height": 59 }, + "mouth_oooo": { "x": 23.68, "y": -32.23, "rotation": -70.63, "width": 93, "height": 59 }, + "mouth_smile": { "x": 23.68, "y": -32.23, "rotation": -70.63, "width": 93, "height": 59 } + }, + "muzzle": { + "muzzle": { "x": 18.25, "y": 5.44, "rotation": 0.15, "width": 462, "height": 400 } + }, + "neck": { + "neck": { "x": 9.76, "y": -3.01, "rotation": -55.22, "width": 36, "height": 41 } + }, + "rear_bracer": { + "rear_bracer": { "x": 11.15, "y": -2.2, "rotation": 66.17, "width": 56, "height": 72 } + }, + "rear_foot": { + "rear_foot": { "x": 31.51, "y": 3.57, "rotation": 23.07, "width": 113, "height": 60 }, + "rear_foot_bend1": { "x": 34.39, "y": 4.8, "rotation": 23.07, "width": 117, "height": 66 }, + "rear_foot_bend2": { "x": 30.38, "y": 12.62, "rotation": 23.07, "width": 103, "height": 83 } + }, + "rear_shin": { + "rear_shin": { "x": 58.29, "y": -2.75, "rotation": 92.37, "width": 75, "height": 178 } + }, + "rear_thigh": { + "rear_thigh": { "x": 33.1, "y": -4.11, "rotation": 72.54, "width": 65, "height": 104 } + }, + "rear_upper_arm": { + "rear_upper_arm": { "x": 21.12, "y": 4.08, "rotation": 89.32, "width": 47, "height": 87 } + }, + "torso": { + "torso": { "x": 63.61, "y": 7.12, "rotation": -94.53, "width": 98, "height": 180 } + } + } +}, +"events": { + "footstep": {}, + "headAttach": { "int": 3, "float": 4 }, + "headBehind": { "int": 5, "float": 6, "string": "setup" }, + "headPop": { "int": 1, "float": 2 } +}, +"animations": { + "death": { + "slots": { + "eye": { + "attachment": [ + { "time": 0, "name": "eye_surprised" }, + { "time": 0.4666, "name": "eye_indifferent" }, + { "time": 2.2333, "name": "eye_surprised" }, + { "time": 4.5333, "name": "eye_indifferent" } + ] + }, + "front_fist": { + "attachment": [ + { "time": 0, "name": "front_fist_open" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0, "name": "mouth_oooo" }, + { "time": 2.2333, "name": "mouth_grind" }, + { "time": 4.5333, "name": "mouth_oooo" } + ] + } + }, + "bones": { + "head": { + "rotate": [ + { "time": 0, "angle": -2.82 }, + { "time": 0.1333, "angle": -28.74 }, + { "time": 0.2333, "angle": 11.42 }, + { "time": 0.3333, "angle": -50.24 }, + { "time": 0.4, "angle": -72.66, "curve": "stepped" }, + { "time": 0.4333, "angle": -72.66 }, + { "time": 0.5, "angle": -20.24 }, + { "time": 0.5666, "angle": -85.28, "curve": "stepped" }, + { "time": 0.9333, "angle": -85.28, "curve": "stepped" }, + { "time": 2.2333, "angle": -85.28 }, + { "time": 2.5, "angle": -51.96, "curve": "stepped" }, + { "time": 4.5333, "angle": -51.96 }, + { "time": 4.6666, "angle": -85.28 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": -2.82 }, + { "time": 0.1333, "angle": 12.35 }, + { "time": 0.2333, "angle": 29.89 }, + { "time": 0.3, "angle": 70.36 }, + { "time": 0.4, "angle": -10.22, "curve": "stepped" }, + { "time": 0.4333, "angle": -10.22 }, + { "time": 0.5, "angle": 2.92 }, + { "time": 0.5666, "angle": 47.94, "curve": "stepped" }, + { "time": 2.2333, "angle": 47.94 }, + { "time": 2.5, "angle": 18.5, "curve": "stepped" }, + { "time": 4.5333, "angle": 18.5 }, + { "time": 4.6666, "angle": 47.94 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "torso": { + "rotate": [ + { "time": 0, "angle": -8.61 }, + { "time": 0.1333, "angle": 28.19 }, + { "time": 0.2666, "angle": -280.19 }, + { "time": 0.4, "angle": -237.22, "curve": "stepped" }, + { "time": 0.4333, "angle": -237.22 }, + { "time": 0.5, "angle": 76.03, "curve": "stepped" }, + { "time": 0.8, "angle": 76.03, "curve": "stepped" }, + { "time": 0.9333, "angle": 76.03, "curve": "stepped" }, + { "time": 2.2333, "angle": 76.03 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 2.2333, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_upper_arm": { + "rotate": [ + { "time": 0, "angle": -38.85 }, + { "time": 0.1333, "angle": -299.58 }, + { "time": 0.2666, "angle": -244.74 }, + { "time": 0.4, "angle": -292.35 }, + { "time": 0.4333, "angle": -315.84 }, + { "time": 0.5, "angle": -347.94 }, + { "time": 0.6999, "angle": -347.33, "curve": "stepped" }, + { "time": 2.2333, "angle": -347.33 }, + { "time": 2.7, "angle": -290.68 }, + { "time": 2.7666, "angle": -285.1 }, + { "time": 4.6666, "angle": -290.68 }, + { "time": 4.8, "angle": 8.61 }, + { "time": 4.8666, "angle": 10.94 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { "time": 0, "angle": -44.69 }, + { "time": 0.1333, "angle": 112.26 }, + { "time": 0.2666, "angle": 129.07 }, + { "time": 0.4, "angle": 134.94, "curve": "stepped" }, + { "time": 0.4333, "angle": 134.94 }, + { "time": 0.5666, "angle": 172.6, "curve": "stepped" }, + { "time": 0.9333, "angle": 172.6, "curve": "stepped" }, + { "time": 2.2333, "angle": 172.6 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_bracer": { + "rotate": [ + { "time": 0, "angle": 21.88 }, + { "time": 0.1333, "angle": 11.48 }, + { "time": 0.2666, "angle": -18.81 }, + { "time": 0.4, "angle": -18.92 }, + { "time": 0.4333, "angle": -18.28 }, + { "time": 0.5, "angle": 60.61 }, + { "time": 0.6999, "angle": -18.87, "curve": "stepped" }, + { "time": 2.2333, "angle": -18.87 }, + { "time": 2.7, "angle": -1.95, "curve": "stepped" }, + { "time": 4.6666, "angle": -1.95 }, + { "time": 4.8, "angle": 34.55 }, + { "time": 4.9333, "angle": -18.74 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_fist": { + "rotate": [ + { "time": 0, "angle": -2.33 }, + { "time": 0.2666, "angle": 26.34 }, + { "time": 0.6999, "angle": -6.07, "curve": "stepped" }, + { "time": 2.2333, "angle": -6.07 }, + { "time": 2.7, "angle": 5.72, "curve": "stepped" }, + { "time": 4.6666, "angle": 5.72 }, + { "time": 4.8666, "angle": -6.52 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_bracer": { + "rotate": [ + { "time": 0, "angle": 10.36 }, + { "time": 0.1333, "angle": -23.12 }, + { "time": 0.2666, "angle": -23.11 }, + { "time": 0.4, "angle": -23.16, "curve": "stepped" }, + { "time": 0.4333, "angle": -23.16 }, + { "time": 0.5666, "angle": -23.2, "curve": "stepped" }, + { "time": 0.9333, "angle": -23.2, "curve": "stepped" }, + { "time": 2.2333, "angle": -23.2 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "gun": { + "rotate": [ + { "time": 0, "angle": -2.78 }, + { "time": 0.1333, "angle": -24.58 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.9333, "angle": 0, "curve": "stepped" }, + { "time": 2.2333, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.2, "x": 50.34, "y": 151.73 }, + { "time": 0.4, "x": 5.16, "y": -119.64, "curve": "stepped" }, + { "time": 0.4333, "x": 5.16, "y": -119.64 }, + { "time": 0.5, "x": 50.34, "y": -205.18, "curve": "stepped" }, + { "time": 0.8, "x": 50.34, "y": -205.18, "curve": "stepped" }, + { "time": 0.9333, "x": 50.34, "y": -205.18, "curve": "stepped" }, + { "time": 2.2333, "x": 50.34, "y": -205.18 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_thigh": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1333, "angle": 8.47 }, + { "time": 0.2666, "angle": 115.95 }, + { "time": 0.4, "angle": 180.66, "curve": "stepped" }, + { "time": 0.4333, "angle": 180.66 }, + { "time": 0.5, "angle": 155.22 }, + { "time": 0.6, "angle": 97.73 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_shin": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1333, "angle": -27.37 }, + { "time": 0.2666, "angle": -35.1 }, + { "time": 0.4, "angle": -37.72, "curve": "stepped" }, + { "time": 0.4333, "angle": -37.72 }, + { "time": 0.5, "angle": -40.06 }, + { "time": 0.6, "angle": 2.76 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_thigh": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1333, "angle": 70.45 }, + { "time": 0.2666, "angle": 155.34 }, + { "time": 0.4, "angle": 214.31, "curve": "stepped" }, + { "time": 0.4333, "angle": 214.31 }, + { "time": 0.5, "angle": 169.67 }, + { "time": 0.8, "angle": 83.27 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_shin": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1333, "angle": 18.93 }, + { "time": 0.2666, "angle": -21.04 }, + { "time": 0.4, "angle": -29.93, "curve": "stepped" }, + { "time": 0.4333, "angle": -29.93 }, + { "time": 0.5, "angle": -16.79 }, + { "time": 0.8, "angle": 7.77 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_foot": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1333, "angle": -11.62 }, + { "time": 0.4, "angle": -45.59, "curve": "stepped" }, + { "time": 0.4333, "angle": -45.59 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_foot": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.4, "angle": -48.75, "curve": "stepped" }, + { "time": 0.4333, "angle": -48.75 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "gunTip": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + } + } + }, + "hit": { + "slots": { + "front_fist": { + "attachment": [ + { "time": 0.1666, "name": "front_fist_open" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0, "name": "mouth_grind" }, + { "time": 0.3333, "name": "mouth_smile" } + ] + } + }, + "bones": { + "torso": { + "rotate": [ + { "time": 0, "angle": 56.42 }, + { "time": 0.3333, "angle": 8.89 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 35.38 }, + { "time": 0.2333, "angle": 24.94 } + ] + }, + "head": { + "rotate": [ + { "time": 0, "angle": 10.21 }, + { "time": 0.3333, "angle": -41.3 } + ] + }, + "front_upper_arm": { + "rotate": [ + { + "time": 0, + "angle": -310.92, + "curve": [ 0.38, 0.53, 0.744, 1 ] + }, + { "time": 0.3333, "angle": -112.59 } + ], + "translate": [ + { "time": 0, "x": 7.23, "y": -13.13 } + ] + }, + "front_bracer": { + "rotate": [ + { "time": 0, "angle": 36.99 }, + { "time": 0.3333, "angle": -28.64 } + ] + }, + "front_fist": { + "rotate": [ + { "time": 0, "angle": 13.59 }, + { "time": 0.3333, "angle": 7.55 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { + "time": 0, + "angle": 271.02, + "curve": [ 0.342, 0.36, 0.68, 0.71 ] + }, + { "time": 0.3333, "angle": -15.84 } + ], + "translate": [ + { "time": 0.3333, "x": -0.09, "y": -0.46 } + ] + }, + "rear_bracer": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": 40.03 } + ] + }, + "gun": { + "rotate": [ + { "time": 0, "angle": 14.98 }, + { "time": 0.3333, "angle": 39.75 } + ] + }, + "hip": { + "translate": [ + { "time": 0, "x": -75.54, "y": -78.03 }, + { "time": 0.2333, "x": -36.48, "y": 12.42 }, + { "time": 0.3333, "x": -36.48, "y": -2.99 } + ] + }, + "front_thigh": { + "rotate": [ + { + "time": 0, + "angle": 90.94, + "curve": [ 0.227, 0.26, 0.432, 1 ] + }, + { "time": 0.3333, "angle": 32.02 } + ], + "translate": [ + { "time": 0, "x": 7.21, "y": -4 } + ] + }, + "rear_thigh": { + "rotate": [ + { + "time": 0, + "angle": 40.51, + "curve": [ 0.295, 0.3, 0.59, 0.99 ] + }, + { "time": 0.3333, "angle": 90.76 } + ], + "translate": [ + { "time": 0, "x": -1.96, "y": -0.32 } + ] + }, + "front_shin": { + "rotate": [ + { "time": 0, "angle": -96.62 }, + { "time": 0.3333, "angle": -15.13 } + ] + }, + "rear_shin": { + "rotate": [ + { "time": 0, "angle": 7.99 }, + { "time": 0.3333, "angle": -67.54 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_foot": { + "rotate": [ + { "time": 0, "angle": 5.4 }, + { "time": 0.3333, "angle": -16.26 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_foot": { + "rotate": [ + { "time": 0, "angle": 2.67 }, + { "time": 0.3333, "angle": -10.31 } + ] + } + } + }, + "idle": { + "slots": { + "front_fist": { + "attachment": [ + { "time": 0, "name": "front_fist_open" }, + { "time": 1.6666, "name": "front_fist_open" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0, "name": "mouth_smile" }, + { "time": 1.6666, "name": "mouth_smile" } + ] + } + }, + "bones": { + "torso": { + "rotate": [ + { + "time": 0, + "angle": -5.61, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.8333, + "angle": -9.65, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1.6666, "angle": -5.61 } + ], + "translate": [ + { "time": 0, "x": -6.49, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "front_upper_arm": { + "rotate": [ + { + "time": 0, + "angle": -59.85, + "curve": [ 0.492, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": -54.31, + "curve": [ 0.324, 0.11, 0.75, 1 ] + }, + { "time": 1.6666, "angle": -59.85 } + ], + "translate": [ + { "time": 0, "x": -7.12, "y": -8.23 }, + { "time": 0.6666, "x": -6.32, "y": -8.3 }, + { "time": 1.6666, "x": -7.12, "y": -8.23 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { + "time": 0, + "angle": 62.41, + "curve": [ 0.504, 0.02, 0.75, 1 ] + }, + { + "time": 0.7333, + "angle": 43.83, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1.6666, "angle": 62.41 } + ], + "translate": [ + { "time": 0, "x": -1.83, "y": -16.78 }, + { "time": 0.6666, "x": 0.34, "y": -15.23 }, + { "time": 1.6666, "x": -1.83, "y": -16.78 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.6666, "angle": 2.39 }, + { "time": 1.6666, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": -1.88, "y": -4.76, "curve": "stepped" }, + { "time": 1.6666, "x": -1.88, "y": -4.76 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "front_thigh": { + "rotate": [ + { + "time": 0, + "angle": 0.64, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "angle": -4.34, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "angle": 0.64 } + ], + "translate": [ + { "time": 0, "x": -13.39, "y": 6.69, "curve": "stepped" }, + { "time": 1.6666, "x": -13.39, "y": 6.69 } + ], + "scale": [ + { + "time": 0, + "x": 0.896, + "y": 1, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "x": 0.825, + "y": 1, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "x": 0.896, "y": 1 } + ] + }, + "front_shin": { + "rotate": [ + { "time": 0, "angle": -19.28, "curve": "stepped" }, + { "time": 1.6666, "angle": -19.28 } + ], + "scale": [ + { + "time": 0, + "x": 1, + "y": 1, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "x": 0.994, + "y": 1, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "rear_thigh": { + "rotate": [ + { + "time": 0, + "angle": 30.5, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "angle": 40.15, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "angle": 30.5 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "rear_shin": { + "rotate": [ + { + "time": 0, + "angle": -23.83, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "angle": -43.77, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "angle": -23.83 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "front_foot": { + "rotate": [ + { + "time": 0, + "angle": 5.13, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "angle": 10.04, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "angle": 5.13 } + ], + "scale": [ + { "time": 0, "x": 0.755, "y": 1.309, "curve": "stepped" }, + { "time": 1.6666, "x": 0.755, "y": 1.309 } + ] + }, + "hip": { + "translate": [ + { + "time": 0, + "x": -6.63, + "y": -23.01, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "x": 6.27, + "y": -35, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "x": -6.63, "y": -23.01 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "rear_foot": { + "rotate": [ + { + "time": 0, + "angle": -7.34, + "curve": [ 0.235, 0, 0.558, 0.99 ] + }, + { + "time": 0.6666, + "angle": 3.85, + "curve": [ 0.594, 0, 0.653, 1 ] + }, + { "time": 1.6666, "angle": -7.34 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "rear_bracer": { + "rotate": [ + { + "time": 0, + "angle": -17.16, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": 12.52, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1.6666, "angle": -17.16 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "head": { + "rotate": [ + { + "time": 0, + "angle": -5.51, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": -3.12, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1.6666, "angle": -5.51 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "front_bracer": { + "rotate": [ + { + "time": 0, + "angle": 45.46, + "curve": [ 0.492, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": 41.33, + "curve": [ 0.32, 0.1, 0.736, 0.91 ] + }, + { "time": 1.6666, "angle": 45.46 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "gun": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": -15.59, + "curve": [ 0.732, 0, 0.769, 0.99 ] + }, + { "time": 1.6666, "angle": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + }, + "front_fist": { + "rotate": [ + { + "time": 0, + "angle": -6.84, + "curve": [ 0.492, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": -14.63, + "curve": [ 0.324, 0.11, 0.75, 1 ] + }, + { "time": 1.6666, "angle": -6.84 } + ], + "scale": [ + { + "time": 0, + "x": 1, + "y": 1, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "x": 0.689, + "y": 1.1, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1.6666, "x": 1, "y": 1 } + ] + } + } + }, + "jump": { + "slots": { + "front_fist": { + "attachment": [ + { "time": 0, "name": "front_fist_open" }, + { "time": 0.2, "name": "front_fist_closed" }, + { "time": 0.6666, "name": "front_fist_open" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0, "name": "mouth_grind" } + ] + }, + "torso": { + "attachment": [ + { "time": 0, "name": "torso" } + ] + } + }, + "bones": { + "front_thigh": { + "rotate": [ + { + "time": 0, + "angle": 91.53, + "curve": [ 0.278, 0.46, 0.763, 1 ] + }, + { + "time": 0.2, + "angle": -35.83, + "curve": [ 0.761, 0, 0.75, 1 ] + }, + { "time": 0.4333, "angle": 127.74 }, + { + "time": 0.7333, + "angle": 48.18, + "curve": [ 0.227, 0.26, 0.432, 1 ] + }, + { "time": 0.8333, "angle": 25.35 }, + { "time": 0.9333, "angle": 45.37 }, + { "time": 1.0333, "angle": 38.12 }, + { "time": 1.1333, "angle": 25.35 }, + { "time": 1.3333, "angle": 91.53 } + ], + "translate": [ + { "time": 0, "x": -2.56, "y": 5.77 }, + { "time": 0.4333, "x": 8.3, "y": 7.98 }, + { "time": 0.7333, "x": 7.21, "y": -4 }, + { "time": 1.3333, "x": -2.56, "y": 5.77 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "torso": { + "rotate": [ + { "time": 0, "angle": -42.63 }, + { "time": 0.2, "angle": -5.74 }, + { "time": 0.4333, "angle": -50.76 }, + { "time": 0.7333, "angle": 1.89 }, + { "time": 0.8333, "angle": 11.58 }, + { "time": 0.9666, "angle": -1.89 }, + { "time": 1.1333, "angle": 11.58 }, + { "time": 1.3333, "angle": -42.63 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_thigh": { + "rotate": [ + { "time": 0, "angle": -26.32 }, + { "time": 0.2, "angle": 121.44 }, + { "time": 0.4333, "angle": 70.54 }, + { + "time": 0.7333, + "angle": 79.89, + "curve": [ 0.295, 0.3, 0.59, 0.99 ] + }, + { "time": 0.8333, "angle": 99.12 }, + { "time": 0.9333, "angle": 74.05 }, + { "time": 1.0333, "angle": 98.04 }, + { "time": 1.1333, "angle": 99.12 }, + { "time": 1.3333, "angle": -26.32 } + ], + "translate": [ + { "time": 0, "x": -0.56, "y": -0.32 }, + { "time": 0.4333, "x": -8.5, "y": 10.58 }, + { "time": 0.7333, "x": -1.96, "y": -0.32 }, + { "time": 1.3333, "x": -0.56, "y": -0.32 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_shin": { + "rotate": [ + { "time": 0, "angle": -78.69 }, + { "time": 0.4333, "angle": -55.56 }, + { "time": 0.7333, "angle": -62.84 }, + { "time": 0.8333, "angle": -80.74 }, + { "time": 0.9333, "angle": -41.12 }, + { "time": 1.0333, "angle": -77.4 }, + { "time": 1.1333, "angle": -80.74 }, + { "time": 1.3333, "angle": -78.69 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.7333, "x": 1, "y": 1 } + ] + }, + "front_upper_arm": { + "rotate": [ + { "time": 0, "angle": -22.61 }, + { "time": 0.2, "angle": -246.68 }, + { + "time": 0.6, + "angle": 11.28, + "curve": [ 0.246, 0, 0.633, 0.53 ] + }, + { + "time": 0.7333, + "angle": -57.45, + "curve": [ 0.38, 0.53, 0.744, 1 ] + }, + { "time": 0.8666, "angle": -112.59 }, + { "time": 0.9333, "angle": -102.17 }, + { "time": 1.0333, "angle": -108.61 }, + { "time": 1.1333, "angle": -112.59 }, + { "time": 1.3333, "angle": -22.61 } + ], + "translate": [ + { "time": 0, "x": 6.08, "y": 7.15 }, + { "time": 0.2, "x": 7.23, "y": -13.13, "curve": "stepped" }, + { "time": 0.7333, "x": 7.23, "y": -13.13 }, + { "time": 1.3333, "x": 6.08, "y": 7.15 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_bracer": { + "rotate": [ + { "time": 0, "angle": 66.46 }, + { "time": 0.2, "angle": 42.39 }, + { "time": 0.4333, "angle": 26.06 }, + { "time": 0.7333, "angle": 13.28 }, + { "time": 0.8666, "angle": -28.64 }, + { "time": 0.9333, "angle": -22.31 }, + { "time": 1.0333, "angle": -35.39 }, + { "time": 1.1333, "angle": -28.64 }, + { "time": 1.3333, "angle": 66.46 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_fist": { + "rotate": [ + { "time": 0, "angle": -28.43 }, + { "time": 0.4333, "angle": -45.6 }, + { "time": 0.7333, "angle": -53.66 }, + { "time": 0.8666, "angle": 7.55 }, + { "time": 0.9333, "angle": 31.15 }, + { "time": 1.0333, "angle": -32.58 }, + { "time": 1.1333, "angle": 7.55 }, + { "time": 1.3333, "angle": -28.43 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { "time": 0, "angle": 39.68 }, + { "time": 0.2, "angle": 276.57 }, + { "time": 0.3, "angle": 17.73 }, + { "time": 0.4333, "angle": 83.38 }, + { + "time": 0.6, + "angle": -4.71, + "curve": [ 0.246, 0, 0.633, 0.53 ] + }, + { + "time": 0.7333, + "angle": -69.63, + "curve": [ 0.342, 0.36, 0.68, 0.71 ] + }, + { + "time": 0.7666, + "angle": 321.47, + "curve": [ 0.333, 0.33, 0.667, 0.66 ] + }, + { + "time": 0.8, + "angle": 33.7, + "curve": [ 0.358, 0.64, 0.693, 1 ] + }, + { "time": 0.8666, "angle": 34.56 }, + { "time": 1.0333, "angle": 71.96 }, + { "time": 1.1333, "angle": 34.56 }, + { "time": 1.3333, "angle": 39.68 } + ], + "translate": [ + { "time": 0, "x": -3.1, "y": -4.86 }, + { "time": 0.2, "x": 23.33, "y": 49.07 }, + { "time": 0.4333, "x": 20.78, "y": 40.21 }, + { "time": 1.3333, "x": -3.1, "y": -4.86 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_bracer": { + "rotate": [ + { "time": 0, "angle": 29.66 }, + { "time": 0.2, "angle": 45.06 }, + { "time": 0.4333, "angle": -4.34 }, + { "time": 0.7666, "angle": 61.68 }, + { "time": 0.8, "angle": 82.59 }, + { "time": 0.8666, "angle": 80.06 }, + { "time": 1.0333, "angle": 57.56 }, + { "time": 1.1333, "angle": 80.06 }, + { "time": 1.3333, "angle": 29.66 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 24.9 }, + { "time": 0.2, "angle": 16.31 }, + { "time": 0.4333, "angle": 7.44 }, + { "time": 0.7333, "angle": -20.35 }, + { "time": 0.8333, "angle": -0.69, "curve": "stepped" }, + { "time": 1.1333, "angle": -0.69 }, + { "time": 1.3333, "angle": 24.9 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "head": { + "rotate": [ + { "time": 0, "angle": 24.92 }, + { "time": 0.2, "angle": 10.36 }, + { "time": 0.4333, "angle": 28.65 }, + { "time": 0.7333, "angle": -2.65 }, + { "time": 0.8333, "angle": -28.94, "curve": "stepped" }, + { "time": 1.1333, "angle": -28.94 }, + { "time": 1.3333, "angle": 24.92 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "hip": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -34.51, + "y": -78.62, + "curve": [ 0.232, 1, 0.75, 1 ] + }, + { + "time": 0.2, + "x": -34.51, + "y": 182.5, + "curve": [ 0.232, 0.48, 0.598, 0.79 ] + }, + { + "time": 0.7666, + "x": -34.51, + "y": 596.22, + "curve": [ 0.329, 0.17, 0.66, 0.21 ] + }, + { "time": 1.1333, "x": -34.51, "y": 2.49 }, + { "time": 1.3333, "x": -34.51, "y": -78.62 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_shin": { + "rotate": [ + { + "time": 0, + "angle": -90.62, + "curve": [ 0.416, 0.54, 0.743, 1 ] + }, + { + "time": 0.2, + "angle": -10.52, + "curve": [ 0.644, 0, 0.75, 1 ] + }, + { "time": 0.4333, "angle": -127.72 }, + { "time": 0.7333, "angle": -19.91 }, + { "time": 0.8333, "angle": -5.16 }, + { "time": 0.9333, "angle": -35.06 }, + { "time": 1.0333, "angle": -43.97 }, + { "time": 1.1333, "angle": -5.16 }, + { "time": 1.3333, "angle": -90.62 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "front_foot": { + "rotate": [ + { "time": 0, "angle": -0.79 }, + { "time": 0.0333, "angle": 16.27 }, + { "time": 0.0666, "angle": 23.52 }, + { "time": 0.1, "angle": 21.02 }, + { "time": 0.1333, "angle": 10.92 }, + { "time": 0.2, "angle": -38.45 }, + { "time": 0.4333, "angle": 6.62 }, + { "time": 0.7333, "angle": -11.51 }, + { "time": 1.0333, "angle": -22.91 }, + { "time": 1.3333, "angle": -0.79 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "rear_foot": { + "rotate": [ + { "time": 0, "angle": -12.77 }, + { "time": 0.2, "angle": 17.05 }, + { "time": 0.4333, "angle": 19.45 }, + { "time": 0.7333, "angle": 2.67 }, + { "time": 1.0333, "angle": -28.49 }, + { "time": 1.3333, "angle": -12.77 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + }, + "gun": { + "rotate": [ + { "time": 0, "angle": 6.18 }, + { "time": 0.2, "angle": 30.81 }, + { "time": 0.4333, "angle": 13.25 }, + { "time": 0.7333, "angle": 14.98 }, + { "time": 0.7666, "angle": 25.64 }, + { "time": 0.8, "angle": 20.62 }, + { "time": 0.8666, "angle": 64.52 }, + { "time": 1.0333, "angle": 8.59 }, + { "time": 1.1333, "angle": 64.52 }, + { "time": 1.3333, "angle": 6.18 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 } + ] + } + } + }, + "run": { + "slots": { + "front_fist": { + "attachment": [ + { "time": 0, "name": "front_fist_closed" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0, "name": "mouth_grind" } + ] + }, + "torso": { + "attachment": [ + { "time": 0, "name": "torso" } + ] + } + }, + "bones": { + "front_thigh": { + "rotate": [ + { + "time": 0, + "angle": 42.05, + "curve": [ 0.195, 0.86, 0.75, 1 ] + }, + { "time": 0.0666, "angle": 46.07 }, + { "time": 0.1333, "angle": -20.28 }, + { "time": 0.2, "angle": -27.23 }, + { "time": 0.2666, "angle": -47.16 }, + { "time": 0.3333, "angle": -39.79 }, + { "time": 0.4, "angle": -25.86 }, + { "time": 0.4666, "angle": 14.35 }, + { "time": 0.5333, "angle": 55.62 }, + { "time": 0.6, "angle": 69.65 }, + { "time": 0.6666, "angle": 86.4 }, + { "time": 0.7333, "angle": 65.87 }, + { "time": 0.8, "angle": 42.05 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.0333, "x": -5.79, "y": 11.15 }, + { "time": 0.0666, "x": -5.13, "y": 11.55 }, + { "time": 0.1333, "x": -7.7, "y": 8.98 }, + { "time": 0.5333, "x": -1.26, "y": 3.83 }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "torso": { + "rotate": [ + { "time": 0, "angle": -39.7 }, + { "time": 0.2, "angle": -57.29 }, + { "time": 0.4, "angle": -39.7 }, + { "time": 0.6, "angle": -57.29 }, + { "time": 0.8, "angle": -39.7 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.4, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_thigh": { + "rotate": [ + { "time": 0, "angle": -56.59 }, + { "time": 0.0666, "angle": -21.57 }, + { "time": 0.1333, "angle": 27.95 }, + { "time": 0.2, "angle": 42.42 }, + { "time": 0.2666, "angle": 62.37 }, + { "time": 0.3333, "angle": 45.42 }, + { "time": 0.4, "angle": 15.67 }, + { "time": 0.4666, "angle": 28.22 }, + { "time": 0.5333, "angle": -38.62 }, + { "time": 0.6, "angle": -53.26 }, + { "time": 0.6666, "angle": -79.31 }, + { "time": 0.7333, "angle": -86.47 }, + { "time": 0.8, "angle": -56.59 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.4, "x": -6.76, "y": -3.86 }, + { "time": 0.4333, "x": -15.85, "y": 7.28 }, + { "time": 0.4666, "x": -13.04, "y": 4.04 }, + { "time": 0.5, "x": -10.24, "y": 7.11 }, + { "time": 0.5333, "x": -9.01, "y": -5.15 }, + { "time": 0.6666, "x": -23.18, "y": -2.57 }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_shin": { + "rotate": [ + { "time": 0, "angle": -74 }, + { "time": 0.0666, "angle": -83.38 }, + { "time": 0.1333, "angle": -106.69 }, + { "time": 0.2, "angle": -66.01 }, + { "time": 0.2666, "angle": -55.22 }, + { "time": 0.3333, "angle": -24.8 }, + { + "time": 0.4, + "angle": 18.44, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.4666, "angle": -56.65 }, + { + "time": 0.5333, + "angle": -11.94, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "angle": -41.26 }, + { "time": 0.7333, "angle": -43.6 }, + { "time": 0.8, "angle": -74 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_upper_arm": { + "rotate": [ + { "time": 0, "angle": -89.36 }, + { "time": 0.0666, "angle": -95.67 }, + { "time": 0.1333, "angle": -22 }, + { "time": 0.2, "angle": -316.04 }, + { "time": 0.2666, "angle": -274.94 }, + { "time": 0.3333, "angle": -273.74 }, + { "time": 0.4, "angle": -272.09 }, + { "time": 0.4666, "angle": -264.89 }, + { "time": 0.5333, "angle": -320.09 }, + { "time": 0.6, "angle": -50.83 }, + { "time": 0.6666, "angle": -81.72 }, + { "time": 0.7333, "angle": -83.92 }, + { "time": 0.8, "angle": -89.36 } + ], + "translate": [ + { "time": 0, "x": 6.24, "y": 10.05 }, + { "time": 0.2666, "x": 4.95, "y": -13.13 }, + { "time": 0.6, "x": -2.43, "y": 1.94 }, + { "time": 0.8, "x": 6.24, "y": 10.05 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_bracer": { + "rotate": [ + { "time": 0, "angle": 33.43 }, + { "time": 0.0666, "angle": 20.53 }, + { "time": 0.1333, "angle": 15.26 }, + { "time": 0.2, "angle": 19.28 }, + { "time": 0.2666, "angle": 22.62 }, + { "time": 0.3333, "angle": 37.29 }, + { "time": 0.4, "angle": 41.53 }, + { "time": 0.4666, "angle": 31.73 }, + { "time": 0.5333, "angle": 67.45 }, + { "time": 0.6666, "angle": 39.77 }, + { "time": 0.7333, "angle": 30.95 }, + { "time": 0.8, "angle": 33.43 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_fist": { + "rotate": [ + { "time": 0, "angle": -19.75 }, + { "time": 0.0666, "angle": -37.11 }, + { "time": 0.1333, "angle": -50.79 }, + { "time": 0.2666, "angle": -12.69 }, + { "time": 0.3333, "angle": 3.01 }, + { "time": 0.4333, "angle": 12.05 }, + { "time": 0.5333, "angle": 13.25 }, + { "time": 0.8, "angle": -19.75 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { "time": 0, "angle": 68.68 }, + { "time": 0.0666, "angle": 73.89 }, + { "time": 0.1333, "angle": -9.64 }, + { "time": 0.2, "angle": 284.27 }, + { "time": 0.2666, "angle": 283.29 }, + { "time": 0.3333, "angle": 278.28 }, + { "time": 0.4, "angle": 271.02 }, + { "time": 0.4666, "angle": 263.2 }, + { "time": 0.5333, "angle": 314.25 }, + { "time": 0.6, "angle": 16.83 }, + { "time": 0.6666, "angle": 70.35 }, + { "time": 0.7333, "angle": 73.53 }, + { "time": 0.8, "angle": 68.68 } + ], + "translate": [ + { "time": 0, "x": -2.57, "y": -8.89 }, + { "time": 0.1333, "x": -4.68, "y": 7.2 }, + { "time": 0.2, "x": 21.73, "y": 51.17 }, + { "time": 0.6, "x": 4.33, "y": 2.05 }, + { "time": 0.8, "x": -2.57, "y": -8.89 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_bracer": { + "rotate": [ + { "time": 0, "angle": 31.04 }, + { "time": 0.0666, "angle": 28.28 }, + { "time": 0.1333, "angle": 49.36 }, + { "time": 0.2, "angle": 59.37 }, + { "time": 0.2666, "angle": 8.56 }, + { "time": 0.3333, "angle": 9.38 }, + { "time": 0.4, "angle": 11.51 }, + { "time": 0.4666, "angle": 7.22 }, + { "time": 0.5333, "angle": -18.44 }, + { "time": 0.6, "angle": 11.44 }, + { "time": 0.6666, "angle": 9.99 }, + { "time": 0.7333, "angle": 8.28 }, + { "time": 0.8, "angle": 31.04 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 11.03 }, + { "time": 0.2, "angle": 13.58 }, + { "time": 0.4, "angle": 11.03 }, + { "time": 0.6, "angle": 13.58 }, + { "time": 0.8, "angle": 11.03 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.4, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "head": { + "rotate": [ + { "time": 0, "angle": 11.03 }, + { "time": 0.1, "angle": 12.34 }, + { "time": 0.2, "angle": 25.55 }, + { "time": 0.4, "angle": 11.03 }, + { "time": 0.5, "angle": 12.34 }, + { "time": 0.6, "angle": 25.55 }, + { "time": 0.8, "angle": 11.03 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.4, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": -62.47, "y": -23.1 }, + { + "time": 0.0666, + "x": -62.47, + "y": -38.51, + "curve": [ 0.244, 0.04, 0.75, 1 ] + }, + { + "time": 0.2666, + "x": -62.47, + "y": 22.28, + "curve": [ 0.17, 0.52, 0.75, 1 ] + }, + { "time": 0.4, "x": -62.47, "y": -23.1 }, + { "time": 0.4333, "x": -62.47, "y": -24.59 }, + { + "time": 0.4666, + "x": -62.47, + "y": -43.29, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "x": -62.47, "y": 22.28 }, + { "time": 0.8, "x": -62.47, "y": -23.1 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_shin": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.481, 0.01, 0.75, 1 ] + }, + { "time": 0.0666, "angle": -64.42 }, + { + "time": 0.1333, + "angle": -20.59, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.2666, "angle": -62.51 }, + { "time": 0.3333, "angle": -79.74 }, + { "time": 0.4, "angle": -78.28 }, + { + "time": 0.4666, + "angle": -118.96, + "curve": [ 0.93, 0, 0.952, 0.95 ] + }, + { "time": 0.6, "angle": -88.95 }, + { "time": 0.6666, "angle": -79.09 }, + { "time": 0.7333, "angle": -47.77 }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_foot": { + "rotate": [ + { "time": 0, "angle": 0 }, + { + "time": 0.0333, + "angle": -21.13, + "curve": [ 0.121, 0.23, 0.75, 1 ] + }, + { "time": 0.0666, "angle": 17.64 }, + { "time": 0.1, "angle": 29.92 }, + { "time": 0.1333, "angle": 16.44 }, + { "time": 0.2, "angle": -29.22 }, + { "time": 0.2666, "angle": -1.61 }, + { "time": 0.3333, "angle": -10.22 }, + { "time": 0.4666, "angle": -15.99 }, + { "time": 0.6, "angle": 9.03 }, + { "time": 0.7333, "angle": 17.32 }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_foot": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.0666, "angle": -12.04 }, + { "time": 0.1333, "angle": -0.87 }, + { "time": 0.2, "angle": 25.81 }, + { "time": 0.2666, "angle": 4.71 }, + { + "time": 0.4, + "angle": 18.09, + "curve": [ 0.281, 0.73, 0.75, 1 ] + }, + { "time": 0.4333, "angle": -1.7 }, + { "time": 0.4666, "angle": 27.12 }, + { "time": 0.5, "angle": 38.83 }, + { "time": 0.5333, "angle": 30.76 }, + { "time": 0.5666, "angle": -20.49 }, + { "time": 0.6, "angle": -30.8 }, + { "time": 0.6666, "angle": -1.31 }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "gun": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1333, "angle": 24.72 }, + { "time": 0.5, "angle": -11.87 }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + } + }, + "events": [ + { "time": 0, "name": "footstep" }, + { "time": 0.4, "name": "footstep", "int": 1 } + ] + }, + "shoot": { + "slots": { + "front_fist": { + "attachment": [ + { "time": 0.1333, "name": "front_fist_closed" }, + { "time": 0.4, "name": "front_fist_open" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0.1333, "name": "mouth_grind" } + ] + }, + "muzzle": { + "color": [ + { + "time": 0.1333, + "color": "ffffff00", + "curve": [ 0.118, 0.99, 0.75, 1 ] + }, + { + "time": 0.1666, + "color": "ffffffff", + "curve": [ 0.821, 0, 0.909, 0.89 ] + }, + { "time": 0.2666, "color": "ffffff00" } + ], + "attachment": [ + { "time": 0.1333, "name": "muzzle" }, + { "time": 0.2666, "name": null } + ] + } + }, + "bones": { + "front_fist": { + "scale": [ + { "time": 0.1333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.4, "x": 1, "y": 1 } + ] + }, + "gunTip": { + "translate": [ + { "time": 0.1333, "x": 0, "y": 0 }, + { "time": 0.2, "x": 20.93, "y": 1.57 } + ], + "scale": [ + { "time": 0.1333, "x": 1, "y": 1 }, + { "time": 0.2, "x": 1.247, "y": 1.516 } + ] + }, + "gun": { + "rotate": [ + { "time": 0, "angle": 1.9 } + ], + "translate": [ + { + "time": 0, + "x": 7.95, + "y": 5.84, + "curve": [ 0, 0.3, 0.678, 1 ] + }, + { "time": 0.3, "x": -9.3, "y": -1.41 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "rear_bracer": { + "rotate": [ + { "time": 0, "angle": -30.47 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0, 0.3, 0.678, 1 ] + }, + { "time": 0.3, "x": -5.99, "y": -3.71 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { "time": 0, "angle": 62.3 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0, 0.3, 0.678, 1 ] + }, + { "time": 0.3, "x": 2.81, "y": 11.41 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + } + } + }, + "test": { + "slots": { + "front_foot": { + "color": [ + { "time": 0.6666, "color": "ffffffff" }, + { "time": 1.3333, "color": "ff0700ff" } + ] + }, + "gun": { + "color": [ + { "time": 0, "color": "ffffffff", "curve": "stepped" }, + { "time": 0.6666, "color": "ffffffff" }, + { "time": 1.3333, "color": "32ff00ff" } + ] + }, + "rear_foot": { + "color": [ + { "time": 0.6666, "color": "ffffffff" }, + { "time": 1.3333, "color": "ff0700ff" } + ] + } + }, + "bones": { + "head": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": -20.72 }, + { "time": 0.6666, "angle": -32.41 }, + { "time": 1, "angle": -5.3 }, + { "time": 1.3333, "angle": 24.96 }, + { "time": 1.6666, "angle": 15.61 }, + { "time": 2, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.172, 0.37, 0.574, 0.73 ] + }, + { + "time": 0.1666, + "x": 144.19, + "y": -77.59, + "curve": [ 0.372, 0.61, 0.765, 1 ] + }, + { + "time": 0.3333, + "x": 217.61, + "y": -192.63, + "curve": [ 0.282, 0, 0.624, 0.31 ] + }, + { + "time": 0.5, + "x": 181.21, + "y": -365.66, + "curve": [ 0.313, 0.21, 0.654, 0.54 ] + }, + { + "time": 0.6666, + "x": 20.09, + "y": -500.4, + "curve": [ 0.147, 0.27, 0.75, 1 ] + }, + { "time": 0.8333, "x": -194.24, "y": -341.84 }, + { "time": 1, "x": -307.93, "y": -114 }, + { + "time": 1.1666, + "x": -330.38, + "y": 121.42, + "curve": [ 0.25, 0, 0.764, 0.48 ] + }, + { + "time": 1.3333, + "x": -240.42, + "y": 335.66, + "curve": [ 0.229, 0.37, 0.58, 0.73 ] + }, + { + "time": 1.5, + "x": -56.12, + "y": 288.06, + "curve": [ 0.296, 0.6, 0.641, 1 ] + }, + { + "time": 1.6666, + "x": 87.63, + "y": 191.33, + "curve": [ 0.238, 0, 0.626, 0.39 ] + }, + { + "time": 1.8333, + "x": 60.62, + "y": 95.14, + "curve": [ 0.41, 0.26, 0.803, 0.62 ] + }, + { "time": 2, "x": 0, "y": 0 } + ] + } + }, + "drawOrder": [ + { + "time": 0.6666, + "offsets": [ + { "slot": "head", "offset": -9 }, + { "slot": "eye", "offset": -9 }, + { "slot": "mouth", "offset": -12 }, + { "slot": "goggles", "offset": -12 } + ] + }, + { "time": 1.3333 } + ], + "events": [ + { "time": 0, "name": "headPop", "int": 0, "float": 0, "string": "pop.wav" }, + { "time": 1, "name": "headBehind", "int": 7, "float": 8, "string": "animate" }, + { "time": 2, "name": "headAttach", "int": 0, "float": 0, "string": "attach.wav" } + ] + }, + "walk": { + "slots": { + "front_fist": { + "attachment": [ + { "time": 0, "name": "front_fist_closed" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0, "name": "mouth_smile" } + ] + }, + "torso": { + "attachment": [ + { "time": 0, "name": "torso" } + ] + } + }, + "bones": { + "front_thigh": { + "rotate": [ + { "time": 0, "angle": 15.79 }, + { "time": 0.1, "angle": 27.39 }, + { "time": 0.2, "angle": -7.94 }, + { "time": 0.3, "angle": -16.94 }, + { "time": 0.4, "angle": -28.62 }, + { "time": 0.5, "angle": -19.3 }, + { "time": 0.6, "angle": -3.08 }, + { "time": 0.6999, "angle": 29.51 }, + { "time": 0.8, "angle": 15.79 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.4, "x": -1.18, "y": 0.54 }, + { "time": 0.5, "x": 0.11, "y": 0.41 }, + { "time": 0.6, "x": 9.48, "y": 0.27 }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.4, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_shin": { + "rotate": [ + { "time": 0, "angle": 5.12 }, + { "time": 0.1, "angle": -20.87 }, + { "time": 0.2, "angle": 13.37 }, + { "time": 0.3, "angle": 15.98 }, + { "time": 0.4, "angle": 5.94 }, + { "time": 0.5, "angle": -26.76 }, + { "time": 0.6999, "angle": -55.44 }, + { "time": 0.8, "angle": 5.12 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_thigh": { + "rotate": [ + { "time": 0, "angle": -34.38 }, + { "time": 0.1, "angle": -30.32 }, + { "time": 0.2, "angle": -37.22 }, + { "time": 0.3, "angle": 20.73 }, + { "time": 0.4, "angle": 8.69 }, + { "time": 0.5, "angle": 12.16 }, + { "time": 0.6, "angle": -24.62 }, + { "time": 0.6999, "angle": -27.26 }, + { "time": 0.8, "angle": -34.38 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.4, "x": 4.08, "y": -9.53 }, + { "time": 0.5, "x": 0, "y": 0 }, + { "time": 0.6999, "x": -21.14, "y": -9.6 }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_shin": { + "rotate": [ + { "time": 0, "angle": 14.26 }, + { "time": 0.1, "angle": -17.3 }, + { "time": 0.2, "angle": -12.67 }, + { "time": 0.3, "angle": -58.89 }, + { "time": 0.4, "angle": 15.95 }, + { "time": 0.5, "angle": -9 }, + { "time": 0.6, "angle": 26.06 }, + { "time": 0.6999, "angle": 21.85 }, + { "time": 0.8, "angle": 14.26 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1 }, + { "time": 0.1, "x": 0.951, "y": 1 }, + { "time": 0.5, "x": 0.975, "y": 1 }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_foot": { + "rotate": [ + { "time": 0, "angle": 10.13 }, + { "time": 0.1, "angle": 12.27 }, + { "time": 0.2, "angle": -2.94 }, + { "time": 0.3, "angle": 6.29 }, + { "time": 0.4, "angle": 13.45 }, + { "time": 0.5, "angle": -3.57 }, + { "time": 0.6, "angle": -0.97 }, + { "time": 0.6999, "angle": 2.97 }, + { "time": 0.8, "angle": 10.13 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_upper_arm": { + "rotate": [ + { "time": 0, "angle": -23.74 }, + { "time": 0.4, "angle": -320.57 }, + { "time": 0.8, "angle": -23.74 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_upper_arm": { + "rotate": [ + { "time": 0, "angle": 11.62 }, + { "time": 0.1, "angle": 19.36 }, + { "time": 0.4, "angle": 345.26 }, + { "time": 0.5, "angle": 343.44 }, + { "time": 0.8, "angle": 11.62 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "torso": { + "rotate": [ + { "time": 0, "angle": -12.11 }, + { "time": 0.1666, "angle": -17.16 }, + { "time": 0.4, "angle": -12.11 }, + { "time": 0.5666, "angle": -15.81 }, + { "time": 0.8, "angle": -12.11 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 1.41 }, + { "time": 0.2333, "angle": -3.04 }, + { "time": 0.4, "angle": 1.41 }, + { "time": 0.6333, "angle": -3.04 }, + { "time": 0.8, "angle": 1.41 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.4, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "head": { + "rotate": [ + { "time": 0, "angle": 6.97 }, + { "time": 0.1666, "angle": 8.02 }, + { "time": 0.2666, "angle": 12.65 }, + { "time": 0.4, "angle": 6.97 }, + { "time": 0.5666, "angle": 8.02 }, + { "time": 0.6666, "angle": 12.65 }, + { "time": 0.8, "angle": 6.97 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.4, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -23.93, + "y": 3.22, + "curve": [ 0.518, 0.03, 0.807, 0.61 ] + }, + { + "time": 0.1, + "x": -23.93, + "y": -9.24, + "curve": [ 0.135, 0.33, 0.601, 0.99 ] + }, + { + "time": 0.2, + "x": -23.93, + "y": 4.35, + "curve": [ 0.204, 0.68, 0.75, 1 ] + }, + { + "time": 0.3, + "x": -23.93, + "y": 2.38, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.4, + "x": -23.93, + "y": -2.5, + "curve": [ 0.692, 0.01, 0.75, 1 ] + }, + { + "time": 0.5, + "x": -23.93, + "y": -10.32, + "curve": [ 0.235, 0.77, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -23.93, + "y": 4.35, + "curve": [ 0.287, 0.37, 0.718, 0.76 ] + }, + { + "time": 0.6999, + "x": -23.93, + "y": 10.34, + "curve": [ 0.615, 0, 0.75, 1 ] + }, + { "time": 0.8, "x": -23.93, "y": 3.22 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_bracer": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.4, "angle": 20.59 }, + { "time": 0.8, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_foot": { + "rotate": [ + { "time": 0, "angle": 12.49 }, + { "time": 0.1, "angle": -8.34 }, + { "time": 0.2, "angle": -6.17 }, + { "time": 0.3, "angle": -0.75 }, + { "time": 0.3333, "angle": 3.89 }, + { "time": 0.4, "angle": 10.22 }, + { "time": 0.5, "angle": 11.44 }, + { "time": 0.6, "angle": -0.33 }, + { "time": 0.6999, "angle": 0.15 }, + { "time": 0.8, "angle": 12.49 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "rear_bracer": { + "rotate": [ + { "time": 0, "angle": 3.58 }, + { "time": 0.1, "angle": 5.51 }, + { "time": 0.4, "angle": -22.77 }, + { "time": 0.5, "angle": -9.65 }, + { "time": 0.8, "angle": 3.58 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "front_fist": { + "rotate": [ + { "time": 0, "angle": -15.22 }, + { "time": 0.1, "angle": -51.4 }, + { "time": 0.4, "angle": -39.4 }, + { "time": 0.5, "angle": 19.26 }, + { "time": 0.8, "angle": -15.22 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + }, + "gun": { + "rotate": [ + { + "time": 0, + "angle": -24.06, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.1, + "angle": -10.94, + "curve": [ 0.381, 0.54, 0.742, 1 ] + }, + { + "time": 0.4, + "angle": 25.34, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6666, + "angle": -27.47, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8, "angle": -24.06 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.8, "x": 1, "y": 1 } + ] + } + } + } +} +} \ No newline at end of file diff --git a/spine-ue4/Content/SpineBoy/spineboy.png b/spine-ue4/Content/SpineBoy/spineboy.png new file mode 100644 index 000000000..2eae82219 Binary files /dev/null and b/spine-ue4/Content/SpineBoy/spineboy.png differ diff --git a/spine-ue4/Content/SpineBoy/spineboy.skel b/spine-ue4/Content/SpineBoy/spineboy.skel new file mode 100644 index 000000000..9347353cf Binary files /dev/null and b/spine-ue4/Content/SpineBoy/spineboy.skel differ diff --git a/spine-ue4/Content/SpineBoy/spineboy.uasset b/spine-ue4/Content/SpineBoy/spineboy.uasset new file mode 100644 index 000000000..43116b775 Binary files /dev/null and b/spine-ue4/Content/SpineBoy/spineboy.uasset differ diff --git a/spine-ue4/LICENSE b/spine-ue4/LICENSE new file mode 100644 index 000000000..daceab94a --- /dev/null +++ b/spine-ue4/LICENSE @@ -0,0 +1,27 @@ +Spine Runtimes Software License v2.5 + +Copyright (c) 2013-2016, Esoteric Software +All rights reserved. + +You are granted a perpetual, non-exclusive, non-sublicensable, and +non-transferable license to use, install, execute, and perform the Spine +Runtimes software and derivative works solely for personal or internal +use. Without the written permission of Esoteric Software (see Section 2 of +the Spine Software License Agreement), you may not (a) modify, translate, +adapt, or develop new applications using the Spine Runtimes or otherwise +create derivative works or improvements of the Spine Runtimes or (b) remove, +delete, alter, or obscure any trademarks or any copyright, trademark, patent, +or other intellectual property or proprietary rights notices on or in the +Software, including any copy thereof. Redistributions in binary or source +form must include this license and terms. + +THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Resources/Icon128.png b/spine-ue4/Plugins/SpinePlugin/Resources/Icon128.png new file mode 100644 index 000000000..ae6416412 Binary files /dev/null and b/spine-ue4/Plugins/SpinePlugin/Resources/Icon128.png differ diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp new file mode 100644 index 000000000..fc71635d5 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineAtlasImportFactory.cpp @@ -0,0 +1,111 @@ +#include "SpineEditorPluginPrivatePCH.h" + +#include "SpineAtlasAsset.h" +#include "AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "PackageTools.h" +#include "Developer/DesktopPlatform/Public/IDesktopPlatform.h" +#include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h" +#include "spine/spine.h" +#include +#include +#include + +#define LOCTEXT_NAMESPACE "Spine" + +USpineAtlasAssetFactory::USpineAtlasAssetFactory (const FObjectInitializer& objectInitializer): Super(objectInitializer) { + bCreateNew = false; + bEditAfterNew = true; + bEditorImport = true; + SupportedClass = USpineAtlasAsset::StaticClass(); + + Formats.Add(TEXT("atlas;Spine atlas file")); +} + +FText USpineAtlasAssetFactory::GetToolTip () const { + return LOCTEXT("SpineAtlasAssetFactory", "Animations exported from Spine"); +} + +bool USpineAtlasAssetFactory::FactoryCanImport (const FString& filename) { + return true; +} + +UObject* USpineAtlasAssetFactory::FactoryCreateFile (UClass * InClass, UObject * InParent, FName InName, EObjectFlags Flags, const FString & Filename, const TCHAR* Parms, FFeedbackContext * Warn, bool& bOutOperationCanceled) { + FString rawString; + if (!FFileHelper::LoadFileToString(rawString, *Filename)) { + return nullptr; + } + const FString longPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName()); + FString CurrentSourcePath; + FString FilenameNoExtension; + FString UnusedExtension; + FPaths::Split(UFactory::GetCurrentFilename(), CurrentSourcePath, FilenameNoExtension, UnusedExtension); + FString name(InName.ToString()); + name.Append("-atlas"); + USpineAtlasAsset* asset = NewObject(InParent, InClass, FName(*name), Flags); + asset->SetRawData(rawString); + asset->SetAtlasFileName(FName(*Filename)); + LoadAtlas(asset, CurrentSourcePath, longPackagePath); + return asset; +} + +bool USpineAtlasAssetFactory::CanReimport(UObject* Obj, TArray& OutFilenames) { + USpineAtlasAsset* asset = Cast(Obj); + if (!asset) return false; + FString filename = asset->GetAtlasFileName().ToString(); + if (!filename.IsEmpty()) + OutFilenames.Add(filename); + return true; +} + +void USpineAtlasAssetFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) { + USpineAtlasAsset* asset = Cast(Obj); + if (asset && ensure(NewReimportPaths.Num() == 1)) + asset->SetAtlasFileName(FName(*NewReimportPaths[0])); +} + +EReimportResult::Type USpineAtlasAssetFactory::Reimport(UObject* Obj) { + USpineAtlasAsset* asset = Cast(Obj); + FString rawString; + if (!FFileHelper::LoadFileToString(rawString, *asset->GetAtlasFileName().ToString())) return EReimportResult::Failed; + asset->SetRawData(rawString); + const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName()); + FString CurrentSourcePath; + FString FilenameNoExtension; + FString UnusedExtension; + FPaths::Split(UFactory::GetCurrentFilename(), CurrentSourcePath, FilenameNoExtension, UnusedExtension); + LoadAtlas(asset, CurrentSourcePath, longPackagePath); + if (Obj->GetOuter()) Obj->GetOuter()->MarkPackageDirty(); + else Obj->MarkPackageDirty(); + return EReimportResult::Succeeded; +} + +UTexture2D* resolveTexture (USpineAtlasAsset* asset, const FString& pageFileName, const FString& targetSubPath) { + FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked("AssetTools"); + + TArray fileNames; + fileNames.Add(pageFileName); + + //@TODO: Avoid the first compression, since we're going to recompress + TArray importedAsset = AssetToolsModule.Get().ImportAssets(fileNames, targetSubPath); + UTexture2D* texture = (importedAsset.Num() > 0) ? Cast(importedAsset[0]) : nullptr; + + return texture; +} + +void USpineAtlasAssetFactory::LoadAtlas(USpineAtlasAsset* asset, const FString& currentSourcePath, const FString& longPackagePath) { + spAtlas* atlas = asset->GetAtlas(true); + asset->atlasPages.Empty(); + + const FString targetTexturePath = longPackagePath / TEXT("Textures"); + + spAtlasPage* page = atlas->pages; + while (page) { + const FString sourceTextureFilename = FPaths::Combine(*currentSourcePath, UTF8_TO_TCHAR(page->name)); + UTexture2D* texture = resolveTexture(asset, sourceTextureFilename, targetTexturePath); + page = page->next; + asset->atlasPages.Add(texture); + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp new file mode 100644 index 000000000..9bbc27815 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPlugin.cpp @@ -0,0 +1,31 @@ +// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. + +#include "SpineEditorPluginPrivatePCH.h" +#include "spine/spine.h" + + +class FSpineEditorPlugin : public ISpineEditorPlugin +{ + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; + +IMPLEMENT_MODULE( FSpineEditorPlugin, ISpineEditorPlugin ) + + + +void FSpineEditorPlugin::StartupModule() +{ + // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.) +} + + +void FSpineEditorPlugin::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + + + diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPluginPrivatePCH.h b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPluginPrivatePCH.h new file mode 100644 index 000000000..3512f5093 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineEditorPluginPrivatePCH.h @@ -0,0 +1,4 @@ +#include "SpineEditorPlugin.h" +#include "SpinePlugin.h" +#include "SpineAtlasImportFactory.h" +#include "SpineSkeletonImportFactory.h" \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp new file mode 100644 index 000000000..a58d2fbf9 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp @@ -0,0 +1,83 @@ +#include "SpineEditorPluginPrivatePCH.h" + +#include "SpineSkeletonDataAsset.h" +#include "AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "PackageTools.h" +#include "Developer/DesktopPlatform/Public/IDesktopPlatform.h" +#include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h" +#include "spine/spine.h" +#include +#include +#include + +#define LOCTEXT_NAMESPACE "Spine" + +USpineSkeletonAssetFactory::USpineSkeletonAssetFactory (const FObjectInitializer& objectInitializer): Super(objectInitializer) { + bCreateNew = false; + bEditAfterNew = true; + bEditorImport = true; + SupportedClass = USpineSkeletonDataAsset::StaticClass(); + + Formats.Add(TEXT("json;Spine skeleton file")); + Formats.Add(TEXT("skel;Spine skeleton file")); +} + +FText USpineSkeletonAssetFactory::GetToolTip () const { + return LOCTEXT("USpineSkeletonAssetFactory", "Animations exported from Spine"); +} + +bool USpineSkeletonAssetFactory::FactoryCanImport (const FString& filename) { + return true; +} + +void LoadAtlas (const FString& filename, const FString& targetPath) { + FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked("AssetTools"); + + FString skelFile = filename.Replace(TEXT(".skel"), TEXT(".atlas")).Replace(TEXT(".json"), TEXT(".atlas")); + if (!FPaths::FileExists(skelFile)) return; + + TArray fileNames; + fileNames.Add(skelFile); + TArray importedAssets = AssetToolsModule.Get().ImportAssets(fileNames, targetPath); +} + +UObject* USpineSkeletonAssetFactory::FactoryCreateFile (UClass * InClass, UObject * InParent, FName InName, EObjectFlags Flags, const FString & Filename, const TCHAR* Parms, FFeedbackContext * Warn, bool& bOutOperationCanceled) { + FString name(InName.ToString()); + name.Append("-data"); + USpineSkeletonDataAsset* asset = NewObject(InParent, InClass, FName(*name), Flags); + if (!FFileHelper::LoadFileToArray(asset->GetRawData(), *Filename, 0)) { + return nullptr; + } + asset->SetSkeletonDataFileName(FName(*Filename)); + const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName()); + LoadAtlas(Filename, longPackagePath); + return asset; +} + +bool USpineSkeletonAssetFactory::CanReimport(UObject* Obj, TArray& OutFilenames) { + USpineSkeletonDataAsset* asset = Cast(Obj); + if (!asset) return false; + FString filename = asset->GetSkeletonDataFileName().ToString(); + if (!filename.IsEmpty()) + OutFilenames.Add(filename); + return true; +} + +void USpineSkeletonAssetFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) { + USpineSkeletonDataAsset* asset = Cast(Obj); + if (asset && ensure(NewReimportPaths.Num() == 1)) + asset->SetSkeletonDataFileName(FName(*NewReimportPaths[0])); +} + +EReimportResult::Type USpineSkeletonAssetFactory::Reimport(UObject* Obj) { + USpineSkeletonDataAsset* asset = Cast(Obj); + FString rawString; + if (!FFileHelper::LoadFileToArray(asset->GetRawData(), *asset->GetSkeletonDataFileName().ToString(), 0)) return EReimportResult::Failed; + const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName()); + LoadAtlas(*asset->GetSkeletonDataFileName().ToString(), longPackagePath); + if (Obj->GetOuter()) Obj->GetOuter()->MarkPackageDirty(); + else Obj->MarkPackageDirty(); + return EReimportResult::Succeeded; +} +#undef LOCTEXT_NAMESPACE diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineAtlasImportFactory.h b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineAtlasImportFactory.h new file mode 100644 index 000000000..324b8f8d4 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineAtlasImportFactory.h @@ -0,0 +1,22 @@ + +#pragma once + +#include "UnrealEd.h" +#include "SpineAtlasAsset.h" +#include "SpineAtlasImportFactory.generated.h" + +UCLASS() +class USpineAtlasAssetFactory : public UFactory, public FReimportHandler +{ + GENERATED_UCLASS_BODY() + + virtual FText GetToolTip() const override; + virtual bool FactoryCanImport(const FString& Filename) override; + virtual UObject* FactoryCreateFile (UClass * InClass, UObject * InParent, FName InName, EObjectFlags Flags, const FString & Filename, const TCHAR* Parms, FFeedbackContext * Warn, bool& bOutOperationCanceled) override; + + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; + + void LoadAtlas(USpineAtlasAsset* asset, const FString& currentSourcePath, const FString& longPackagePath); +}; \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineEditorPlugin.h b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineEditorPlugin.h new file mode 100644 index 000000000..dd51331c1 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineEditorPlugin.h @@ -0,0 +1,17 @@ + +#pragma once + +#include "ModuleManager.h" + +class ISpineEditorPlugin : public IModuleInterface { + +public: + static inline ISpineEditorPlugin& Get() { + return FModuleManager::LoadModuleChecked< ISpineEditorPlugin >( "SpineEditorPlugin" ); + } + + static inline bool IsAvailable() { + return FModuleManager::Get().IsModuleLoaded( "SpineEditorPlugin" ); + } +}; + diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineSkeletonImportFactory.h b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineSkeletonImportFactory.h new file mode 100644 index 000000000..95847fe11 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public/SpineSkeletonImportFactory.h @@ -0,0 +1,20 @@ + +#pragma once + +#include "UnrealEd.h" +#include "SpineAtlasAsset.h" +#include "SpineSkeletonImportFactory.generated.h" + +UCLASS() +class USpineSkeletonAssetFactory : public UFactory, public FReimportHandler +{ + GENERATED_UCLASS_BODY() + + virtual FText GetToolTip() const override; + virtual bool FactoryCanImport(const FString& Filename) override; + virtual UObject* FactoryCreateFile (UClass * InClass, UObject * InParent, FName InName, EObjectFlags Flags, const FString & Filename, const TCHAR* Parms, FFeedbackContext * Warn, bool& bOutOperationCanceled) override; + + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; +}; \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/SpineEditorPlugin.Build.cs b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/SpineEditorPlugin.Build.cs new file mode 100644 index 000000000..c8aaba35c --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/SpineEditorPlugin.Build.cs @@ -0,0 +1,32 @@ +// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. + +namespace UnrealBuildTool.Rules +{ + public class SpineEditorPlugin : ModuleRules + { + public SpineEditorPlugin(TargetInfo Target) + { + PublicIncludePaths.AddRange(new string[] { "SpineEditorPlugin/Public" }); + + PrivateIncludePaths.AddRange(new string[] { "SpineEditorPlugin/Private" }); + + PublicDependencyModuleNames.AddRange(new string[] { + "Core", + "CoreUObject", + "Engine", + "UnrealEd", + "SpinePlugin" + }); + + PublicIncludePathModuleNames.AddRange(new string[] { + "AssetTools", + "AssetRegistry" + }); + + DynamicallyLoadedModuleNames.AddRange(new string[] { + "AssetTools", + "AssetRegistry" + }); + } + } +} \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp new file mode 100644 index 000000000..f957251f6 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp @@ -0,0 +1,82 @@ +#include "SpinePluginPrivatePCH.h" +#include "spine/spine.h" +#include +#include +#include + +FString USpineAtlasAsset::GetRawData () const { + return rawData; +} + +FName USpineAtlasAsset::GetAtlasFileName () const { +#if WITH_EDITORONLY_DATA + TArray files; + if (importData) importData->ExtractFilenames(files); + if (files.Num() > 0) return FName(*files[0]); + else return atlasFileName; +#else + return atlasFileName; +#endif +} + +#if WITH_EDITORONLY_DATA + +void USpineAtlasAsset::SetRawData (const FString &_rawData) { + this->rawData = _rawData; +} + +void USpineAtlasAsset::SetAtlasFileName (const FName &_atlasFileName) { + importData->UpdateFilenameOnly(_atlasFileName.ToString()); + TArray files; + importData->ExtractFilenames(files); + if (files.Num() > 0) atlasFileName = FName(*files[0]); +} + +void USpineAtlasAsset::PostInitProperties () { + if (!HasAnyFlags(RF_ClassDefaultObject)) importData = NewObject(this, TEXT("AssetImportData")); + Super::PostInitProperties(); +} + +void USpineAtlasAsset::GetAssetRegistryTags (TArray& OutTags) const { + if (importData) { + OutTags.Add(FAssetRegistryTag(SourceFileTagName(), importData->GetSourceData().ToJson(), FAssetRegistryTag::TT_Hidden) ); + } + + Super::GetAssetRegistryTags(OutTags); +} + +void USpineAtlasAsset::Serialize (FArchive& Ar) { + Super::Serialize(Ar); + if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData) + importData = NewObject(this, TEXT("AssetImportData")); +} + +void USpineAtlasAsset::BeginDestroy () { + if (atlas) { + spAtlas_dispose(atlas); + atlas = nullptr; + } + Super::BeginDestroy(); +} + +spAtlas* USpineAtlasAsset::GetAtlas (bool forceReload) { + if (!atlas || forceReload) { + if (atlas) { + spAtlas_dispose(atlas); + atlas = nullptr; + } + std::string t = TCHAR_TO_UTF8(*rawData); + atlas = spAtlas_create(t.c_str(), strlen(t.c_str()), "", nullptr); + spAtlasPage* page = atlas->pages; + int i = 0; + while (page) { + int num = atlasPages.Num(); + if (atlasPages.Num() > 0 && atlasPages.Num() > i) + page->rendererObject = atlasPages[i++]; + page = page->next; + } + } + return this->atlas; +} + +#endif diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp new file mode 100644 index 000000000..39c3c11c7 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp @@ -0,0 +1,42 @@ +// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. + +#include "SpinePluginPrivatePCH.h" +#include "spine/spine.h" + + +class FSpinePlugin : public SpinePlugin { + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; + +IMPLEMENT_MODULE( FSpinePlugin, SpinePlugin ) + + + +void FSpinePlugin::StartupModule() { + // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.) + printf("This is a test"); +} + + +void FSpinePlugin::ShutdownModule() { + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +extern "C" { + void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) { + + } + void _spAtlasPage_disposeTexture (spAtlasPage* self) { + + } + + char* _spUtil_readFile (const char* path, int* length) { + return 0; + } +} + + + diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePluginPrivatePCH.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePluginPrivatePCH.h new file mode 100644 index 000000000..1c97102a4 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePluginPrivatePCH.h @@ -0,0 +1,10 @@ +// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. + +#include "SpinePlugin.h" + +// You should place include statements to your module's private header files here. You only need to +// add includes for headers that are used in most of your module's source files though. +#include "SpineSkeletonDataAsset.h" +#include "SpineAtlasAsset.h" +#include "SpineSkeletonComponent.h" +#include "SpineSkeletonRendererComponent.h" \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp new file mode 100644 index 000000000..e7f489849 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp @@ -0,0 +1,65 @@ +// iFll out your copyright notice in the Description page of Project Settings. + +#include "SpinePluginPrivatePCH.h" + +// Sets default values for this component's properties +USpineSkeletonComponent::USpineSkeletonComponent() +{ + // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features + // off to improve performance if you don't need them. + bWantsBeginPlay = true; + PrimaryComponentTick.bCanEverTick = true; + bTickInEditor = true; + bAutoActivate = true; +} + +void USpineSkeletonComponent::BeginPlay() { + Super::BeginPlay(); +} + +void USpineSkeletonComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) { + Super::TickComponent( DeltaTime, TickType, ThisTickFunction ); + + if (lastAtlas != atlas || lastData != skeletonData) { + DisposeState(); + + if (atlas && skeletonData) { + spSkeletonData* data = skeletonData->GetSkeletonData(atlas->GetAtlas(true), true); + skeleton = spSkeleton_create(data); + stateData = spAnimationStateData_create(data); + state = spAnimationState_create(stateData); + spAnimationState_setAnimationByName(state, 0, "walk", true); + } + + lastAtlas = atlas; + lastData = skeletonData; + } + + if (state) { + spAnimationState_update(state, DeltaTime); + spAnimationState_apply(state, skeleton); + spSkeleton_updateWorldTransform(skeleton); + } +} + +void USpineSkeletonComponent::DisposeState() { + if (stateData) { + spAnimationStateData_dispose(stateData); + stateData = nullptr; + } + + if (state) { + spAnimationState_dispose(state); + state = nullptr; + } + + if (skeleton) { + spSkeleton_dispose(skeleton); + skeleton = nullptr; + } +} + +void USpineSkeletonComponent::BeginDestroy() { + DisposeState(); + Super::BeginDestroy(); +} \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp new file mode 100644 index 000000000..1812288e3 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp @@ -0,0 +1,79 @@ +#include "SpinePluginPrivatePCH.h" +#include "spine/spine.h" +#include +#include +#include + +FName USpineSkeletonDataAsset::GetSkeletonDataFileName () const { +#if WITH_EDITORONLY_DATA + TArray files; + if (importData) importData->ExtractFilenames(files); + if (files.Num() > 0) return FName(*files[0]); + else return skeletonDataFileName; +#else + return skeletonDataFileName; +#endif +} + +TArray& USpineSkeletonDataAsset::GetRawData () { + return this->rawData; +} + +#if WITH_EDITORONLY_DATA + +void USpineSkeletonDataAsset::SetSkeletonDataFileName (const FName &_skeletonDataFileName) { + importData->UpdateFilenameOnly(_skeletonDataFileName.ToString()); + TArray files; + importData->ExtractFilenames(files); + if (files.Num() > 0) this->skeletonDataFileName = FName(*files[0]); +} + +void USpineSkeletonDataAsset::PostInitProperties () { + if (!HasAnyFlags(RF_ClassDefaultObject)) importData = NewObject(this, TEXT("AssetImportData")); + Super::PostInitProperties(); +} + +void USpineSkeletonDataAsset::GetAssetRegistryTags (TArray& OutTags) const { + if (importData) { + OutTags.Add(FAssetRegistryTag(SourceFileTagName(), importData->GetSourceData().ToJson(), FAssetRegistryTag::TT_Hidden) ); + } + + Super::GetAssetRegistryTags(OutTags); +} + +void USpineSkeletonDataAsset::Serialize (FArchive& Ar) { + Super::Serialize(Ar); + if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData) + importData = NewObject(this, TEXT("AssetImportData")); +} + +void USpineSkeletonDataAsset::BeginDestroy () { + if (this->skeletonData) { + spSkeletonData_dispose(this->skeletonData); + this->skeletonData = nullptr; + } + Super::BeginDestroy(); +} + +spSkeletonData* USpineSkeletonDataAsset::GetSkeletonData (spAtlas* atlas, bool forceReload) { + if (!skeletonData || forceReload) { + if (skeletonData) { + spSkeletonData_dispose(skeletonData); + skeletonData = nullptr; + } + int dataLen = rawData.Num(); + if (skeletonDataFileName.GetPlainNameString().Contains(TEXT(".json"))) { + spSkeletonJson* json = spSkeletonJson_create(atlas); + this->skeletonData = spSkeletonJson_readSkeletonData(json, (const char*)rawData.GetData()); + spSkeletonJson_dispose(json); + } else { + spSkeletonBinary* binary = spSkeletonBinary_create(atlas); + this->skeletonData = spSkeletonBinary_readSkeletonData(binary, (const unsigned char*)rawData.GetData(), (int)rawData.Num()); + spSkeletonBinary_dispose(binary); + } + lastAtlas = atlas; + } + return this->skeletonData; +} + +#endif diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp new file mode 100644 index 000000000..649807449 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp @@ -0,0 +1,118 @@ +#include "SpinePluginPrivatePCH.h" +#include "Engine.h" +#include "spine/spine.h" +#include + +USpineSkeletonRendererComponent::USpineSkeletonRendererComponent(const FObjectInitializer& ObjectInitializer) +{ + // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features + // off to improve performance if you don't need them. + bWantsBeginPlay = true; + PrimaryComponentTick.bCanEverTick = true; + bTickInEditor = true; + bAutoActivate = true; +} + +// Called when the game starts +void USpineSkeletonRendererComponent::BeginPlay() +{ + Super::BeginPlay(); +} + +// Called every frame +void USpineSkeletonRendererComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) +{ + Super::TickComponent( DeltaTime, TickType, ThisTickFunction ); + + UClass* skeletonClass = USpineSkeletonComponent::StaticClass(); + AActor* owner = GetOwner(); + if (owner) { + USpineSkeletonComponent* skeleton = Cast(owner->GetComponentByClass(skeletonClass)); + if (skeleton && skeleton->skeleton) { + spSkeleton_updateWorldTransform(skeleton->skeleton); + UpdateMesh(skeleton->skeleton); + } + } +} + +void USpineSkeletonRendererComponent::UpdateMesh(spSkeleton* skeleton) { + TArray vertices; + TArray indices; + TArray uvs; + TArray colors; + + int worldVerticesLength = 1000; + float* worldVertices = (float*)malloc((2 + 2 + 5) * worldVerticesLength); + int idx = 0; + int meshSection = 0; + + ClearAllMeshSections(); + + for (int i = 0; i < skeleton->slotsCount; ++i) { + spSlot* slot = skeleton->drawOrder[i]; + spAttachment* attachment = slot->attachment; + if (!attachment) continue; + + if (attachment->type == SP_ATTACHMENT_REGION) { + spRegionAttachment* regionAttachment = (spRegionAttachment*)attachment; + spRegionAttachment_computeWorldVertices(regionAttachment, slot->bone, worldVertices); + + uint8 r = static_cast(skeleton->r * slot->r * 255); + uint8 g = static_cast(skeleton->g * slot->g * 255); + uint8 b = static_cast(skeleton->b * slot->b * 255); + uint8 a = static_cast(skeleton->a * slot->a * 255); + + colors.Add(FColor(r, g, b, a)); + vertices.Add(FVector(worldVertices[0], 0, worldVertices[1])); + uvs.Add(FVector2D(regionAttachment->uvs[0], regionAttachment->uvs[1])); + + colors.Add(FColor(r, g, b, a)); + vertices.Add(FVector(worldVertices[2], 0, worldVertices[3])); + uvs.Add(FVector2D(regionAttachment->uvs[2], regionAttachment->uvs[3])); + + colors.Add(FColor(r, g, b, a)); + vertices.Add(FVector(worldVertices[4], 0, worldVertices[5])); + uvs.Add(FVector2D(regionAttachment->uvs[4], regionAttachment->uvs[5])); + + colors.Add(FColor(r, g, b, a)); + vertices.Add(FVector(worldVertices[6], 0, worldVertices[7])); + uvs.Add(FVector2D(regionAttachment->uvs[6], regionAttachment->uvs[7])); + + indices.Add(idx + 0); + indices.Add(idx + 1); + indices.Add(idx + 2); + indices.Add(idx + 0); + indices.Add(idx + 2); + indices.Add(idx + 3); + idx += 4; + } + /*else if (attachment->type == ATTACHMENT_MESH) { + MeshAttachment* mesh = (MeshAttachment*)attachment; + if (mesh->super.worldVerticesLength > SPINE_MESH_VERTEX_COUNT_MAX) continue; + texture = (Texture*)((AtlasRegion*)mesh->rendererObject)->page->rendererObject; + MeshAttachment_computeWorldVertices(mesh, slot, worldVertices); + + Uint8 r = static_cast(skeleton->r * slot->r * 255); + Uint8 g = static_cast(skeleton->g * slot->g * 255); + Uint8 b = static_cast(skeleton->b * slot->b * 255); + Uint8 a = static_cast(skeleton->a * slot->a * 255); + vertex.color.r = r; + vertex.color.g = g; + vertex.color.b = b; + vertex.color.a = a; + + Vector2u size = texture->getSize(); + for (int i = 0; i < mesh->trianglesCount; ++i) { + int index = mesh->triangles[i] << 1; + vertex.position.x = worldVertices[index]; + vertex.position.y = worldVertices[index + 1]; + vertex.texCoords.x = mesh->uvs[index] * size.x; + vertex.texCoords.y = mesh->uvs[index + 1] * size.y; + vertexArray->append(vertex); + } + }*/ + } + + CreateMeshSection(0, vertices, indices, TArray(), uvs, colors, TArray(), false); + free(worldVertices); +} \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineAtlasAsset.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineAtlasAsset.h new file mode 100644 index 000000000..21b2be5b0 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineAtlasAsset.h @@ -0,0 +1,45 @@ +#pragma once + +#include "Engine.h" +#include "spine/spine.h" +#include "SpineAtlasAsset.generated.h" + +UCLASS( ClassGroup=(Spine) ) +class SPINEPLUGIN_API USpineAtlasAsset : public UObject { + GENERATED_BODY() + +public: + spAtlas* GetAtlas (bool forceReload = false); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine) + TArray atlasPages; + + FString GetRawData () const; + FName GetAtlasFileName () const; + + virtual void BeginDestroy () override; + +protected: + spAtlas* atlas = nullptr; + + UPROPERTY() + FString rawData; + + UPROPERTY() + FName atlasFileName; + +#if WITH_EDITORONLY_DATA + +public: + void SetRawData (const FString &rawData); + void SetAtlasFileName (const FName &atlasFileName); + +protected: + UPROPERTY(VisibleAnywhere, Instanced, Category=ImportSettings) + class UAssetImportData* importData; + + virtual void PostInitProperties ( ) override; + virtual void GetAssetRegistryTags(TArray& OutTags) const override; + virtual void Serialize (FArchive& Ar) override; +#endif +}; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h new file mode 100644 index 000000000..fabf34753 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h @@ -0,0 +1,38 @@ +// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "ModuleManager.h" + + +/** + * The public interface to this module. In most cases, this interface is only public to sibling modules + * within this plugin. + */ +class SPINEPLUGIN_API SpinePlugin : public IModuleInterface +{ + +public: + + /** + * Singleton-like access to this module's interface. This is just for convenience! + * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. + * + * @return Returns singleton instance, loading the module on demand if needed + */ + static inline SpinePlugin& Get() + { + return FModuleManager::LoadModuleChecked< SpinePlugin >( "SpinePlugin" ); + } + + /** + * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. + * + * @return True if the module is loaded and ready to use + */ + static inline bool IsAvailable() + { + return FModuleManager::Get().IsModuleLoaded( "SpinePlugin" ); + } +}; + diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h new file mode 100644 index 000000000..686aadfb1 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "Components/ActorComponent.h" +#include "SpineSkeletonComponent.generated.h" + +class USpineAtlasAsset; +UCLASS( ClassGroup=(Spine), meta=(BlueprintSpawnableComponent) ) +class SPINEPLUGIN_API USpineSkeletonComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine) + USpineAtlasAsset* atlas; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine) + USpineSkeletonDataAsset* skeletonData; + + spAnimationStateData* stateData; + spAnimationState* state; + spSkeleton* skeleton; + + USpineSkeletonComponent(); + + virtual void BeginPlay() override; + + virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override; + + virtual void BeginDestroy () override; + +protected: + void DisposeState(); + + USpineAtlasAsset* lastAtlas = nullptr; + USpineSkeletonDataAsset* lastData = nullptr; +}; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h new file mode 100644 index 000000000..23a890035 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Engine.h" +#include "spine/spine.h" +#include "SpineSkeletonDataAsset.generated.h" + +UCLASS( ClassGroup=(Spine) ) +class SPINEPLUGIN_API USpineSkeletonDataAsset : public UObject { + GENERATED_BODY() + +public: + spSkeletonData* GetSkeletonData(spAtlas* atlas, bool forceReload = false); + + FName GetSkeletonDataFileName () const; + TArray& GetRawData (); + + virtual void BeginDestroy () override; + +protected: + UPROPERTY() + TArray rawData; + + spAtlas* lastAtlas = nullptr; + spSkeletonData* skeletonData = nullptr; + + UPROPERTY() + FName skeletonDataFileName; + +#if WITH_EDITORONLY_DATA +public: + void SetSkeletonDataFileName (const FName &skeletonDataFileName); + +protected: + UPROPERTY(VisibleAnywhere, Instanced, Category=ImportSettings) + class UAssetImportData* importData; + + virtual void PostInitProperties ( ) override; + virtual void GetAssetRegistryTags(TArray& OutTags) const override; + virtual void Serialize (FArchive& Ar) override; +#endif +}; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h new file mode 100644 index 000000000..cc7f222c0 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h @@ -0,0 +1,27 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "Components/ActorComponent.h" +#include "ProceduralMeshComponent.h" +#include "SpineSkeletonComponent.h" +#include "SpineSkeletonRendererComponent.generated.h" + + +UCLASS( ClassGroup=(Spine), meta=(BlueprintSpawnableComponent) ) +class SPINEPLUGIN_API USpineSkeletonRendererComponent : public UProceduralMeshComponent +{ + GENERATED_BODY() + +public: + USpineSkeletonRendererComponent (const FObjectInitializer& ObjectInitializer); + + virtual void BeginPlay () override; + + virtual void TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + +protected: + void UpdateMesh (spSkeleton* skeleton); + + bool updated; +}; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/SpinePlugin.Build.cs b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/SpinePlugin.Build.cs new file mode 100644 index 000000000..f2618c664 --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/SpinePlugin.Build.cs @@ -0,0 +1,16 @@ +// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. + +namespace UnrealBuildTool.Rules +{ + public class SpinePlugin : ModuleRules + { + public SpinePlugin(TargetInfo Target) + { + PublicIncludePaths.AddRange(new string[] { "SpinePlugin/Public", "SpinePlugin/Public/spine-c/include" }); + PrivateIncludePaths.AddRange(new string[] { "SpinePlugin/Private", "SpinePlugin/Public/spine-c/include" }); + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "ProceduralMeshComponent" }); + PrivateDependencyModuleNames.AddRange(new string[] { "RHI", "RenderCore", "ShaderCore" }); + OptimizeCode = CodeOptimization.Never; + } + } +} \ No newline at end of file diff --git a/spine-ue4/Plugins/SpinePlugin/SpinePlugin.uplugin b/spine-ue4/Plugins/SpinePlugin/SpinePlugin.uplugin new file mode 100644 index 000000000..fb0c4854e --- /dev/null +++ b/spine-ue4/Plugins/SpinePlugin/SpinePlugin.uplugin @@ -0,0 +1,29 @@ +{ + "FileVersion" : 3, + "Version" : 1, + "VersionName" : "1.0", + "FriendlyName" : "Spine Plugin", + "Description" : "A plugin to load and render Spine animations in Unreal Engine", + "Category" : "2D", + "CreatedBy" : "Esoteric Software", + "CreatedByURL" : "http://esotericsoftware.com", + "DocsURL" : "", + "MarketplaceURL" : "", + "SupportURL" : "", + "EnabledByDefault" : false, + "CanContainContent" : false, + "IsBetaVersion" : false, + "Installed" : false, + "Modules" : + [ + { + "Name" : "SpinePlugin", + "Type" : "Runtime", + "LoadingPhase" : "PreDefault" + }, + { + "Name" : "SpineEditorPlugin", + "Type" : "Editor" + } + ] +} \ No newline at end of file diff --git a/spine-ue4/README.md b/spine-ue4/README.md new file mode 100644 index 000000000..ed900c60d --- /dev/null +++ b/spine-ue4/README.md @@ -0,0 +1,29 @@ +# spine-ue4 +The spine-ue4 runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unreal Engine 4](https://www.unrealengine.com/). spine-ue4 is based on [spine-c](https://github.com/EsotericSoftware/spine-runtimes/tree/master/spine-c). + +## Licensing + +This Spine Runtime may only be used for personal or internal use, typically to evaluate Spine before purchasing. If you would like to incorporate a Spine Runtime into your applications, distribute software containing a Spine Runtime, or modify a Spine Runtime, then you will need a valid [Spine license](https://esotericsoftware.com/spine-purchase). Please see the [Spine Runtimes Software License](https://github.com/EsotericSoftware/spine-runtimes/blob/master/LICENSE) for detailed information. + +The Spine Runtimes are developed with the intent to be used with data exported from Spine. By purchasing Spine, `Section 2` of the [Spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the Spine Runtimes. + +## Spine version + +spine-ue4 works with data exported from Spine 3.4.02. + +spine-ue4 supports all Spine features. + +## Usage +1. Create a new Unreal Engine project. See the [Unreal Engine documentation](https://docs.unrealengine.com/latest/INT/) or have a look at the example in this repository. +2. Download the Spine Runtimes source using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip) +3. Copy the `Plugins` folder from this directory to your new project's root directory. +4. Copy the `spine-c` folder from this repositories root directory to your project's `Plugins/SpinePlugin/Sources/SpinePlugin/Public/` directory. +5. Open the Unreal Project in the Unreal Editor + +See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documentation#runtimesTitle) on how to use the APIs or check out the Spine UE4 example. + +## Example +The Spine UE4 example works on all platforms supported by Unreal Engine. + +1. Copy the `spine-c` folder from this repositories root directory to your `Plugins/SpinePlugin/Sources/SpinePlugin/Public/` directory. +2. Open the SpineUE4.uproject file with Unreal Editor diff --git a/spine-ue4/Source/SpineUE4.Target.cs b/spine-ue4/Source/SpineUE4.Target.cs new file mode 100644 index 000000000..c5288c9b5 --- /dev/null +++ b/spine-ue4/Source/SpineUE4.Target.cs @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class SpineUE4Target : TargetRules +{ + public SpineUE4Target(TargetInfo Target) + { + Type = TargetType.Game; + } + + // + // TargetRules interface. + // + + public override void SetupBinaries( + TargetInfo Target, + ref List OutBuildBinaryConfigurations, + ref List OutExtraModuleNames + ) + { + OutExtraModuleNames.AddRange( new string[] { "SpineUE4" } ); + } +} diff --git a/spine-ue4/Source/SpineUE4/SpineUE4.Build.cs b/spine-ue4/Source/SpineUE4/SpineUE4.Build.cs new file mode 100644 index 000000000..08f863be8 --- /dev/null +++ b/spine-ue4/Source/SpineUE4/SpineUE4.Build.cs @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using UnrealBuildTool; + +public class SpineUE4 : ModuleRules +{ + public SpineUE4(TargetInfo Target) + { + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "ProceduralMeshComponent" }); + + PrivateDependencyModuleNames.AddRange(new string[] { }); + + // Uncomment if you are using Slate UI + // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); + + // Uncomment if you are using online features + // PrivateDependencyModuleNames.Add("OnlineSubsystem"); + + // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true + } +} diff --git a/spine-ue4/Source/SpineUE4/SpineUE4.cpp b/spine-ue4/Source/SpineUE4/SpineUE4.cpp new file mode 100644 index 000000000..43f51b9b2 --- /dev/null +++ b/spine-ue4/Source/SpineUE4/SpineUE4.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "SpineUE4.h" + +IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, SpineUE4, "SpineUE4" ); diff --git a/spine-ue4/Source/SpineUE4/SpineUE4.h b/spine-ue4/Source/SpineUE4/SpineUE4.h new file mode 100644 index 000000000..3d85d22a4 --- /dev/null +++ b/spine-ue4/Source/SpineUE4/SpineUE4.h @@ -0,0 +1,6 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "Engine.h" + diff --git a/spine-ue4/Source/SpineUE4/SpineUE4GameMode.cpp b/spine-ue4/Source/SpineUE4/SpineUE4GameMode.cpp new file mode 100644 index 000000000..16d36891b --- /dev/null +++ b/spine-ue4/Source/SpineUE4/SpineUE4GameMode.cpp @@ -0,0 +1,8 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "SpineUE4.h" +#include "SpineUE4GameMode.h" + + + + diff --git a/spine-ue4/Source/SpineUE4/SpineUE4GameMode.h b/spine-ue4/Source/SpineUE4/SpineUE4GameMode.h new file mode 100644 index 000000000..3570c67a0 --- /dev/null +++ b/spine-ue4/Source/SpineUE4/SpineUE4GameMode.h @@ -0,0 +1,19 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "GameFramework/GameMode.h" +#include "SpineUE4GameMode.generated.h" + +/** + * + */ +UCLASS() +class SPINEUE4_API ASpineUE4GameMode : public AGameMode +{ + GENERATED_BODY() + + + + +}; diff --git a/spine-ue4/Source/SpineUE4Editor.Target.cs b/spine-ue4/Source/SpineUE4Editor.Target.cs new file mode 100644 index 000000000..3b5458abe --- /dev/null +++ b/spine-ue4/Source/SpineUE4Editor.Target.cs @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class SpineUE4EditorTarget : TargetRules +{ + public SpineUE4EditorTarget(TargetInfo Target) + { + Type = TargetType.Editor; + } + + // + // TargetRules interface. + // + + public override void SetupBinaries( + TargetInfo Target, + ref List OutBuildBinaryConfigurations, + ref List OutExtraModuleNames + ) + { + OutExtraModuleNames.AddRange( new string[] { "SpineUE4" } ); + } +} diff --git a/spine-ue4/SpineUE4.uproject b/spine-ue4/SpineUE4.uproject new file mode 100644 index 000000000..29c8d54a6 --- /dev/null +++ b/spine-ue4/SpineUE4.uproject @@ -0,0 +1,16 @@ +{ + "FileVersion": 3, + "EngineAssociation": "4.13", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "SpineUE4", + "Type": "Runtime", + "LoadingPhase": "Default", + "AdditionalDependencies": [ + "Engine" + ] + } + ] +}