initial commit
This commit is contained in:
commit
12466b5d4e
16 changed files with 5640 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
*/.DS_Store
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
*/thumbs.db
|
||||||
|
thumbs.db
|
||||||
|
.thumbs.db?
|
||||||
|
.luxe/
|
||||||
|
_luxe.data/
|
||||||
|
_luxe.deploy/
|
||||||
|
log.txt
|
||||||
1
.luxeignore
Normal file
1
.luxeignore
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
preview.png
|
||||||
17
.vscode/tasks.json
vendored
Normal file
17
.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "luxe",
|
||||||
|
"script": "run",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$luxe-absolute",
|
||||||
|
"$luxe-relative"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"label": "luxe: run - Build & Run the project"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
62
assets/down_the_mountain/snowplayers.atlas
Normal file
62
assets/down_the_mountain/snowplayers.atlas
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
skeleton.png
|
||||||
|
size: 1203,242
|
||||||
|
format: RGBA8888
|
||||||
|
filter: Linear,Linear
|
||||||
|
repeat: none
|
||||||
|
Untitled-1
|
||||||
|
rotate: false
|
||||||
|
xy: 1071, 122
|
||||||
|
size: 130, 118
|
||||||
|
orig: 130, 118
|
||||||
|
offset: 0, 0
|
||||||
|
index: -1
|
||||||
|
Untitled-2
|
||||||
|
rotate: true
|
||||||
|
xy: 743, 29
|
||||||
|
size: 133, 157
|
||||||
|
orig: 133, 157
|
||||||
|
offset: 0, 0
|
||||||
|
index: -1
|
||||||
|
Untitled-3
|
||||||
|
rotate: false
|
||||||
|
xy: 743, 164
|
||||||
|
size: 326, 76
|
||||||
|
orig: 327, 76
|
||||||
|
offset: 1, 0
|
||||||
|
index: -1
|
||||||
|
Untitled-4
|
||||||
|
rotate: true
|
||||||
|
xy: 2, 6
|
||||||
|
size: 234, 268
|
||||||
|
orig: 236, 278
|
||||||
|
offset: 2, 0
|
||||||
|
index: -1
|
||||||
|
Untitled-5
|
||||||
|
rotate: false
|
||||||
|
xy: 542, 6
|
||||||
|
size: 199, 234
|
||||||
|
orig: 199, 234
|
||||||
|
offset: 0, 0
|
||||||
|
index: -1
|
||||||
|
eye1
|
||||||
|
rotate: false
|
||||||
|
xy: 1061, 2
|
||||||
|
size: 130, 118
|
||||||
|
orig: 130, 118
|
||||||
|
offset: 0, 0
|
||||||
|
index: -1
|
||||||
|
eye2
|
||||||
|
rotate: true
|
||||||
|
xy: 902, 29
|
||||||
|
size: 133, 157
|
||||||
|
orig: 133, 157
|
||||||
|
offset: 0, 0
|
||||||
|
index: -1
|
||||||
|
eye3
|
||||||
|
rotate: true
|
||||||
|
xy: 272, 6
|
||||||
|
size: 234, 268
|
||||||
|
orig: 236, 278
|
||||||
|
offset: 2, 0
|
||||||
|
index: -1
|
||||||
4
assets/down_the_mountain/snowplayers.image.lx
Normal file
4
assets/down_the_mountain/snowplayers.image.lx
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
image = {
|
||||||
|
source = "assets/down_the_mountain/snowplayers.png"
|
||||||
|
generate_mipmaps_at_load = false
|
||||||
|
}
|
||||||
4974
assets/down_the_mountain/snowplayers.json
Normal file
4974
assets/down_the_mountain/snowplayers.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
assets/down_the_mountain/snowplayers.png
Normal file
BIN
assets/down_the_mountain/snowplayers.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 246 KiB |
4
assets/down_the_mountain/snowplayers.spine.lx
Normal file
4
assets/down_the_mountain/snowplayers.spine.lx
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
spine = {
|
||||||
|
skeleton = "assets/down_the_mountain/snowplayers",
|
||||||
|
atlas = "assets/down_the_mountain/snowplayers"
|
||||||
|
}
|
||||||
58
game.wren
Normal file
58
game.wren
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import "luxe: game" for Ready
|
||||||
|
import "luxe: assets" for Assets
|
||||||
|
import "luxe: input" for Input, Key
|
||||||
|
import "luxe: world" for World, Entity, Transform, Sprite, Values, Tags, Camera
|
||||||
|
import "luxe: math" for Math
|
||||||
|
import "luxe: draw" for Draw
|
||||||
|
import "luxe: io" for IO
|
||||||
|
|
||||||
|
import "outline/app" for App
|
||||||
|
import "spine" for Spine
|
||||||
|
|
||||||
|
class Game is Ready {
|
||||||
|
|
||||||
|
construct ready() {
|
||||||
|
|
||||||
|
super("ready!")
|
||||||
|
|
||||||
|
app = App.new()
|
||||||
|
|
||||||
|
System.print("render size: %(app.width) x %(app.height) @ %(app.scale)x")
|
||||||
|
|
||||||
|
_logo = Entity.create(app.world, "sprite")
|
||||||
|
Transform.create(_logo)
|
||||||
|
Transform.set_pos(_logo, app.width/2, app.height/2, 0)
|
||||||
|
Sprite.create(_logo, Assets.material("luxe: material/logo"), 128, 128)
|
||||||
|
|
||||||
|
|
||||||
|
var spine_asset = Spine.parse("assets/down_the_mountain/snowplayers")
|
||||||
|
_debug = Draw.create(World.render_set(app.world))
|
||||||
|
spine_asset.draw_bones(_debug)
|
||||||
|
spine_asset.draw_outlines(_debug)
|
||||||
|
Draw.commit(_debug)
|
||||||
|
} //ready
|
||||||
|
|
||||||
|
tick(delta) {
|
||||||
|
|
||||||
|
var pos = Camera.screen_point_to_world(app.camera, Input.mouse_x(), Input.mouse_y())
|
||||||
|
Transform.set_pos(_logo, pos.x, pos.y, 0)
|
||||||
|
|
||||||
|
if(Input.key_state_released(Key.escape)) {
|
||||||
|
IO.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.color.r = app.color.g = app.color.b = (IO.timestamp()/20 % 1)
|
||||||
|
|
||||||
|
} //tick
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
|
||||||
|
System.print("unready!")
|
||||||
|
app.destroy()
|
||||||
|
|
||||||
|
} //destroy
|
||||||
|
|
||||||
|
app { _app }
|
||||||
|
app=(v) { _app=v }
|
||||||
|
|
||||||
|
} //Game
|
||||||
74
outline/app.wren
Normal file
74
outline/app.wren
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import "luxe: world" for World, Camera, Entity, Transform
|
||||||
|
import "luxe: render" for Render
|
||||||
|
import "luxe: game" for Frame
|
||||||
|
|
||||||
|
class App {
|
||||||
|
|
||||||
|
world { _world }
|
||||||
|
ui { _ui_world }
|
||||||
|
|
||||||
|
camera { _camera }
|
||||||
|
ui_camera { _ui_camera }
|
||||||
|
|
||||||
|
color { _color }
|
||||||
|
color=(v) { _color = v }
|
||||||
|
|
||||||
|
width { Render.window_w() }
|
||||||
|
height { Render.window_h() }
|
||||||
|
scale { Render.drawable_ratio() }
|
||||||
|
|
||||||
|
construct new() {
|
||||||
|
|
||||||
|
_color = [1,1,1,1]
|
||||||
|
|
||||||
|
//create worlds
|
||||||
|
|
||||||
|
_world = World.create("game")
|
||||||
|
_ui_world = World.create("ui")
|
||||||
|
|
||||||
|
//create cameras
|
||||||
|
|
||||||
|
_camera = Entity.create(_world, "app.camera")
|
||||||
|
Transform.create(_camera)
|
||||||
|
Camera.create(_camera)
|
||||||
|
Camera.set_default(_world, _camera)
|
||||||
|
|
||||||
|
_ui_camera = Entity.create(_ui_world, "app.ui_camera")
|
||||||
|
Transform.create(_ui_camera)
|
||||||
|
Camera.create(_ui_camera)
|
||||||
|
Camera.set_default(_ui_world, _ui_camera)
|
||||||
|
|
||||||
|
//update our worlds
|
||||||
|
|
||||||
|
Frame.on(Frame.sim) {|delta|
|
||||||
|
World.tick(_world, delta)
|
||||||
|
World.tick(_ui_world, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
//render our worlds
|
||||||
|
|
||||||
|
Frame.on(Frame.visual) {|delta|
|
||||||
|
World.render(_world, _camera, "game", {"clear_color":_color})
|
||||||
|
World.render(_ui_world, _ui_camera, "ui")
|
||||||
|
}
|
||||||
|
|
||||||
|
} //new
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
|
||||||
|
//destroy cameras
|
||||||
|
|
||||||
|
Camera.destroy(_camera)
|
||||||
|
Camera.destroy(_ui_camera)
|
||||||
|
|
||||||
|
Entity.destroy(_camera)
|
||||||
|
Entity.destroy(_ui_camera)
|
||||||
|
|
||||||
|
//destroy worlds
|
||||||
|
|
||||||
|
World.destroy(_ui_world)
|
||||||
|
World.destroy(_world)
|
||||||
|
|
||||||
|
} //destroy
|
||||||
|
|
||||||
|
} //
|
||||||
23
outline/inputs.input.lx
Normal file
23
outline/inputs.input.lx
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
input = {
|
||||||
|
nodes = [
|
||||||
|
{ name = "ui" where = "front" channels = ["c01"] }
|
||||||
|
{ name = "game" where = "after: ui" channels = ["c02"] }
|
||||||
|
]
|
||||||
|
|
||||||
|
map = {
|
||||||
|
left = { keys = ["key_a", "left"] }
|
||||||
|
right = { keys = ["key_d", "right"] }
|
||||||
|
up = { keys = ["key_w", "up"] }
|
||||||
|
down = { keys = ["key_s", "down"] }
|
||||||
|
jump = {
|
||||||
|
keys = ["key_x", "up", "key_w", "space"]
|
||||||
|
mouse = ["left"]
|
||||||
|
gamepad = [0]
|
||||||
|
}
|
||||||
|
|
||||||
|
next = {
|
||||||
|
keys = ["key_x", "up", "key_w", "space", "enter", "escape"]
|
||||||
|
mouse = ["left", "right"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
outline/renderer.wren
Normal file
51
outline/renderer.wren
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import "luxe: render" for Render, RenderLayerDesc, PassLayerDesc, LoadAction
|
||||||
|
import "luxe: render" for SortType, ImageDesc, ImageType, PixelFormat
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
|
||||||
|
construct new() {
|
||||||
|
|
||||||
|
System.print("game / render / init / ok")
|
||||||
|
|
||||||
|
} //new
|
||||||
|
|
||||||
|
ready() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(delta) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render_path(ctx) {
|
||||||
|
|
||||||
|
if(ctx.path == "game") {
|
||||||
|
game_render_path(ctx)
|
||||||
|
} else if(ctx.path == "ui") {
|
||||||
|
ui_render_path(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
} //render_path
|
||||||
|
|
||||||
|
game_render_path(ctx) {
|
||||||
|
|
||||||
|
var layer = RenderLayerDesc.new()
|
||||||
|
layer.dest.color[0].clear_color = ctx.get("clear_color", [1,1,1,1])
|
||||||
|
layer.dest.color[0].load_action = LoadAction.clear
|
||||||
|
layer.dest.depth.load_action = LoadAction.clear
|
||||||
|
|
||||||
|
ctx.layer_render("default", layer)
|
||||||
|
|
||||||
|
} //game_render_path
|
||||||
|
|
||||||
|
ui_render_path(ctx) {
|
||||||
|
|
||||||
|
var layer = RenderLayerDesc.new()
|
||||||
|
layer.dest.color[0].load_action = LoadAction.dont_care
|
||||||
|
layer.dest.depth.load_action = LoadAction.clear
|
||||||
|
|
||||||
|
ctx.layer_render("default", layer)
|
||||||
|
|
||||||
|
} //ui_render_path
|
||||||
|
|
||||||
|
} //Renderer
|
||||||
17
outline/settings.settings.lx
Normal file
17
outline/settings.settings.lx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
engine = {
|
||||||
|
input.entry = "outline/inputs"
|
||||||
|
runtime = {
|
||||||
|
window = {
|
||||||
|
width = 960
|
||||||
|
height = 640
|
||||||
|
resizable = false
|
||||||
|
fullscreen = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render = {
|
||||||
|
antialiasing = 2
|
||||||
|
stencil = 8
|
||||||
|
depth = 24
|
||||||
|
}
|
||||||
|
}
|
||||||
14
project.luxe
Normal file
14
project.luxe
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import "luxe: project" for Entry
|
||||||
|
|
||||||
|
class Project is Entry {
|
||||||
|
|
||||||
|
construct entry(target) {
|
||||||
|
|
||||||
|
name = "game"
|
||||||
|
version = "0.0.0"
|
||||||
|
renderer = "outline/renderer"
|
||||||
|
settings = "outline/settings"
|
||||||
|
|
||||||
|
} //new
|
||||||
|
|
||||||
|
} //Project
|
||||||
3
project.modules.lx
Normal file
3
project.modules.lx
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
modules = {
|
||||||
|
luxe = "2021.0.3"
|
||||||
|
} //modules
|
||||||
328
spine.wren
Normal file
328
spine.wren
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
import "luxe: assets" for Assets
|
||||||
|
import "luxe: color" for Color
|
||||||
|
import "luxe: string" for Str
|
||||||
|
import "luxe: render" for Geometry
|
||||||
|
import "luxe: draw" for Draw, PathStyle, LineJoin, LineCap
|
||||||
|
import "luxe: math" for Math
|
||||||
|
|
||||||
|
|
||||||
|
class Spine{
|
||||||
|
bones_by_index{_bones_by_index}
|
||||||
|
bones_by_name{_bones_by_name}
|
||||||
|
|
||||||
|
static parse(id: String) : Spine{
|
||||||
|
var asset = Assets.lx(id + ".spine.lx")["spine"]
|
||||||
|
var skeleton = Assets.lx(asset["skeleton"]+".json")
|
||||||
|
var atlas = Assets.bytes(asset["atlas"]+".atlas")
|
||||||
|
|
||||||
|
var spine_asset = Spine.from_data(skeleton)
|
||||||
|
return spine_asset
|
||||||
|
}
|
||||||
|
|
||||||
|
construct from_data(jsonDict: Map){
|
||||||
|
init()
|
||||||
|
load_skeleton(jsonDict["skeleton"])
|
||||||
|
load_bones(jsonDict["bones"])
|
||||||
|
load_slots(jsonDict["slots"])
|
||||||
|
load_skins(jsonDict["skins"])
|
||||||
|
load_animations(jsonDict["animations"])
|
||||||
|
//todo: constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
init(){
|
||||||
|
_pos = [0, 0]
|
||||||
|
_size = [0, 0]
|
||||||
|
_fps = 30
|
||||||
|
|
||||||
|
_bones_by_name = {}
|
||||||
|
_bones_by_index = []
|
||||||
|
_slots_by_name = {}
|
||||||
|
_slots_by_index = []
|
||||||
|
_skins = {}
|
||||||
|
_animations = {}
|
||||||
|
_active_skins = ["default"]
|
||||||
|
_active_attachments = []
|
||||||
|
_active_animation = "" //todo: tie this into Anim system
|
||||||
|
}
|
||||||
|
|
||||||
|
load_skeleton(skeleton_data: Map){
|
||||||
|
_pos = [skeleton_data["x"], skeleton_data["y"]]
|
||||||
|
_size = [skeleton_data["width"], skeleton_data["height"]]
|
||||||
|
_fps = skeleton_data["fps"] || 30
|
||||||
|
}
|
||||||
|
|
||||||
|
load_bones(bones_data: List){
|
||||||
|
_bones_by_index.clear()
|
||||||
|
for(bone_data in bones_data){
|
||||||
|
var bone = SpineBone.from_data(bone_data, _bones_by_name)
|
||||||
|
_bones_by_name[bone.name] = bone
|
||||||
|
_bones_by_index.add(bone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load_slots(slots_data: List){
|
||||||
|
if(slots_data == null) return
|
||||||
|
_slots_by_index.clear()
|
||||||
|
for(slot_data in slots_data){
|
||||||
|
var slot = SpineSlot.from_data(slot_data, _bones_by_name)
|
||||||
|
_slots_by_name[slot.name] = slot
|
||||||
|
_slots_by_index.add(slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load_skins(skins_data: List){
|
||||||
|
if(skins_data == null) return
|
||||||
|
for(skin_data in skins_data){
|
||||||
|
var skin = SpineSkin.from_data(skin_data, this)
|
||||||
|
_skins[skin.name] = skin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load_animations(animation_data: Map){
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
draw_bones(context){draw_bones(context, [1, 0, 0, 1])}
|
||||||
|
draw_bones(context, color){
|
||||||
|
for(bone in _bones_by_index){
|
||||||
|
var pos = bone.position()
|
||||||
|
pos = [pos.x+200, pos.y+200]
|
||||||
|
var length = bone.length
|
||||||
|
var rotation = Math.radians(bone.rotation())
|
||||||
|
|
||||||
|
var direction = [rotation.cos, rotation.sin] //todo: use matrices - this approach might be lossy with nonuniform scaling
|
||||||
|
var target = [pos.x + direction.x * length, pos.y + direction.y * length]
|
||||||
|
|
||||||
|
var style = PathStyle.new()
|
||||||
|
style.color = color
|
||||||
|
Draw.line(context, pos.x, pos.y, target.x, target.y, 0, style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_outlines(context){draw_outlines(context, [0, 1, 0, 1])}
|
||||||
|
draw_outlines(context, color){
|
||||||
|
for(skin_id in _active_skins){ //todo: only draw "uppermost" attachments
|
||||||
|
_skins[skin_id].draw_outlines(context, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineSkin{
|
||||||
|
name{_name}
|
||||||
|
|
||||||
|
construct from_data(skin_data: Map, spine){
|
||||||
|
_name = skin_data["name"]
|
||||||
|
|
||||||
|
_slots = {}
|
||||||
|
for(slot in skin_data["attachments"]){
|
||||||
|
var attachments = []
|
||||||
|
for(attachment_data in slot.value){
|
||||||
|
var attachment = SpineAttachment.from_data(attachment_data.value, attachment_data.key, spine)
|
||||||
|
if(attachment) attachments.add(attachment)
|
||||||
|
}
|
||||||
|
_slots[slot.key] = attachments
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo: skins that are not the default skin can have bones/constraints/paths - handle that (http://esotericsoftware.com/spine-json-format/#Attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_outlines(context, color){
|
||||||
|
for(slot in _slots.values){
|
||||||
|
for(attachment in slot){
|
||||||
|
attachment.draw_outlines(context, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineAttachment{
|
||||||
|
type{_type}
|
||||||
|
type=(value){_type=value}
|
||||||
|
|
||||||
|
name{_name}
|
||||||
|
name=(value){_name=value}
|
||||||
|
|
||||||
|
static from_data(attachment_data: Map, map_name: String, spine): SpineAttachment{
|
||||||
|
var type = attachment_data["type"] || "region"
|
||||||
|
var name = attachment_data["name"] || map_name
|
||||||
|
|
||||||
|
var attachment
|
||||||
|
if(type == "mesh"){
|
||||||
|
var weighted = attachment_data["vertices"].count > attachment_data["uvs"].count
|
||||||
|
if(weighted){
|
||||||
|
attachment = SpineSkinnedMeshAttachment.from_data(attachment_data, spine)
|
||||||
|
} else {
|
||||||
|
attachment = SpineMeshAttachment.from_data(attachment_data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.print("Unknown attachment type \"%(type)\"")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
attachment.name = name
|
||||||
|
attachment.type = type
|
||||||
|
|
||||||
|
return attachment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineMeshAttachment is SpineAttachment{
|
||||||
|
path{(_path != null) ? _path : name} //use path when set, otherwise fall back to name
|
||||||
|
|
||||||
|
construct from_data(attachment_data: Map){
|
||||||
|
_hull = attachment_data["hull"] //amount of vertices that form the hull - always the first n vertices in the list
|
||||||
|
_path = attachment_data["path"]
|
||||||
|
//todo: parse tint (RGBA hex)
|
||||||
|
|
||||||
|
var triangles = attachment_data["triangles"]
|
||||||
|
var vertices = attachment_data["vertices"]
|
||||||
|
var uvs = attachment_data["uvs"]
|
||||||
|
|
||||||
|
_vertices = vertices
|
||||||
|
_triangles = triangles
|
||||||
|
_uvs = uvs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineSkinnedMeshAttachment is SpineAttachment{
|
||||||
|
path{(_path != null) ? _path : name} //use path when set, otherwise fall back to name
|
||||||
|
|
||||||
|
construct from_data(attachment_data: Map, spine: Spine){
|
||||||
|
_hull = attachment_data["hull"] //amount of vertices that form the hull - always the first n vertices in the list
|
||||||
|
_path = attachment_data["path"]
|
||||||
|
//todo: parse tint (RGBA hex)
|
||||||
|
|
||||||
|
var triangles = attachment_data["triangles"]
|
||||||
|
var vertices = attachment_data["vertices"]
|
||||||
|
var uvs = attachment_data["uvs"]
|
||||||
|
|
||||||
|
_vertices = []
|
||||||
|
var vert_index = 0
|
||||||
|
//var uv_index = 0
|
||||||
|
while(vert_index<vertices.count){
|
||||||
|
var bone_count = vertices[vert_index]
|
||||||
|
var bones = []
|
||||||
|
vert_index = vert_index+1
|
||||||
|
for(ii in 0...bone_count){
|
||||||
|
var bone_index = vertices[vert_index]
|
||||||
|
var bone = spine.bones_by_index[bone_index]
|
||||||
|
var bind_x = vertices[vert_index+1]
|
||||||
|
var bind_y = vertices[vert_index+2]
|
||||||
|
var weight = vertices[vert_index+3]
|
||||||
|
bones.add(SpineBoneWeight.new(bone, [bind_x, bind_y], weight))
|
||||||
|
vert_index = vert_index+4
|
||||||
|
}
|
||||||
|
//var uv = [uvs[uv_index], uvs[uv_index+1]]
|
||||||
|
//uv_index = uv_index+2
|
||||||
|
_vertices.add(SpineSkinnedVertex.new(null, bones))
|
||||||
|
}
|
||||||
|
|
||||||
|
_uvs = uvs
|
||||||
|
_triangles = triangles
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
draw_outlines(context, color){
|
||||||
|
var style = PathStyle.new()
|
||||||
|
style.color = color
|
||||||
|
var points = (0..._hull).map{|i| _vertices[i].position()}
|
||||||
|
.map{|pos| [pos.x + 200, pos.y + 200]}.toList
|
||||||
|
points.add(points[0])
|
||||||
|
Draw.path(context, points, style, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineSkinnedVertex{
|
||||||
|
bone_weights{_bone_weights}
|
||||||
|
//uv{_uv}
|
||||||
|
|
||||||
|
construct new(uv: List, bones:List){
|
||||||
|
//_uv = uv
|
||||||
|
_bone_weights = bones
|
||||||
|
}
|
||||||
|
|
||||||
|
position(){
|
||||||
|
return _bone_weights
|
||||||
|
.map{|weight| [weight.bone.transform(weight.bind_position), weight.weight]}
|
||||||
|
.map{|args| [args[0].x * args[1], args[0].y * args[1]]}
|
||||||
|
.reduce([0, 0]){|acc, item| [acc.x+item.x, acc.y+item.y]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineBoneWeight{
|
||||||
|
bind_position{_bind_position}
|
||||||
|
weight{_weight}
|
||||||
|
bone{_bone}
|
||||||
|
|
||||||
|
construct new(bone: SpineBone, bind_position: List, weight: Num){
|
||||||
|
_bone = bone
|
||||||
|
_bind_position = bind_position
|
||||||
|
_weight = weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineBone{
|
||||||
|
name{_name}
|
||||||
|
|
||||||
|
rotation{_rotation}
|
||||||
|
position{_position}
|
||||||
|
scale{_scale}
|
||||||
|
parent{_parent}
|
||||||
|
length{_length}
|
||||||
|
|
||||||
|
//todo: get good information out of bones
|
||||||
|
|
||||||
|
construct from_data(bone_data, existing_bones){
|
||||||
|
_name = bone_data["name"]
|
||||||
|
_parent = bone_data["parent"]
|
||||||
|
_parent = _parent && existing_bones[_parent] //we can just take a reference to the parent because parent bones are guaranteed to be before their children
|
||||||
|
_length = bone_data["length"] || 0
|
||||||
|
_transform = bone_data["transform"] || "normal" //todo: use enum
|
||||||
|
_skin = bone_data["skin"] || false
|
||||||
|
//all transformation are relative to the parent
|
||||||
|
_position = [bone_data["x"] || 0, bone_data["y"] || 0]
|
||||||
|
_rotation = bone_data["rotation"] || 0
|
||||||
|
_scale = [bone_data["scaleX"] || 1, bone_data["scaleY"] || 1]
|
||||||
|
_shear = [bone_data["shearX"] || 0, bone_data["shearY"] || 0]
|
||||||
|
//todo: tint of the bone
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(pos){
|
||||||
|
var bone = this
|
||||||
|
while(bone != null){
|
||||||
|
pos = [pos.x * bone.scale.x, pos.y * bone.scale.y]
|
||||||
|
Math.rotate(pos, 0, 0, bone.rotation)
|
||||||
|
pos = [pos.x + bone.position.x, pos.y + bone.position.y]
|
||||||
|
bone = bone.parent
|
||||||
|
}
|
||||||
|
return pos
|
||||||
|
}
|
||||||
|
|
||||||
|
position(){
|
||||||
|
return transform([0, 0])
|
||||||
|
}
|
||||||
|
|
||||||
|
rotation(){
|
||||||
|
var bone = this
|
||||||
|
var rot = 0
|
||||||
|
while(bone != null){
|
||||||
|
rot = rot + bone.rotation
|
||||||
|
bone = bone.parent
|
||||||
|
}
|
||||||
|
return rot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpineSlot{
|
||||||
|
name{_name}
|
||||||
|
|
||||||
|
construct from_data(slot_data, bone_dict){
|
||||||
|
_name = slot_data["name"]
|
||||||
|
_bone = slot_data["bone"]
|
||||||
|
_bone = _bone && bone_dict[_bone]
|
||||||
|
//todo: parse color
|
||||||
|
//todo: parse dark color
|
||||||
|
//todo: consider looking at and understanding how "attachment" works
|
||||||
|
_blend = slot_data["blend"] || "normal" //todo: enum
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue