package main import "base:runtime" import "core:fmt" import "core:math" double :: f64 v2 :: [2]f32 v3 :: [3]f32 v4 :: [4]f32 Color :: [4]byte System :: struct { particle_number: int, index: int, tpage_size: int, vertices: [^]Vertex, instances: [^]Instance, } Instance :: struct { life: int, image_uv_width: f32, image_index, image_speed, image_number: f32, position, speed, gravity: v3, image_blend, image_blend_add: v4, scale, grow: v2, image_angle, image_angle_speed: f32, hsv, hsv_add: v3, } Vertex :: struct { position: v3, color: Color, texcoord: v2, offset: v3, scale: v2, angle: f32, rotation_direction: v3, animation: f32, hsv: v3, } system_current: ^System pem_show_error :: proc(message: string, loc: runtime.Source_Code_Location) { context = context fmt.println("Error (", loc.procedure, ") L", loc.line, ": ", message, sep = "") } @(export) pem_get_size :: proc "c" (number: double) -> double { number := int(number) system := size_of(System) vertices := _projectile_system_get_vertex_buffer_size(number) instances := size_of(Instance) * number return double(system + vertices + instances) } @(export) pem_get_vertex_buffer_size :: proc "c" (number: double) -> double { return double(_projectile_system_get_vertex_buffer_size(int(number))) } _projectile_system_get_vertex_buffer_size :: proc "c" (number: int) -> int { return size_of(Vertex) * 6 * number } @(export) pem_get_vertex_buffer_offset :: proc "c" () -> double { return double(_projectile_system_get_vertex_buffer_offset()) } _projectile_system_get_vertex_buffer_offset :: proc "c" () -> int { return size_of(System) } @(export) pem_init :: proc "c" (memory: [^]byte, particle_number, tpage_size: double) { context = runtime.default_context() particle_number := int(particle_number) system := cast(^System)memory system.particle_number = particle_number system.tpage_size = int(tpage_size) system.index = 0 vertex_buffer_offset := _projectile_system_get_vertex_buffer_offset() system.vertices = cast([^]Vertex)&memory[vertex_buffer_offset] instance_buffer_offset := vertex_buffer_offset + _projectile_system_get_vertex_buffer_size(particle_number) system.instances = cast([^]Instance)&memory[instance_buffer_offset] } @(export) _pem_update :: proc "c" (system: ^System) { vertices := system.vertices instances := system.instances for i in 0 ..< system.particle_number { instances[i].life = max(instances[i].life - 1, -1) if instances[i].life == 0 { instances[i].grow = v2{0.0, 0.0} instances[i].image_blend.a = 0.0 instances[i].image_blend_add.a = 0.0 } instances[i].speed += instances[i].gravity instances[i].position += instances[i].speed instances[i].image_blend += instances[i].image_blend_add instances[i].scale += instances[i].grow instances[i].image_angle += instances[i].image_angle_speed instances[i].image_index = math.wrap(instances[i].image_index + instances[i].image_speed, instances[i].image_number) instances[i].hsv = v3 { instances[i].hsv.x + instances[i].hsv_add.x, clamp(instances[i].hsv.y + instances[i].hsv_add.y, 0.0, 2.0), clamp(instances[i].hsv.z + instances[i].hsv_add.z, 0.0, 2.0), } offset := i * 6 #unroll for j in 0 ..< 6 { vertices[offset + j].color = { u8(clamp(instances[i].image_blend.r, 0, 255)), u8(clamp(instances[i].image_blend.g, 0, 255)), u8(clamp(instances[i].image_blend.b, 0, 255)), u8(clamp(instances[i].image_blend.a, 0, 255)), } vertices[offset + j].offset = instances[i].position vertices[offset + j].scale = instances[i].scale vertices[offset + j].angle = instances[i].image_angle vertices[offset + j].animation = math.floor(instances[i].image_index) * instances[i].image_uv_width vertices[offset + j].hsv = instances[i].hsv } } } @(export) _pem_set :: proc "c" (system: ^System) { system_current = system } Origin :: enum { TopLeft, TopMiddle, TopRight, MiddleLeft, Middle, MiddleRight, BottomLeft, BottomMiddle, BottomRight, } @(export) pem_instance_create_origin :: proc "c" (x, y, w, h, origin, uv_left, uv_top, uv_right, uv_bottom, image_number: double) -> double { xorigin, yorigin: double origin := Origin(int(origin)) switch origin { case .TopLeft: xorigin = 0.0; yorigin = 0.0 case .TopMiddle: xorigin = w / 2.0; yorigin = 0.0 case .TopRight: xorigin = w; yorigin = 0.0 case .MiddleLeft: xorigin = 0.0; yorigin = h / 2.0 case .Middle: xorigin = w / 2.0; yorigin = h / 2.0 case .MiddleRight: xorigin = w; yorigin = h / 2.0 case .BottomLeft: xorigin = 0.0; yorigin = h case .BottomMiddle: xorigin = w; yorigin = h case .BottomRight: xorigin = w / 2.0; yorigin = h } return pem_instance_create(x, y, w, h, xorigin, yorigin, uv_left, uv_top, uv_right, uv_bottom, image_number) } @(export) pem_instance_create :: proc "c" (x, y, w, h, xorigin, yorigin, uv_left, uv_top, uv_right, uv_bottom, image_number: double) -> double { position := v3{f32(x), f32(y), 1.0} size := v3{f32(w), f32(h), 1.0} origin := v3{f32(xorigin), f32(yorigin), 0.0} uv_left := f32(uv_left) uv_right := f32(uv_right) uv_top := f32(uv_top) uv_bottom := f32(uv_bottom) image_number := f32(max(0.0, image_number)) uv_tl := v2{uv_left, uv_top} uv_br := v2{uv_left + (uv_right - uv_left) / image_number, uv_bottom} index := system_current.index offset := system_current.index * 6 system_current.index = (system_current.index + 1) % system_current.particle_number system_current.instances[index] = { life = -1, image_uv_width = (uv_right - uv_left) / image_number, image_index = 0.0, image_number = image_number, image_speed = 1.0, position = position, image_blend = {255.0, 255.0, 255.0, 255.0}, scale = {1.0, 1.0}, image_angle = 0.0, hsv = {0.0, 1.0, 1.0}, } tl := v3{0.0, 0.0, 1.0} tr := v3{1.0, 0.0, 1.0} bl := v3{0.0, 1.0, 1.0} br := v3{1.0, 1.0, 1.0} pos := [6]v3{tl, bl, tr, tr, bl, br} #unroll for i in 0 ..< 6 { system_current.vertices[offset + i] = { position = pos[i] * size - origin, color = {255, 255, 255, 255}, texcoord = math.lerp(uv_tl, uv_br, pos[i].xy), offset = system_current.instances[index].position, scale = system_current.instances[index].scale, angle = system_current.instances[index].image_angle, rotation_direction = v3{0.0, 0.0, -1.0}, hsv = system_current.instances[index].hsv, } } return double(index) } @(export) pem_instance_set_life :: proc "c" (id, life: double) { system_current.instances[int(id)].life = int(life) } @(export) pem_instance_set_life_to_animation :: proc "c" (id, animation_count: double) { id := int(id) image_number_from_index := system_current.instances[id].image_number - system_current.instances[id].image_index if image_number_from_index <= 0.0 { context = runtime.default_context() pem_show_error("image number left is 0", #location()) system_current.instances[id].life = -1 return } system_current.instances[id].life = int(image_number_from_index / system_current.instances[id].image_speed) * int(animation_count) } @(export) pem_instance_set_life_to_alpha_fade :: proc "c" (id: double) { id := int(id) a := system_current.instances[id].image_blend.a a_add := system_current.instances[id].image_blend_add.a if a <= 0.0 { context = runtime.default_context() pem_show_error("alpha started at 0.0 or lower", #location()) system_current.instances[id].life = -1 return } frames := int((a / a_add) * -1.0) if frames <= 0 { context = runtime.default_context() pem_show_error("alpha never reaches 0.0", #location()) system_current.instances[id].life = -1 return } system_current.instances[id].life = frames } @(export) pem_instance_set_life_to_shrink :: proc "c" (id: double) { id := int(id) scale := v2{system_current.instances[id].scale.x, system_current.instances[id].scale.y} grow := v2{system_current.instances[id].grow.x, system_current.instances[id].grow.y} frames := v2{scale.x / grow.x * -1.0, scale.y / grow.y * -1.0} frames_max := max(frames.x, frames.y) if frames_max <= 0.0 { context = runtime.default_context() pem_show_error("instance never shrinks in on itself", #location()) system_current.instances[id].life = -1 return } system_current.instances[id].life = int(frames_max) } @(export) pem_instance_set_image_blend :: proc "c" (id, r, g, b, a: double) { system_current.instances[int(id)].image_blend = {f32(r), f32(g), f32(b), f32(a)} * 255.0 } @(export) pem_instance_set_image_blend_add :: proc "c" (id, r, g, b, a: double) { system_current.instances[int(id)].image_blend_add = {f32(r), f32(g), f32(b), f32(a)} * 255.0 } @(export) pem_instance_set_speed :: proc "c" (id, hspeed, vspeed: double) { system_current.instances[int(id)].speed = {f32(hspeed), f32(vspeed), 0.0} } @(export) pem_instance_set_gravity :: proc "c" (id, x, y: double) { system_current.instances[int(id)].gravity = {f32(x), f32(y), 0.0} } @(export) pem_instance_set_scale :: proc "c" (id, xscale, yscale: double) { system_current.instances[int(id)].scale = {f32(xscale), f32(yscale)} } @(export) pem_instance_set_grow :: proc "c" (id, xgrow, ygrow: double) { system_current.instances[int(id)].grow = {f32(xgrow), f32(ygrow)} } @(export) pem_instance_set_image_angle :: proc "c" (id, angle: double) { system_current.instances[int(id)].image_angle = f32(angle) * math.RAD_PER_DEG } @(export) pem_instance_set_image_angle_speed :: proc "c" (id, angular_velocity: double) { system_current.instances[int(id)].image_angle_speed = f32(angular_velocity) * math.RAD_PER_DEG } @(export) pem_instance_set_image_angle_direction :: proc "c" (id, x, y, z: double) { offset := int(id) * 6 #unroll for i in 0 ..< 6 { system_current.vertices[offset + i].rotation_direction = {f32(x), f32(y), f32(z)} } } @(export) pem_instance_set_image_index :: proc "c" (id, index: double) { system_current.instances[int(id)].image_index = f32(index) } @(export) pem_instance_set_image_speed :: proc "c" (id, speed: double) { system_current.instances[int(id)].image_speed = f32(speed) } @(export) pem_instance_set_hsv :: proc "c" (id, h, s, v: double) { system_current.instances[int(id)].hsv = v3{f32(h) / 360.0, f32(s) / 100.0, f32(v) / 100.0} } @(export) pem_instance_set_hsv_add :: proc "c" (id, h, s, v: double) { system_current.instances[int(id)].hsv_add = v3{f32(h) / 360.0, f32(s) / 100.0, f32(v) / 100.0} }