From de4045d8ee609bb2aa349b748970606ec05ba90b Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 3 Oct 2025 14:50:37 +0300 Subject: [PATCH] examples: add random_stars.v, to illustrate that the streaming image can be moved/scaled just like any other static image --- examples/gg/random_stars.v | 118 +++++++++++++++++++++++++++++++++++++ vlib/gg/README.md | 2 + vlib/sokol/README.md | 2 + 3 files changed, 122 insertions(+) create mode 100644 examples/gg/random_stars.v diff --git a/examples/gg/random_stars.v b/examples/gg/random_stars.v new file mode 100644 index 000000000..d88778315 --- /dev/null +++ b/examples/gg/random_stars.v @@ -0,0 +1,118 @@ +import gg +import math +import time +import rand + +const pwidth = 800 + +const pheight = 600 + +struct AppState { +mut: + gg &gg.Context = unsafe { nil } + istream_idx int + pixels [pheight][pwidth]gg.Color +} + +@[direct_array_access] +fn (mut state AppState) update() { + for { + unsafe { vmemset(&state.pixels, 0, pwidth * pheight * sizeof[gg.Color]()) } + state.draw_sky() or {} + time.sleep(30_000 * time.millisecond) + } +} + +fn (mut state AppState) draw_sky() ! { + for _ in 0 .. 2000 { + mut star_size := 1 + for rand.i32_in_range(0, 100)! < 5 { + star_size += 10 + } + for rand.i32_in_range(0, 100000)! < 5 { + star_size += 85 + } + sx := if star_size < 10 { + rand.i32_in_range(-40, pwidth + 40)! + } else { + rand.i32_in_range(40, pwidth - 40)! + } + sy := if star_size < 10 { + rand.i32_in_range(-40, pheight + 40)! + } else { + rand.i32_in_range(40, pheight - 40)! + } + state.draw_star(sx, sy, gg.Color{ + r: u8(rand.i32_in_range(50, 255)!) + g: u8(rand.i32_in_range(50, 255)!) + b: u8(rand.i32_in_range(50, 255)!) + }, star_size) + } +} + +@[direct_array_access] +fn (mut state AppState) draw_star(x int, y int, c gg.Color, radius int) { + if radius == 0 { + return + } + minx := math.max(0, x - radius) + miny := math.max(0, y - radius) + maxx := math.min(pwidth, x + radius) + maxy := math.min(pheight, y + radius) + for cx in minx .. maxx { + for cy in miny .. maxy { + dx := math.abs[f32](cx - x) / f32(radius) + dy := math.abs[f32](cy - y) / f32(radius) + gradient := math.max[f32](0, math.min[f32](1, 1 - (math.sqrtf(dx) + math.sqrtf(dy)))) + if gradient < 0.01 { + continue + } + mut pixel := unsafe { &state.pixels[cy][cx] } + color := gg.Color{ + r: u8(math.min(255, int(pixel.r) + int(f32(c.r) * gradient))) + g: u8(math.min(255, int(pixel.g) + int(f32(c.g) * gradient))) + b: u8(math.min(255, int(pixel.b) + int(f32(c.b) * gradient))) + a: 255 + } + unsafe { + *pixel = color + } + } + } +} + +fn (mut state AppState) draw() { + mut istream_image := state.gg.get_cached_image_by_idx(state.istream_idx) + istream_image.update_pixel_data(unsafe { &u8(&state.pixels) }) + size := gg.window_size() + sx := -50 + 50 * math.sinf(f32(state.gg.frame) / 100) + sy := -50 + 50 * math.cosf(f32(state.gg.frame) / 100) + scale := 200 + 50 * math.sinf(f32(state.gg.frame) / 300) + wx := size.width + scale + wy := size.height + scale + state.gg.draw_image(sx, sy, wx, wy, istream_image) +} + +fn graphics_init(mut state AppState) { + state.istream_idx = state.gg.new_streaming_image(pwidth, pheight, 4, pixel_format: .rgba8) +} + +fn graphics_frame(mut state AppState) { + state.gg.begin() + state.draw() + state.gg.end() +} + +fn main() { + mut state := &AppState{} + state.gg = gg.new_context( + width: pwidth + height: pheight + window_title: 'Random stars' + init_fn: graphics_init + frame_fn: graphics_frame + user_data: state + ) + spawn state.update() + state.gg.run() +} diff --git a/vlib/gg/README.md b/vlib/gg/README.md index e5105f2fb..1364a385e 100644 --- a/vlib/gg/README.md +++ b/vlib/gg/README.md @@ -56,6 +56,8 @@ Another approach to that problem, is to draw everything yourself in a streaming texture, then upload that streaming texture as a single draw command to the GPU. You can see an example of that done in: https://github.com/vlang/v/blob/master/examples/gg/random.v +and in: +https://github.com/vlang/v/blob/master/examples/gg/random_stars.v A third approach, is to only upload your changing inputs to the GPU, and do all the calculations and drawing there in shaders. diff --git a/vlib/sokol/README.md b/vlib/sokol/README.md index 28dd55f7d..a65d42972 100644 --- a/vlib/sokol/README.md +++ b/vlib/sokol/README.md @@ -76,6 +76,8 @@ Another approach to that problem, is to draw everything yourself in a streaming texture, then upload that streaming texture as a single draw command to the GPU. You can see an example of that done in: https://github.com/vlang/v/blob/master/examples/gg/random.v +and in: +https://github.com/vlang/v/blob/master/examples/gg/random_stars.v A third approach, is to only upload your changing inputs to the GPU, and do all the calculations and drawing there in shaders. -- 2.39.5