commit 3c95c9e5f3b665cc8aac3999a199337a94193f51 Author: Ronja Date: Sun Jan 23 11:01:09 2022 +0100 Initial Commit first implementation of distance based trail diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9ab400 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*/.DS_Store +.DS_Store +.DS_Store? +*/thumbs.db +thumbs.db +.thumbs.db? +.luxe/ +_luxe.data/ +_luxe.deploy/ +log*.txt \ No newline at end of file diff --git a/.luxeignore b/.luxeignore new file mode 100644 index 0000000..a3de5ef --- /dev/null +++ b/.luxeignore @@ -0,0 +1 @@ +preview.png \ No newline at end of file diff --git a/game.wren b/game.wren new file mode 100644 index 0000000..eaa1d7b --- /dev/null +++ b/game.wren @@ -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 \ No newline at end of file diff --git a/modifiers/trail/trail.modifier.lx b/modifiers/trail/trail.modifier.lx new file mode 100644 index 0000000..33e763e --- /dev/null +++ b/modifiers/trail/trail.modifier.lx @@ -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"} + ] + } +} \ No newline at end of file diff --git a/modifiers/trail/trail.modifier.wren b/modifiers/trail/trail.modifier.wren new file mode 100644 index 0000000..eddbfbb --- /dev/null +++ b/modifiers/trail/trail.modifier.wren @@ -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 + diff --git a/modifiers/trail/trail.wren b/modifiers/trail/trail.wren new file mode 100644 index 0000000..11ad9f3 --- /dev/null +++ b/modifiers/trail/trail.wren @@ -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 diff --git a/outline/app.wren b/outline/app.wren new file mode 100644 index 0000000..a0f1768 --- /dev/null +++ b/outline/app.wren @@ -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 + +} // diff --git a/outline/inputs.input.lx b/outline/inputs.input.lx new file mode 100644 index 0000000..dc45459 --- /dev/null +++ b/outline/inputs.input.lx @@ -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"] + } + } +} diff --git a/outline/renderer.wren b/outline/renderer.wren new file mode 100644 index 0000000..8c00ab4 --- /dev/null +++ b/outline/renderer.wren @@ -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 diff --git a/outline/settings.settings.lx b/outline/settings.settings.lx new file mode 100644 index 0000000..fe39e7e --- /dev/null +++ b/outline/settings.settings.lx @@ -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 + } +} \ No newline at end of file diff --git a/project.luxe b/project.luxe new file mode 100644 index 0000000..ededc55 --- /dev/null +++ b/project.luxe @@ -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 \ No newline at end of file diff --git a/project.modules.lx b/project.modules.lx new file mode 100644 index 0000000..2ce9e6b --- /dev/null +++ b/project.modules.lx @@ -0,0 +1,3 @@ +modules = { + luxe = "dev" +} //modules diff --git a/queue.wren b/queue.wren new file mode 100644 index 0000000..b027d8f --- /dev/null +++ b/queue.wren @@ -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] + } +} \ No newline at end of file