initial commit

This commit is contained in:
Ronja 2021-05-30 20:47:03 +02:00
commit 12466b5d4e
16 changed files with 5640 additions and 0 deletions

10
.gitignore vendored Normal file
View 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
View file

@ -0,0 +1 @@
preview.png

17
.vscode/tasks.json vendored Normal file
View 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"
}
]
}

View 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

View file

@ -0,0 +1,4 @@
image = {
source = "assets/down_the_mountain/snowplayers.png"
generate_mipmaps_at_load = false
}

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

View file

@ -0,0 +1,4 @@
spine = {
skeleton = "assets/down_the_mountain/snowplayers",
atlas = "assets/down_the_mountain/snowplayers"
}

58
game.wren Normal file
View 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
View 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
View 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
View 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

View 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
View 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
View file

@ -0,0 +1,3 @@
modules = {
luxe = "2021.0.3"
} //modules

328
spine.wren Normal file
View 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
}
}