mirror of
https://github.com/IllusionMan1212/O2048.git
synced 2026-10-24 10:12:37 +00:00
zephr/game: fix few memory leaks (and introduced a big one). spawn particles when tiles merge
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -35,6 +35,7 @@ Tile :: struct {
|
|||||||
value: u16,
|
value: u16,
|
||||||
new_value: u16,
|
new_value: u16,
|
||||||
merged: bool,
|
merged: bool,
|
||||||
|
emitter: ParticleEmitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
Game :: struct {
|
Game :: struct {
|
||||||
@@ -55,6 +56,7 @@ Game :: struct {
|
|||||||
help_dialog: bool,
|
help_dialog: bool,
|
||||||
|
|
||||||
icon_textures: [Icon]zephr.TextureId,
|
icon_textures: [Icon]zephr.TextureId,
|
||||||
|
merge_particle_texture: zephr.TextureId,
|
||||||
palette: ColorPalette,
|
palette: ColorPalette,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +74,8 @@ neighbors := [4]zephr.Vec2 {
|
|||||||
{0, -1}, // up
|
{0, -1}, // up
|
||||||
{0, 1}, // down
|
{0, 1}, // down
|
||||||
}
|
}
|
||||||
|
@private
|
||||||
gameover_anim_time: time.Duration = 0
|
gameover_anim_time: time.Duration = 0
|
||||||
|
|
||||||
@private
|
@private
|
||||||
game : Game
|
game : Game
|
||||||
|
|
||||||
@@ -399,6 +400,8 @@ reset_game :: proc() {
|
|||||||
game.board[y][x].value = 0
|
game.board[y][x].value = 0
|
||||||
game.board[y][x].new_value = 0
|
game.board[y][x].new_value = 0
|
||||||
game.board[y][x].merged = false
|
game.board[y][x].merged = false
|
||||||
|
delete(game.board[y][x].emitter.particles)
|
||||||
|
game.board[y][x].emitter = ParticleEmitter{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,6 +421,8 @@ game_init :: proc() {
|
|||||||
game.icon_textures[.SETTINGS_ICON] = zephr.load_texture("assets/icons/settings.png")
|
game.icon_textures[.SETTINGS_ICON] = zephr.load_texture("assets/icons/settings.png")
|
||||||
game.icon_textures[.CLOSE_ICON] = zephr.load_texture("assets/icons/close.png")
|
game.icon_textures[.CLOSE_ICON] = zephr.load_texture("assets/icons/close.png")
|
||||||
|
|
||||||
|
game.merge_particle_texture = zephr.load_texture("assets/particles/merge.png")
|
||||||
|
|
||||||
reset_palette()
|
reset_palette()
|
||||||
|
|
||||||
for i in 0..<2 {
|
for i in 0..<2 {
|
||||||
@@ -448,14 +453,12 @@ spawn_new_tile_with_value :: proc(x, y: u8, value: u16) {
|
|||||||
spawn_new_tile :: proc(x, y: u8) {
|
spawn_new_tile :: proc(x, y: u8) {
|
||||||
val := get_spawned_tile_value()
|
val := get_spawned_tile_value()
|
||||||
|
|
||||||
game.board[x][y] = Tile{
|
game.board[x][y].value = val
|
||||||
value = val,
|
game.board[x][y].new_value = val
|
||||||
new_value = val,
|
game.board[x][y].position = zephr.Vec2{cast(f32)x, cast(f32)y}
|
||||||
position = zephr.Vec2{cast(f32)x, cast(f32)y},
|
game.board[x][y].start_pos = zephr.Vec2{cast(f32)x, cast(f32)y}
|
||||||
start_pos = zephr.Vec2{cast(f32)x, cast(f32)y},
|
game.board[x][y].new_pos = zephr.Vec2{cast(f32)x, cast(f32)y}
|
||||||
new_pos = zephr.Vec2{cast(f32)x, cast(f32)y},
|
game.board[x][y].scale = zephr.Vec2{1, 1}
|
||||||
scale = zephr.Vec2{1, 1},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_random_available_tile_coords :: proc() -> zephr.Vec2 {
|
get_random_available_tile_coords :: proc() -> zephr.Vec2 {
|
||||||
@@ -558,6 +561,7 @@ game_loop :: proc() {
|
|||||||
last_frame = now
|
last_frame = now
|
||||||
|
|
||||||
update_positions(delta_t)
|
update_positions(delta_t)
|
||||||
|
update_emitters(delta_t)
|
||||||
|
|
||||||
draw_bg()
|
draw_bg()
|
||||||
draw_board()
|
draw_board()
|
||||||
@@ -567,6 +571,14 @@ game_loop :: proc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_emitters :: proc(delta_t: time.Duration) {
|
||||||
|
for row in &game.board {
|
||||||
|
for tile in &row {
|
||||||
|
update_emitter(delta_t, &tile.emitter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update_positions :: proc(delta_t: time.Duration) {
|
update_positions :: proc(delta_t: time.Duration) {
|
||||||
if (game.animating) {
|
if (game.animating) {
|
||||||
switch game.last_move_dir {
|
switch game.last_move_dir {
|
||||||
@@ -578,13 +590,14 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if src.anim_timer < TILE_ANIM_DURATION {
|
if src.anim_timer < TILE_ANIM_DURATION {
|
||||||
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.anim_timer += delta_t
|
src.anim_timer += delta_t
|
||||||
} else {
|
} else {
|
||||||
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
||||||
|
|
||||||
if dest.new_value == src.value * 2 {
|
if dest.new_value == src.value * 2 {
|
||||||
|
emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200)
|
||||||
dest.merged = false
|
dest.merged = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,6 +609,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.new_pos,
|
new_pos = src.new_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = dest.emitter,
|
||||||
}
|
}
|
||||||
src^ = Tile{
|
src^ = Tile{
|
||||||
value = src.new_value,
|
value = src.new_value,
|
||||||
@@ -605,6 +619,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.start_pos,
|
new_pos = src.start_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = src.emitter
|
||||||
}
|
}
|
||||||
|
|
||||||
game.spawning_new_tile = true
|
game.spawning_new_tile = true
|
||||||
@@ -619,13 +634,14 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if src.anim_timer < TILE_ANIM_DURATION {
|
if src.anim_timer < TILE_ANIM_DURATION {
|
||||||
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.anim_timer += delta_t
|
src.anim_timer += delta_t
|
||||||
} else {
|
} else {
|
||||||
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
||||||
|
|
||||||
if dest.new_value == src.value * 2 {
|
if dest.new_value == src.value * 2 {
|
||||||
|
emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200)
|
||||||
dest.merged = false
|
dest.merged = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,6 +653,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.new_pos,
|
new_pos = src.new_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = dest.emitter,
|
||||||
}
|
}
|
||||||
src^ = Tile{
|
src^ = Tile{
|
||||||
value = src.new_value,
|
value = src.new_value,
|
||||||
@@ -646,6 +663,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.start_pos,
|
new_pos = src.start_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = src.emitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
game.spawning_new_tile = true
|
game.spawning_new_tile = true
|
||||||
@@ -660,13 +678,14 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if src.anim_timer < TILE_ANIM_DURATION {
|
if src.anim_timer < TILE_ANIM_DURATION {
|
||||||
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.anim_timer += delta_t
|
src.anim_timer += delta_t
|
||||||
} else {
|
} else {
|
||||||
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
||||||
|
|
||||||
if dest.new_value == src.value * 2 {
|
if dest.new_value == src.value * 2 {
|
||||||
|
emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200)
|
||||||
dest.merged = false
|
dest.merged = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,6 +697,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.new_pos,
|
new_pos = src.new_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = dest.emitter,
|
||||||
}
|
}
|
||||||
src^ = Tile{
|
src^ = Tile{
|
||||||
value = src.new_value,
|
value = src.new_value,
|
||||||
@@ -687,6 +707,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.start_pos,
|
new_pos = src.start_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = src.emitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
game.spawning_new_tile = true
|
game.spawning_new_tile = true
|
||||||
@@ -701,13 +722,14 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if src.anim_timer < TILE_ANIM_DURATION {
|
if src.anim_timer < TILE_ANIM_DURATION {
|
||||||
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION))
|
||||||
src.anim_timer += delta_t
|
src.anim_timer += delta_t
|
||||||
} else {
|
} else {
|
||||||
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y]
|
||||||
|
|
||||||
if dest.new_value == src.value * 2 {
|
if dest.new_value == src.value * 2 {
|
||||||
|
emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200)
|
||||||
dest.merged = false
|
dest.merged = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,6 +741,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.new_pos,
|
new_pos = src.new_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = dest.emitter,
|
||||||
}
|
}
|
||||||
src^ = Tile{
|
src^ = Tile{
|
||||||
value = src.new_value,
|
value = src.new_value,
|
||||||
@@ -728,6 +751,7 @@ update_positions :: proc(delta_t: time.Duration) {
|
|||||||
new_pos = src.start_pos,
|
new_pos = src.start_pos,
|
||||||
anim_timer = 0,
|
anim_timer = 0,
|
||||||
scale = zephr.Vec2{1, 1},
|
scale = zephr.Vec2{1, 1},
|
||||||
|
emitter = src.emitter
|
||||||
}
|
}
|
||||||
|
|
||||||
game.spawning_new_tile = true
|
game.spawning_new_tile = true
|
||||||
@@ -883,6 +907,26 @@ draw_board :: proc() {
|
|||||||
|
|
||||||
zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED)
|
zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED)
|
||||||
zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED)
|
zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED)
|
||||||
|
|
||||||
|
// Draw particles
|
||||||
|
particle_con := zephr.DEFAULT_UI_CONSTRAINTS
|
||||||
|
for particle in tile.emitter.particles {
|
||||||
|
if (particle.life <= 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
zephr.set_parent_constraint(&particle_con, &tile_con)
|
||||||
|
zephr.set_x_constraint(&particle_con, particle.pos.x, .FIXED)
|
||||||
|
zephr.set_y_constraint(&particle_con, particle.pos.y, .FIXED)
|
||||||
|
particle_con.scale = particle.size
|
||||||
|
zephr.set_width_constraint(&particle_con, 30, .RELATIVE_PIXELS)
|
||||||
|
zephr.set_height_constraint(&particle_con, 1, .ASPECT_RATIO)
|
||||||
|
zephr.draw_texture(&particle_con, game.merge_particle_texture, zephr.UiStyle{
|
||||||
|
bg_color = particle.color,
|
||||||
|
fg_color = particle.color,
|
||||||
|
align = .CENTER,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
zephr.draw_quad(&tile_con, style)
|
zephr.draw_quad(&tile_con, style)
|
||||||
|
|
||||||
// value text
|
// value text
|
||||||
|
|||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "core:math/rand"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
import "zephr"
|
||||||
|
|
||||||
|
Particle :: struct {
|
||||||
|
pos: zephr.Vec2,
|
||||||
|
vel: zephr.Vec2,
|
||||||
|
color: zephr.Color,
|
||||||
|
size: f32,
|
||||||
|
life: time.Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleEmitter :: struct {
|
||||||
|
pos: zephr.Vec2,
|
||||||
|
particles: [dynamic]Particle,
|
||||||
|
duration: time.Duration,
|
||||||
|
spawn_rate: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
last_emitted_particle_idx: int = 0
|
||||||
|
// restricts emission rate to 60fps
|
||||||
|
@private
|
||||||
|
particle_time: time.Duration = 0
|
||||||
|
|
||||||
|
@private
|
||||||
|
update_particles :: proc(delta_t: time.Duration, particles: ^[dynamic]Particle, start_pos: zephr.Vec2) {
|
||||||
|
delta_t_f := cast(f32)time.duration_seconds(delta_t)
|
||||||
|
|
||||||
|
for p in particles {
|
||||||
|
p.life -= delta_t
|
||||||
|
if p.life <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: fades faster on higher framerates. don't feel like fixing it
|
||||||
|
p.color.a -= 1
|
||||||
|
p.size -= delta_t_f
|
||||||
|
p.vel.x += (p.pos.x + p.vel.x / 7) * delta_t_f
|
||||||
|
p.vel.y -= (p.pos.y + p.vel.y / 7) * delta_t_f
|
||||||
|
p.pos += p.vel * -delta_t_f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
find_first_dead_particle :: proc(particles: [dynamic]Particle) -> int {
|
||||||
|
// search from last emitted particle, should return almost immediately
|
||||||
|
for i in last_emitted_particle_idx..<cap(particles) {
|
||||||
|
if particles[i].life <= 0 {
|
||||||
|
last_emitted_particle_idx = i
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// else do linear search
|
||||||
|
for p, i in particles {
|
||||||
|
if p.life <= 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// overwrite first particle if all are alive
|
||||||
|
last_emitted_particle_idx = 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
update_emitter :: proc(delta_t: time.Duration, emitter: ^ParticleEmitter) {
|
||||||
|
if emitter.duration > 0 {
|
||||||
|
emitter.duration -= delta_t
|
||||||
|
if emitter.duration <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
particle_time += delta_t
|
||||||
|
|
||||||
|
for particle_time >= time.Millisecond * 16 {
|
||||||
|
for i in 0..<emitter.spawn_rate {
|
||||||
|
random := cast(f32)(rand.int_max(100) - 50)
|
||||||
|
offset := cast(f32)rand.int_max(100) - 50
|
||||||
|
rand_velocity := zephr.Vec2{rand.float32_range(-500.0, 500.0) * random / 20, -rand.float32_range(-500.0, 500.0) * random / 20}
|
||||||
|
found_dead_particle_idx := find_first_dead_particle(emitter.particles)
|
||||||
|
assign_at(&emitter.particles, found_dead_particle_idx, Particle{
|
||||||
|
pos = emitter.pos,
|
||||||
|
vel = rand_velocity,
|
||||||
|
color = zephr.COLOR_WHITE,
|
||||||
|
size = 1,
|
||||||
|
life = time.Millisecond * 500,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
particle_time -= time.Millisecond * 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_particles(delta_t, &emitter.particles, emitter.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter_start :: proc(emitter: ^ParticleEmitter, capacity: int, pos: zephr.Vec2, duration: time.Duration, spawn_rate: u32 = 3) {
|
||||||
|
// Should happen in an init function but this works too so idc
|
||||||
|
// Valgrind says we leak here but I don't think so because we "delete()" the memory
|
||||||
|
// when calling "reset_game()"
|
||||||
|
if emitter.particles != nil {
|
||||||
|
delete(emitter.particles)
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.particles = make([dynamic]Particle, capacity)
|
||||||
|
emitter.pos = pos
|
||||||
|
emitter.duration = duration
|
||||||
|
emitter.spawn_rate = spawn_rate
|
||||||
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
layout (location = 0) in vec2 vertex;
|
layout (location = 0) in vec2 vertex;
|
||||||
|
|
||||||
// per instance
|
// per instance
|
||||||
layout (location = 1) in vec4 offset;
|
layout (location = 1) in vec4 offset; // x,y = position, z,w = size
|
||||||
layout (location = 2) in int tex_index;
|
layout (location = 2) in int tex_index;
|
||||||
layout (location = 3) in vec4 color;
|
layout (location = 3) in vec4 color;
|
||||||
layout (location = 4) in mat4 model; // this takes locations 4,5,6,7
|
layout (location = 4) in mat4 model; // this takes locations 4,5,6,7
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ x11_assign_window_icon :: proc(icon_path: cstring, window_title: cstring) {
|
|||||||
target_size := 2 + icon_width * icon_height
|
target_size := 2 + icon_width * icon_height
|
||||||
|
|
||||||
data := make([]u64, target_size)
|
data := make([]u64, target_size)
|
||||||
|
defer delete(data)
|
||||||
|
|
||||||
// first two elements are width and height
|
// first two elements are width and height
|
||||||
data[0] = cast(u64)icon_width
|
data[0] = cast(u64)icon_width
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
package zephr
|
package zephr
|
||||||
|
|
||||||
|
import "core:log"
|
||||||
|
|
||||||
import gl "vendor:OpenGL"
|
import gl "vendor:OpenGL"
|
||||||
import stb "vendor:stb/image"
|
import stb "vendor:stb/image"
|
||||||
|
|
||||||
TextureId :: u32
|
TextureId :: u32
|
||||||
|
|
||||||
load_texture :: proc(path: cstring) -> TextureId {
|
load_texture :: proc(path: cstring) -> TextureId {
|
||||||
|
context.logger = logger
|
||||||
|
|
||||||
texture_id: TextureId
|
texture_id: TextureId
|
||||||
width, height, channels: i32
|
width, height, channels: i32
|
||||||
data := stb.load(path, &width, &height, &channels, 4) // RGBA
|
data := stb.load(path, &width, &height, &channels, 4) // RGBA
|
||||||
|
if data == nil {
|
||||||
|
log.errorf("Failed to load texture: \"%s\"", path)
|
||||||
|
}
|
||||||
defer stb.image_free(data)
|
defer stb.image_free(data)
|
||||||
|
|
||||||
format := gl.RGBA
|
format := gl.RGBA
|
||||||
|
|||||||
+9
-5
@@ -381,13 +381,13 @@ draw_triangle :: proc(constraints: ^UiConstraints, style: UiStyle) {
|
|||||||
gl.BindVertexArray(0)
|
gl.BindVertexArray(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color: Color, radius: f32, align: Alignment) {
|
draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, style: UiStyle) {
|
||||||
use_shader(ui_shader)
|
use_shader(ui_shader)
|
||||||
|
|
||||||
set_vec4f(ui_shader, "aColor", cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255)
|
set_vec4f(ui_shader, "aColor", cast(f32)style.bg_color.r / 255, cast(f32)style.bg_color.g / 255, cast(f32)style.bg_color.b / 255, cast(f32)style.bg_color.a / 255)
|
||||||
set_float(ui_shader, "uiWidth", constraints.width)
|
set_float(ui_shader, "uiWidth", constraints.width)
|
||||||
set_float(ui_shader, "uiHeight", constraints.height)
|
set_float(ui_shader, "uiHeight", constraints.height)
|
||||||
set_float(ui_shader, "borderRadius", radius)
|
set_float(ui_shader, "borderRadius", style.border_radius)
|
||||||
set_bool(ui_shader, "hasTexture", true)
|
set_bool(ui_shader, "hasTexture", true)
|
||||||
|
|
||||||
set_mat4f(ui_shader, "projection", zephr_ctx.projection)
|
set_mat4f(ui_shader, "projection", zephr_ctx.projection)
|
||||||
@@ -395,7 +395,7 @@ draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color:
|
|||||||
rect: Rect = ---
|
rect: Rect = ---
|
||||||
|
|
||||||
apply_constraints(constraints, &rect.pos, &rect.size)
|
apply_constraints(constraints, &rect.pos, &rect.size)
|
||||||
apply_alignment(align, constraints, rect.size, &rect.pos)
|
apply_alignment(style.align, constraints, rect.size, &rect.pos)
|
||||||
|
|
||||||
// set the positions after applying alignment so children can use them
|
// set the positions after applying alignment so children can use them
|
||||||
constraints.x = rect.pos.x
|
constraints.x = rect.pos.x
|
||||||
@@ -586,7 +586,11 @@ draw_icon_button :: proc(constraints: ^UiConstraints, icon_tex_id: TextureId, st
|
|||||||
set_y_constraint(&icon_constraints, 0, .RELATIVE_PIXELS)
|
set_y_constraint(&icon_constraints, 0, .RELATIVE_PIXELS)
|
||||||
set_width_constraint(&icon_constraints, constraints.width * 0.8, .FIXED)
|
set_width_constraint(&icon_constraints, constraints.width * 0.8, .FIXED)
|
||||||
set_height_constraint(&icon_constraints, constraints.height * 0.8, .FIXED)
|
set_height_constraint(&icon_constraints, constraints.height * 0.8, .FIXED)
|
||||||
draw_texture(&icon_constraints, icon_tex_id, style.fg_color, 0, .CENTER)
|
tex_style := UiStyle {
|
||||||
|
bg_color = style.fg_color,
|
||||||
|
align = .CENTER,
|
||||||
|
}
|
||||||
|
draw_texture(&icon_constraints, icon_tex_id, tex_style)
|
||||||
|
|
||||||
if (clicked && state == .ACTIVE) {
|
if (clicked && state == .ACTIVE) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -671,6 +671,8 @@ init :: proc(font_path: cstring, icon_path: cstring, window_title: cstring, wind
|
|||||||
|
|
||||||
deinit :: proc() {
|
deinit :: proc() {
|
||||||
backend_shutdown()
|
backend_shutdown()
|
||||||
|
queue.destroy(&zephr_ctx.event_queue)
|
||||||
|
delete(zephr_ctx.ui.elements)
|
||||||
//audio_close()
|
//audio_close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user