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