324 lines
9.4 KiB
Text
324 lines
9.4 KiB
Text
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
|