Initial Commit
first implementation of distance based trail
This commit is contained in:
commit
3c95c9e5f3
13 changed files with 1205 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
|
||||
61
game.wren
Normal file
61
game.wren
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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 "modifiers/trail/trail" for Trail
|
||||
|
||||
class Game is Ready {
|
||||
|
||||
construct ready() {
|
||||
|
||||
super("ready!")
|
||||
|
||||
app = App.new()
|
||||
app.color = [0,0,0,1]
|
||||
|
||||
System.print("render size: %(app.width) x %(app.height) @ %(app.scale)x")
|
||||
|
||||
_blade = Entity.create(app.world)
|
||||
Transform.create(_blade)
|
||||
Transform.set_pos(_blade, 100, 100)
|
||||
Trail.create(_blade)
|
||||
_prev_pos = [0,0,0]
|
||||
} //ready
|
||||
|
||||
tick(delta) {
|
||||
var pos = Camera.screen_point_to_world(app.camera, Input.mouse_x(), Input.mouse_y())
|
||||
var t = delta * 30
|
||||
pos = [Math.lerp(_prev_pos.x, pos.x, t), Math.lerp(_prev_pos.y, pos.y, t)]
|
||||
if(Math.dist2D(pos, _prev_pos) > 0.1){
|
||||
var angle = Math.angle2D(_prev_pos, pos)
|
||||
Transform.set_angle2D(_blade, angle)
|
||||
_prev_pos = pos
|
||||
}
|
||||
Transform.set_pos(_blade, pos.x, pos.y)
|
||||
|
||||
if(Input.key_state_released(Key.escape)) {
|
||||
IO.shutdown()
|
||||
}
|
||||
if(Input.key_state_released(Key.space)) {
|
||||
Trail.reset(_blade)
|
||||
}
|
||||
|
||||
// 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
|
||||
16
modifiers/trail/trail.modifier.lx
Normal file
16
modifiers/trail/trail.modifier.lx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
modifier = {
|
||||
script = "modifiers/trail/trail"
|
||||
description = "Trail go brrr."
|
||||
field = "trail"
|
||||
display = "Trail" //the display in the editor
|
||||
class = "Trail" //The code generated name for the modifier API
|
||||
block = { //The data for the modifier
|
||||
fields = [
|
||||
{ name="edge_length" type="number" default=100 }
|
||||
{ name="time_based" type="boolean" default=false }
|
||||
{ name="length" type="number" default=100 }
|
||||
{ name="subdivisions" type="number" default=20 }
|
||||
{ name="material" type="id32" editor="material" default="luxe: material/solid"}
|
||||
]
|
||||
}
|
||||
}
|
||||
554
modifiers/trail/trail.modifier.wren
Normal file
554
modifiers/trail/trail.modifier.wren
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
|
||||
//-- this file is generated! don't try editing it.
|
||||
|
||||
import "luxe: io" for IO
|
||||
import "luxe: bytes" for Byter, Bytes
|
||||
import "luxe: world" for Block
|
||||
|
||||
//`modifiers/trail/trail > runtime` types
|
||||
|
||||
class ModifierRuntime {
|
||||
|
||||
construct new() {
|
||||
} //new
|
||||
|
||||
} //ModifierRuntime
|
||||
|
||||
//`modifiers/trail/trail > runtime` compilers
|
||||
|
||||
class ModifierRuntimeCompiler {
|
||||
|
||||
construct new() {
|
||||
}
|
||||
|
||||
bytes_count(instance) {
|
||||
|
||||
var size = 4 //version
|
||||
|
||||
size = size + 0
|
||||
|
||||
return size
|
||||
|
||||
} //bytes_count
|
||||
|
||||
validate(from_source_id, elements) {
|
||||
|
||||
var fields = {
|
||||
"type": false, //:luxe:internal
|
||||
"uuid": false, //:luxe:internal
|
||||
"luxe.self.modifier": false, //:luxe:internal
|
||||
}
|
||||
for(element in elements) {
|
||||
var instance = element.value
|
||||
for(field in instance) {
|
||||
if(fields[field.key] == null) {
|
||||
Fiber.abort("node `%(element.key)` in `%(from_source_id)` has extra field `%(field.key)`")
|
||||
} //if not found
|
||||
} //each field
|
||||
for(field in fields) {
|
||||
var required = field.value
|
||||
if(required && instance[field.key] == null) {
|
||||
Fiber.abort("node `%(element.key)` in `%(from_source_id)` is missing a required field `%(field.key)` (and the field has no default value)")
|
||||
}
|
||||
} //each field
|
||||
} //each element
|
||||
|
||||
} //validate
|
||||
|
||||
write(compiler, instance, out) {
|
||||
|
||||
//version
|
||||
out.write_int32(0)
|
||||
|
||||
return out
|
||||
|
||||
} //write
|
||||
|
||||
bytes_count_block() {
|
||||
|
||||
return 8
|
||||
|
||||
} //bytes_count_block
|
||||
|
||||
write_block(compiler, out) {
|
||||
|
||||
//source id
|
||||
out.write_uint32(compiler.string.hash("modifiers/trail/trail > runtime"))
|
||||
|
||||
//fields count
|
||||
out.write_int32(0)
|
||||
|
||||
} //write_block
|
||||
|
||||
write(instance) {
|
||||
|
||||
var size = bytes_count(instance)
|
||||
var out = Byter.new(size+8)
|
||||
|
||||
out.write_string("LUXEMRTM")
|
||||
|
||||
write(instance, out)
|
||||
|
||||
return out
|
||||
|
||||
} //write
|
||||
|
||||
} //ModifierRuntimeCompiler
|
||||
|
||||
//`modifiers/trail/trail > runtime` block types
|
||||
|
||||
class BlockModifierRuntime {
|
||||
|
||||
field { "trail" }
|
||||
source { "modifiers/trail/trail > runtime" }
|
||||
|
||||
construct new(block) {
|
||||
_block = block
|
||||
_inst_type = instance_type
|
||||
_inst = _inst_type.new(_block, 0)
|
||||
}
|
||||
|
||||
construct new() {
|
||||
_block = Block.create()
|
||||
_inst_type = instance_type
|
||||
_inst = _inst_type.new(_block, 0)
|
||||
Block.set_type(_block, "modifiers/trail/trail > runtime")
|
||||
} //new
|
||||
|
||||
instance_type {
|
||||
|
||||
class BlockInstModifierRuntime {
|
||||
slot { _slot }
|
||||
entity { Block.get_handle(_block, _slot) }
|
||||
block_set_slot(value) {
|
||||
_slot=value
|
||||
return this
|
||||
}
|
||||
construct new(block, slot) {
|
||||
_block = block
|
||||
_slot = slot
|
||||
} //new
|
||||
block { _block }
|
||||
} //BlockInstModifierRuntime
|
||||
|
||||
return BlockInstModifierRuntime
|
||||
|
||||
} //instance_type
|
||||
|
||||
block { _block }
|
||||
count { Block.get_count(_block) }
|
||||
add() { Block.add(_block) }
|
||||
append(other_block) { Block.append(_block, other_block) }
|
||||
each(c, fn) {
|
||||
for(i in 0 ... c) {
|
||||
fn.call(0, _inst.block_set_slot(i))
|
||||
}
|
||||
} //each
|
||||
|
||||
[slot, inst] { _inst.block_set_slot(slot) }
|
||||
|
||||
[slot] { _inst_type.new(_block, slot) }
|
||||
} //BlockModifierRuntime
|
||||
|
||||
|
||||
//`modifiers/trail/trail > data` types
|
||||
|
||||
class ModifierData {
|
||||
|
||||
construct new() {
|
||||
_edge_length = 100
|
||||
_time_based = false
|
||||
_length = 100
|
||||
_subdivisions = 20
|
||||
_material = "luxe: material/solid"
|
||||
} //new
|
||||
|
||||
edge_length { _edge_length }
|
||||
edge_length=(vvv) { _edge_length = vvv }
|
||||
|
||||
time_based { _time_based }
|
||||
time_based=(vvv) { _time_based = vvv }
|
||||
|
||||
length { _length }
|
||||
length=(vvv) { _length = vvv }
|
||||
|
||||
subdivisions { _subdivisions }
|
||||
subdivisions=(vvv) { _subdivisions = vvv }
|
||||
|
||||
material { _material }
|
||||
material=(vvv) { _material = vvv }
|
||||
|
||||
} //ModifierData
|
||||
|
||||
//`modifiers/trail/trail > data` compilers
|
||||
|
||||
class ModifierDataCompiler {
|
||||
|
||||
construct new() {
|
||||
}
|
||||
|
||||
bytes_count(instance) {
|
||||
|
||||
var size = 4 //version
|
||||
|
||||
size = size + 8 // edge_length
|
||||
|
||||
size = size + 4 // time_based
|
||||
|
||||
size = size + 8 // length
|
||||
|
||||
size = size + 8 // subdivisions
|
||||
|
||||
size = size + 4 // material
|
||||
|
||||
size = size + 0
|
||||
|
||||
return size
|
||||
|
||||
} //bytes_count
|
||||
|
||||
validate(from_source_id, elements) {
|
||||
|
||||
var fields = {
|
||||
"type": false, //:luxe:internal
|
||||
"uuid": false, //:luxe:internal
|
||||
"luxe.self.modifier": false, //:luxe:internal
|
||||
"edge_length": false,
|
||||
"time_based": false,
|
||||
"length": false,
|
||||
"subdivisions": false,
|
||||
"material": false,
|
||||
}
|
||||
for(element in elements) {
|
||||
var instance = element.value
|
||||
for(field in instance) {
|
||||
if(fields[field.key] == null) {
|
||||
Fiber.abort("node `%(element.key)` in `%(from_source_id)` has extra field `%(field.key)`")
|
||||
} //if not found
|
||||
} //each field
|
||||
for(field in fields) {
|
||||
var required = field.value
|
||||
if(required && instance[field.key] == null) {
|
||||
Fiber.abort("node `%(element.key)` in `%(from_source_id)` is missing a required field `%(field.key)` (and the field has no default value)")
|
||||
}
|
||||
} //each field
|
||||
} //each element
|
||||
|
||||
} //validate
|
||||
|
||||
write(compiler, instance, out) {
|
||||
|
||||
//version
|
||||
out.write_int32(0)
|
||||
|
||||
var edge_length = instance["edge_length"]
|
||||
if(edge_length == null) edge_length = 100
|
||||
out.write_float64(edge_length)
|
||||
|
||||
|
||||
var time_based = instance["time_based"]
|
||||
if(time_based == null) time_based = false
|
||||
out.write_int32(time_based ? 1 : 0)
|
||||
|
||||
|
||||
var length = instance["length"]
|
||||
if(length == null) length = 100
|
||||
out.write_float64(length)
|
||||
|
||||
|
||||
var subdivisions = instance["subdivisions"]
|
||||
if(subdivisions == null) subdivisions = 20
|
||||
out.write_float64(subdivisions)
|
||||
|
||||
|
||||
var material = instance["material"]
|
||||
if(material == null) material = "luxe: material/solid"
|
||||
out.write_uint32((material && material != "") ? compiler.string.hash(material) : 0)
|
||||
|
||||
|
||||
return out
|
||||
|
||||
} //write
|
||||
|
||||
bytes_count_block() {
|
||||
|
||||
return 80
|
||||
|
||||
} //bytes_count_block
|
||||
|
||||
write_block(compiler, out) {
|
||||
|
||||
//source id
|
||||
out.write_uint32(compiler.string.hash("modifiers/trail/trail > data"))
|
||||
|
||||
//fields count
|
||||
out.write_int32(5)
|
||||
|
||||
// edge_length
|
||||
out.write_uint32(compiler.string.hash("edge_length"))
|
||||
out.write_uint32(467038368) //type number
|
||||
var edge_length_default = 100
|
||||
out.write_float64(edge_length_default)
|
||||
|
||||
|
||||
// time_based
|
||||
out.write_uint32(compiler.string.hash("time_based"))
|
||||
out.write_uint32(1710517951) //type boolean
|
||||
var time_based_default = false
|
||||
out.write_int32(time_based_default ? 1 : 0)
|
||||
|
||||
|
||||
// length
|
||||
out.write_uint32(compiler.string.hash("length"))
|
||||
out.write_uint32(467038368) //type number
|
||||
var length_default = 100
|
||||
out.write_float64(length_default)
|
||||
|
||||
|
||||
// subdivisions
|
||||
out.write_uint32(compiler.string.hash("subdivisions"))
|
||||
out.write_uint32(467038368) //type number
|
||||
var subdivisions_default = 20
|
||||
out.write_float64(subdivisions_default)
|
||||
|
||||
|
||||
// material
|
||||
out.write_uint32(compiler.string.hash("material"))
|
||||
out.write_uint32(2729592961) //type id32
|
||||
var material_default = "luxe: material/solid"
|
||||
out.write_uint32((material_default && material_default != "") ? compiler.string.hash(material_default) : 0)
|
||||
|
||||
|
||||
} //write_block
|
||||
|
||||
write(instance) {
|
||||
|
||||
var size = bytes_count(instance)
|
||||
var out = Byter.new(size+8)
|
||||
|
||||
out.write_string("LUXEMDTA")
|
||||
|
||||
write(instance, out)
|
||||
|
||||
return out
|
||||
|
||||
} //write
|
||||
|
||||
} //ModifierDataCompiler
|
||||
|
||||
//`modifiers/trail/trail > data` block types
|
||||
|
||||
class BlockModifierData {
|
||||
|
||||
field { "trail" }
|
||||
source { "modifiers/trail/trail > data" }
|
||||
|
||||
construct new(block) {
|
||||
_block = block
|
||||
_inst_type = instance_type
|
||||
_inst = _inst_type.new(_block, 0)
|
||||
}
|
||||
|
||||
construct new() {
|
||||
_block = Block.create()
|
||||
_inst_type = instance_type
|
||||
_inst = _inst_type.new(_block, 0)
|
||||
Block.add(_block, "edge_length", "number", 100)
|
||||
Block.add(_block, "time_based", "boolean", false)
|
||||
Block.add(_block, "length", "number", 100)
|
||||
Block.add(_block, "subdivisions", "number", 20)
|
||||
Block.add(_block, "material", "id32", "luxe: material/solid")
|
||||
Block.set_type(_block, "modifiers/trail/trail > data")
|
||||
} //new
|
||||
|
||||
instance_type {
|
||||
|
||||
class BlockInstModifierData {
|
||||
edge_length { Block.get(_block, "edge_length", _slot) }
|
||||
edge_length=(v) { Block.set(_block, "edge_length", _slot, v) }
|
||||
time_based { Block.get(_block, "time_based", _slot) }
|
||||
time_based=(v) { Block.set(_block, "time_based", _slot, v) }
|
||||
length { Block.get(_block, "length", _slot) }
|
||||
length=(v) { Block.set(_block, "length", _slot, v) }
|
||||
subdivisions { Block.get(_block, "subdivisions", _slot) }
|
||||
subdivisions=(v) { Block.set(_block, "subdivisions", _slot, v) }
|
||||
material { Block.get(_block, "material", _slot) }
|
||||
material=(v) { Block.set(_block, "material", _slot, v) }
|
||||
slot { _slot }
|
||||
entity { Block.get_handle(_block, _slot) }
|
||||
block_set_slot(value) {
|
||||
_slot=value
|
||||
return this
|
||||
}
|
||||
construct new(block, slot) {
|
||||
_block = block
|
||||
_slot = slot
|
||||
} //new
|
||||
block { _block }
|
||||
} //BlockInstModifierData
|
||||
|
||||
return BlockInstModifierData
|
||||
|
||||
} //instance_type
|
||||
|
||||
block { _block }
|
||||
count { Block.get_count(_block) }
|
||||
add() { Block.add(_block) }
|
||||
append(other_block) { Block.append(_block, other_block) }
|
||||
each(c, fn) {
|
||||
for(i in 0 ... c) {
|
||||
fn.call(0, _inst.block_set_slot(i))
|
||||
}
|
||||
} //each
|
||||
|
||||
[slot, inst] { _inst.block_set_slot(slot) }
|
||||
|
||||
[slot] { _inst_type.new(_block, slot) }
|
||||
} //BlockModifierData
|
||||
|
||||
|
||||
//`modifiers/trail/trail > world` types
|
||||
|
||||
class ModifierWorld {
|
||||
|
||||
construct new() {
|
||||
} //new
|
||||
|
||||
} //ModifierWorld
|
||||
|
||||
//`modifiers/trail/trail > world` compilers
|
||||
|
||||
class ModifierWorldCompiler {
|
||||
|
||||
construct new() {
|
||||
}
|
||||
|
||||
bytes_count(instance) {
|
||||
|
||||
var size = 4 //version
|
||||
|
||||
size = size + 0
|
||||
|
||||
return size
|
||||
|
||||
} //bytes_count
|
||||
|
||||
validate(from_source_id, elements) {
|
||||
|
||||
var fields = {
|
||||
"type": false, //:luxe:internal
|
||||
"uuid": false, //:luxe:internal
|
||||
"luxe.self.modifier": false, //:luxe:internal
|
||||
}
|
||||
for(element in elements) {
|
||||
var instance = element.value
|
||||
for(field in instance) {
|
||||
if(fields[field.key] == null) {
|
||||
Fiber.abort("node `%(element.key)` in `%(from_source_id)` has extra field `%(field.key)`")
|
||||
} //if not found
|
||||
} //each field
|
||||
for(field in fields) {
|
||||
var required = field.value
|
||||
if(required && instance[field.key] == null) {
|
||||
Fiber.abort("node `%(element.key)` in `%(from_source_id)` is missing a required field `%(field.key)` (and the field has no default value)")
|
||||
}
|
||||
} //each field
|
||||
} //each element
|
||||
|
||||
} //validate
|
||||
|
||||
write(compiler, instance, out) {
|
||||
|
||||
//version
|
||||
out.write_int32(0)
|
||||
|
||||
return out
|
||||
|
||||
} //write
|
||||
|
||||
bytes_count_block() {
|
||||
|
||||
return 8
|
||||
|
||||
} //bytes_count_block
|
||||
|
||||
write_block(compiler, out) {
|
||||
|
||||
//source id
|
||||
out.write_uint32(compiler.string.hash("modifiers/trail/trail > world"))
|
||||
|
||||
//fields count
|
||||
out.write_int32(0)
|
||||
|
||||
} //write_block
|
||||
|
||||
write(instance) {
|
||||
|
||||
var size = bytes_count(instance)
|
||||
var out = Byter.new(size+8)
|
||||
|
||||
out.write_string("LUXEMWLD")
|
||||
|
||||
write(instance, out)
|
||||
|
||||
return out
|
||||
|
||||
} //write
|
||||
|
||||
} //ModifierWorldCompiler
|
||||
|
||||
//`modifiers/trail/trail > world` block types
|
||||
|
||||
class BlockModifierWorld {
|
||||
|
||||
field { "trail" }
|
||||
source { "modifiers/trail/trail > world" }
|
||||
|
||||
construct new(block) {
|
||||
_block = block
|
||||
_inst_type = instance_type
|
||||
_inst = _inst_type.new(_block, 0)
|
||||
}
|
||||
|
||||
construct new() {
|
||||
_block = Block.create()
|
||||
_inst_type = instance_type
|
||||
_inst = _inst_type.new(_block, 0)
|
||||
Block.set_type(_block, "modifiers/trail/trail > world")
|
||||
} //new
|
||||
|
||||
instance_type {
|
||||
|
||||
class BlockInstModifierWorld {
|
||||
slot { _slot }
|
||||
entity { Block.get_handle(_block, _slot) }
|
||||
block_set_slot(value) {
|
||||
_slot=value
|
||||
return this
|
||||
}
|
||||
construct new(block, slot) {
|
||||
_block = block
|
||||
_slot = slot
|
||||
} //new
|
||||
block { _block }
|
||||
} //BlockInstModifierWorld
|
||||
|
||||
return BlockInstModifierWorld
|
||||
|
||||
} //instance_type
|
||||
|
||||
block { _block }
|
||||
count { Block.get_count(_block) }
|
||||
add() { Block.add(_block) }
|
||||
append(other_block) { Block.append(_block, other_block) }
|
||||
each(c, fn) {
|
||||
for(i in 0 ... c) {
|
||||
fn.call(0, _inst.block_set_slot(i))
|
||||
}
|
||||
} //each
|
||||
|
||||
[slot, inst] { _inst.block_set_slot(slot) }
|
||||
|
||||
[slot] { _inst_type.new(_block, slot) }
|
||||
} //BlockModifierWorld
|
||||
|
||||
324
modifiers/trail/trail.wren
Normal file
324
modifiers/trail/trail.wren
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
import "luxe: io" for IO
|
||||
import "luxe: world" for World, Entity, Modifiers, ModifierSystem, Transform
|
||||
import "luxe: draw" for Draw, PathStyle, LineCap
|
||||
import "luxe: color" for Color
|
||||
import "luxe: render" for Geometry, Primitive, IndexType, Render
|
||||
import "luxe: assets" for Assets, Strings
|
||||
import "luxe: bytes" for Uint16, Floats
|
||||
|
||||
import "queue" for Queue
|
||||
|
||||
import "modifiers/trail/trail.modifier" for ModifierData
|
||||
import "luxe: math" for Math
|
||||
|
||||
//User facing API
|
||||
//This is what the user of your modifier will interact with
|
||||
class Trail {
|
||||
|
||||
static create(entity) { Modifiers.create(This, entity) }
|
||||
static destroy(entity) { Modifiers.destroy(This, entity) }
|
||||
static has(entity) { Modifiers.has(This, entity) }
|
||||
|
||||
static reset(entity){
|
||||
Modifiers.get_system(This, entity).reset(entity )
|
||||
}
|
||||
|
||||
} //Trail
|
||||
|
||||
#hidden
|
||||
class Point {
|
||||
construct new(pos: Vec, up: Vec){
|
||||
_pos = pos
|
||||
_up = up
|
||||
}
|
||||
|
||||
pos{_pos}
|
||||
up{_up}
|
||||
}
|
||||
|
||||
#hidden
|
||||
class TrailData {
|
||||
construct new(geometry, indices, positions, colors, uvs){
|
||||
_geometry = geometry
|
||||
_index_buf = indices
|
||||
_pos_buf = positions
|
||||
_col_buf = colors
|
||||
_uv_buf = uvs
|
||||
|
||||
_progress = 0
|
||||
_progress_since_last_point = 0
|
||||
_points = Queue.new()
|
||||
}
|
||||
|
||||
geometry{_geometry}
|
||||
index_buffer{_index_buf}
|
||||
position_buffer{_pos_buf}
|
||||
color_buffer{_col_buf}
|
||||
uv_buffer{_uv_buf}
|
||||
|
||||
points: Queue {_points}
|
||||
|
||||
progress: Num {_progress}
|
||||
progress=(v){_progress=v}
|
||||
|
||||
progress_since_last_point: Num {_progress_since_last_point}
|
||||
progress_since_last_point=(v){_progress_since_last_point = v}
|
||||
|
||||
previous_pos: Vec {_previous_pos}
|
||||
previous_pos=(v){_previous_pos=v}
|
||||
|
||||
previous_up: Vec {_previous_up}
|
||||
previous_up=(v){_previous_up}
|
||||
}
|
||||
|
||||
//Your modifier system implementation.
|
||||
//This speaks to the engine and your user facing API
|
||||
//to do the actual work. You'll get notified when things change
|
||||
//in the world and respond to them here.
|
||||
class TrailSystem is ModifierSystem {
|
||||
|
||||
construct new() {
|
||||
//called when your system is first created.
|
||||
_instance_data = {}
|
||||
}
|
||||
|
||||
init(world) {
|
||||
_world = world
|
||||
_draw = Draw.create(World.render_set(world))
|
||||
var edge_debug = PathStyle.new()
|
||||
edge_debug.color = [1,1,1,1]
|
||||
edge_debug.thickness = 2
|
||||
edge_debug.cap = LineCap.round
|
||||
_debug_edge_style = edge_debug
|
||||
var path_debug = PathStyle.new()
|
||||
path_debug.color = [1, 0, 0, 1]
|
||||
path_debug.thickness = 1
|
||||
_debug_path_style = path_debug
|
||||
}
|
||||
|
||||
reset(entity: Entity){
|
||||
_instance_data[entity].points.clear()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
Draw.destroy(_draw)
|
||||
}
|
||||
|
||||
attach(entity, data: ModifierData) {
|
||||
var material_id = Strings.get(data.material)
|
||||
var material = Assets.material(material_id)
|
||||
var index_buffer = create_index_buffer(data.subdivisions)
|
||||
var geo = Geometry.create(Primitive.triangle, material, (data.subdivisions - 1) * 6, IndexType.u16, index_buffer)
|
||||
var pos_buffer = create_position_buffer(data.edge_length, data.length, data.subdivisions)
|
||||
Geometry.set_vertex_buffer(geo, 0, pos_buffer)
|
||||
var color_buffer = create_color_buffer(data.edge_length, data.length, data.subdivisions)
|
||||
Geometry.set_vertex_buffer(geo, 1, color_buffer)
|
||||
var uv_buffer = create_uv_buffer(data.edge_length, data.length, data.subdivisions)
|
||||
Geometry.set_vertex_buffer(geo, 2, uv_buffer)
|
||||
var identity = Floats.new(16)
|
||||
identity.transform(0, 0, 0, 0, 0, 0, 1, 1, 1)
|
||||
Geometry.set_world_matrix(geo, identity)
|
||||
|
||||
World.render_set_add(_world, geo, entity)
|
||||
|
||||
_instance_data[entity] = TrailData.new(geo, index_buffer, pos_buffer, color_buffer, uv_buffer)
|
||||
}
|
||||
|
||||
create_position_buffer(height: Num, length: Num, subdivs: Num): VertexBuffer{
|
||||
var raw = Floats.new(4 * 2 * subdivs)
|
||||
for(i in 0...subdivs){
|
||||
var x = length * i / (subdivs - 1)
|
||||
raw[i*2*4+0] = x
|
||||
raw[i*2*4+1] = 0
|
||||
raw[i*2*4+2] = 0
|
||||
raw[i*2*4+3] = 1
|
||||
|
||||
raw[i*2*4+4] = x
|
||||
raw[i*2*4+5] = height
|
||||
raw[i*2*4+6] = 0
|
||||
raw[i*2*4+7] = 1
|
||||
}
|
||||
//var arr = (0...(raw.size/4)).map{|i|"[%(raw[i*4]), %(raw[i*4+1]), %(raw[i*4+2])]"}.join("\n")
|
||||
//System.print(arr)
|
||||
return Render.create_vertex_buffer(raw, raw.length)
|
||||
}
|
||||
|
||||
create_color_buffer(height: Num, length: Num, subdivs: Num): VertexBuffer{
|
||||
var raw = Floats.new(4 * 2 * subdivs)
|
||||
for(i in 0...subdivs){
|
||||
var x = length * i / (subdivs - 1)
|
||||
raw[i*2*4+0] = 1
|
||||
raw[i*2*4+1] = 1
|
||||
raw[i*2*4+2] = 1
|
||||
raw[i*2*4+3] = 1
|
||||
|
||||
raw[i*2*4+4] = 1
|
||||
raw[i*2*4+5] = 1
|
||||
raw[i*2*4+6] = 1
|
||||
raw[i*2*4+7] = 1
|
||||
}
|
||||
//var arr = (0...(raw.size/4)).map{|i|"[%(raw[i*4]), %(raw[i*4+1]), %(raw[i*4+2])]"}.join("\n")
|
||||
//System.print(arr)
|
||||
return Render.create_vertex_buffer(raw, raw.length)
|
||||
}
|
||||
|
||||
create_uv_buffer(height: Num, length: Num, subdivs: Num): VertexBuffer{
|
||||
var raw = Floats.new(2 * 2 * subdivs)
|
||||
for(i in 0...subdivs){
|
||||
var x = i / (subdivs - 1)
|
||||
raw[i*2*2+0] = x
|
||||
raw[i*2*2+1] = 1
|
||||
|
||||
raw[i*2*2+2] = x
|
||||
raw[i*2*2+3] = 0
|
||||
}
|
||||
//var arr = (0...(raw.size/2)).map{|i|"[%(raw[i*2]), %(raw[i*2+1])]"}.join("\n")
|
||||
//System.print(arr)
|
||||
return Render.create_vertex_buffer(raw, raw.length)
|
||||
}
|
||||
|
||||
create_index_buffer(subdivs: Num): IndexBuffer{
|
||||
var index_count = (subdivs - 1) * 6
|
||||
var raw = Uint16.new(index_count)
|
||||
for(i in 0...(subdivs-1)){
|
||||
//first triangle
|
||||
raw[i*6+0] = i * 2 + 0
|
||||
raw[i*6+1] = i * 2 + 2
|
||||
raw[i*6+2] = i * 2 + 1
|
||||
|
||||
//second triangle
|
||||
raw[i*6+3] = i * 2 + 1
|
||||
raw[i*6+4] = i * 2 + 2
|
||||
raw[i*6+5] = i * 2 + 3
|
||||
}
|
||||
//var arr = (0...(raw.length/(3*4))).map{|i|"[%(raw[i*3]), %(raw[i*3+1]), %(raw[i*3+2])]"}.join("\n")
|
||||
//System.print(arr)
|
||||
return Render.create_index_buffer(raw, raw.length)
|
||||
}
|
||||
|
||||
detach(entity, data: ModifierData) {
|
||||
//called when detached from an entity, like on destroy
|
||||
|
||||
}
|
||||
|
||||
tick(delta) {
|
||||
//System.print("positions")
|
||||
record_positions()
|
||||
//System.print("buffers")
|
||||
update_buffers()
|
||||
//System.print("debug")
|
||||
debug_draw()
|
||||
//System.print("end")
|
||||
} //tick
|
||||
|
||||
record_positions(){
|
||||
each{|entity, data: ModifierData|
|
||||
if(!Transform.has(entity)) return
|
||||
|
||||
var instance_data: TrailData = _instance_data[entity]
|
||||
|
||||
var pos = Transform.get_pos_world(entity)
|
||||
var up = Transform.local_dir_to_world(entity, 0, data.edge_length * 0.5, 0)
|
||||
var prev_pos = instance_data.previous_pos || pos
|
||||
var prev_up = instance_data.previous_up || up
|
||||
|
||||
var dist = instance_data.progress_since_last_point
|
||||
var diff = Math.dist(pos, prev_pos)
|
||||
if(diff > data.length){
|
||||
var dir = [prev_pos.x - pos.x, prev_pos.y - pos.y, prev_pos.z - pos.z]
|
||||
Math.normalize(dir)
|
||||
dir.x = dir.x * data.length
|
||||
dir.y = dir.y * data.length
|
||||
dir.z = dir.z * data.length
|
||||
|
||||
prev_pos.x = pos.x + dir.x
|
||||
prev_pos.y = pos.y + dir.y
|
||||
prev_pos.z = pos.z + dir.z
|
||||
|
||||
diff = data.length
|
||||
}
|
||||
dist = dist + diff
|
||||
var step = data.length / data.subdivisions
|
||||
while(dist > step){
|
||||
dist = dist - step
|
||||
var t = 1 - dist / diff
|
||||
var point = Point.new([
|
||||
Math.lerp(prev_pos.x, pos.x, t),
|
||||
Math.lerp(prev_pos.y, pos.y, t),
|
||||
Math.lerp(prev_pos.z, pos.z, t)
|
||||
], [
|
||||
Math.lerp(prev_up.x, up.x, t),
|
||||
Math.lerp(prev_up.y, up.y, t),
|
||||
Math.lerp(prev_up.z, up.z, t)
|
||||
])
|
||||
Math.normalize(point.up)
|
||||
if(instance_data.points.count >= data.subdivisions - 1) instance_data.points.dequeue()
|
||||
instance_data.points.enqueue(point)
|
||||
}
|
||||
instance_data.progress_since_last_point = dist
|
||||
|
||||
instance_data.previous_pos = pos
|
||||
instance_data.previous_up = up
|
||||
}
|
||||
}
|
||||
|
||||
update_buffers(){
|
||||
each{|entity, data: ModifierData|
|
||||
var instance_data: TrailData = _instance_data[entity]
|
||||
var positions = Floats.new(Render.vertex_buffer_get_size(instance_data.position_buffer) / 4)
|
||||
Render.vertex_buffer_get_data(instance_data.position_buffer, positions, positions.size * 4, 0)
|
||||
//System.print("positions(%(Render.vertex_buffer_get_size(instance_data.position_buffer))):")
|
||||
//System.print((0...(positions.size/4)).map{|i|"[%(positions[i*4]), %(positions[i*4+1]), %(positions[i*4+2])]"}.join("\n"))
|
||||
var i = -1
|
||||
for(p in instance_data.points){
|
||||
i = i + 1
|
||||
positions[i * 8 + 0] = p.pos.x - p.up.x * data.edge_length * 0.5
|
||||
positions[i * 8 + 1] = p.pos.y - p.up.y * data.edge_length * 0.5
|
||||
positions[i * 8 + 2] = p.pos.z - p.up.z * data.edge_length * 0.5
|
||||
|
||||
positions[i * 8 + 4] = p.pos.x + p.up.x * data.edge_length * 0.5
|
||||
positions[i * 8 + 5] = p.pos.y + p.up.y * data.edge_length * 0.5
|
||||
positions[i * 8 + 6] = p.pos.z + p.up.z * data.edge_length * 0.5
|
||||
}
|
||||
if(Transform.has(entity)){
|
||||
i = i + 1
|
||||
|
||||
var start = Transform.local_point_to_world(entity, 0, data.edge_length * -0.5, 0)
|
||||
var end = Transform.local_point_to_world(entity, 0, data.edge_length * 0.5, 0)
|
||||
|
||||
positions[i * 8 + 0] = start.x
|
||||
positions[i * 8 + 1] = start.y
|
||||
positions[i * 8 + 2] = start.z
|
||||
|
||||
positions[i * 8 + 4] = end.x
|
||||
positions[i * 8 + 5] = end.y
|
||||
positions[i * 8 + 6] = end.z
|
||||
}
|
||||
Render.vertex_buffer_replace(instance_data.position_buffer, positions, positions.length)
|
||||
Geometry.set_vert_count(instance_data.geometry, i.max(0) * 6)
|
||||
}
|
||||
}
|
||||
|
||||
debug_draw(){
|
||||
each {|entity, data: ModifierData|
|
||||
var start = [0, 0]
|
||||
var end = [0, data.edge_length]
|
||||
var depth = 0
|
||||
if(Transform.has(entity)){
|
||||
start = Transform.local_point_to_world(entity, 0, data.edge_length * -0.5, 0)
|
||||
end = Transform.local_point_to_world(entity, 0, data.edge_length * 0.5, 0)
|
||||
depth = Transform.get_depth2D_world(entity)
|
||||
}
|
||||
Draw.line(_draw, start.x, start.y, end.x, end.y, depth, _debug_edge_style)
|
||||
|
||||
var instance_data: TrailData = _instance_data[entity]
|
||||
if(instance_data.points.count > 1){
|
||||
Draw.path3D(_draw, instance_data.points.map{|p:Point|p.pos}.toList, _debug_path_style, false)
|
||||
}
|
||||
}
|
||||
Draw.commit(_draw)
|
||||
}
|
||||
|
||||
} //TrailSystem
|
||||
|
||||
var Modifier = TrailSystem //required
|
||||
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 = "Trail Test"
|
||||
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 = "dev"
|
||||
} //modules
|
||||
57
queue.wren
Normal file
57
queue.wren
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
class Queue is Sequence{
|
||||
data{_data}
|
||||
count{_count}
|
||||
|
||||
construct new(){
|
||||
_data = []
|
||||
_count = 0
|
||||
_head = -1
|
||||
}
|
||||
|
||||
clear(){
|
||||
_count = 0
|
||||
_head = -1
|
||||
}
|
||||
|
||||
enqueue(value){
|
||||
_count = _count + 1
|
||||
_head = _head + 1
|
||||
if(_data.count >= _count){
|
||||
if(_head >= _data.count){
|
||||
_head = _head - _data.count
|
||||
}
|
||||
_data[_head] = value
|
||||
} else {
|
||||
_data.insert(_head, value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
dequeue(){
|
||||
if(_count == 0) return null
|
||||
var res = peek()
|
||||
_count = _count - 1
|
||||
return res
|
||||
}
|
||||
|
||||
peek(){
|
||||
if(_count == 0) return null
|
||||
var tail = _head - _count + 1
|
||||
if(tail < 0) tail = _data.count + tail
|
||||
return _data[tail]
|
||||
}
|
||||
|
||||
empty(){_count == 0}
|
||||
|
||||
iterate(iter){
|
||||
if(!iter) return _count > 0 ? 0 : false
|
||||
if(iter >= _count - 1) return false
|
||||
return iter + 1
|
||||
}
|
||||
|
||||
iteratorValue(iter){
|
||||
var index = _head - _count + 1 + iter
|
||||
if(index < 0) index = _data.count + index
|
||||
return _data[index]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue