Add gfx.Commands.draw_texture (closes #67)
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
13c58bf106
commit
bb1d383ccb
|
@ -13,6 +13,9 @@ const CRT = extern struct {
|
||||||
|
|
||||||
const Effects = struct {
|
const Effects = struct {
|
||||||
render_texture: gfx.Texture = .default,
|
render_texture: gfx.Texture = .default,
|
||||||
|
image_textures: [2]gfx.Texture = [_]gfx.Texture{.default} ** 2,
|
||||||
|
last_time: f64 = 0,
|
||||||
|
image_index: usize = 0,
|
||||||
crt_effect: gfx.Effect = .default,
|
crt_effect: gfx.Effect = .default,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,14 +34,44 @@ fn load(display: ona.Write(gfx.Display), effects: ona.Write(Effects), assets: on
|
||||||
});
|
});
|
||||||
|
|
||||||
effects.state.crt_effect = try assets.state.load_effect_file(ona.files.bundle, "./crt.frag.spv");
|
effects.state.crt_effect = try assets.state.load_effect_file(ona.files.bundle, "./crt.frag.spv");
|
||||||
|
|
||||||
|
var descs = gfx.Descs.init(ona.heap.allocator);
|
||||||
|
|
||||||
|
defer {
|
||||||
|
descs.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
effects.state.image_textures = .{
|
||||||
|
try assets.state.load_texture(try descs.checker_texture(.{
|
||||||
|
.colors = .{gfx.colors.black, gfx.colors.purple},
|
||||||
|
.width = 8,
|
||||||
|
.height = 8,
|
||||||
|
})),
|
||||||
|
|
||||||
|
try assets.state.load_texture(try descs.checker_texture(.{
|
||||||
|
.colors = .{gfx.colors.black, gfx.colors.grey},
|
||||||
|
.width = 8,
|
||||||
|
.height = 8,
|
||||||
|
}))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const main = ona.App.setup.
|
pub const main = ona.App.setup.
|
||||||
with_module(gfx).
|
with_module(gfx).
|
||||||
with_state(Effects{}).
|
with_state(Effects{}).
|
||||||
with_system(.load, ona.system_fn(load), .{.label = "load effects"}).
|
with_system(.load, ona.system_fn(load), .{.label = "load effects"}).
|
||||||
|
with_system(.update, ona.system_fn(update), .{.label = "update effects"}).
|
||||||
with_system(.render, ona.system_fn(render), .{.label = "render effects"}).build();
|
with_system(.render, ona.system_fn(render), .{.label = "render effects"}).build();
|
||||||
|
|
||||||
|
fn update(effects: ona.Write(Effects), app: ona.Read(ona.App)) void {
|
||||||
|
const update_seconds = 5;
|
||||||
|
|
||||||
|
if ((app.state.elapsed_time - effects.state.last_time) > update_seconds) {
|
||||||
|
effects.state.image_index = (effects.state.image_index + 1) % effects.state.image_textures.len;
|
||||||
|
effects.state.last_time = app.state.elapsed_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(commands: gfx.Commands, effects: ona.Write(Effects), app: ona.Read(ona.App), display: ona.Write(gfx.Display)) !void {
|
fn render(commands: gfx.Commands, effects: ona.Write(Effects), app: ona.Read(ona.App), display: ona.Write(gfx.Display)) !void {
|
||||||
try commands.set_target(.{
|
try commands.set_target(.{
|
||||||
.texture = effects.state.render_texture,
|
.texture = effects.state.render_texture,
|
||||||
|
@ -55,7 +88,7 @@ fn render(commands: gfx.Commands, effects: ona.Write(Effects), app: ona.Read(ona
|
||||||
});
|
});
|
||||||
|
|
||||||
try commands.draw_texture(.{
|
try commands.draw_texture(.{
|
||||||
.texture = .default,
|
.texture = effects.state.image_textures[effects.state.image_index],
|
||||||
.transform = display_transform,
|
.transform = display_transform,
|
||||||
.resolution = .{display.state.width, display.state.height},
|
.resolution = .{display.state.width, display.state.height},
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,29 +7,29 @@ const ona = @import("ona");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Spawned = struct {
|
const Spawned = struct {
|
||||||
|
visual: struct {
|
||||||
|
color: gfx.Color,
|
||||||
transform: gfx.Transform2D,
|
transform: gfx.Transform2D,
|
||||||
|
},
|
||||||
|
|
||||||
lifetime_seconds: f32,
|
lifetime_seconds: f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Visuals = struct {
|
const Visuals = struct {
|
||||||
spawned_keyboards: ona.stack.Parallel(Spawned) = .{},
|
spawned: ona.stack.Parallel(Spawned) = .{},
|
||||||
spawned_mouses: ona.stack.Parallel(Spawned) = .{},
|
|
||||||
keyboard_icon: gfx.Texture = .default,
|
|
||||||
mouse_icon: gfx.Texture = .default,
|
|
||||||
random: std.Random.Xoroshiro128,
|
random: std.Random.Xoroshiro128,
|
||||||
mouse_position: @Vector(2, f32) = @splat(0),
|
mouse_position: @Vector(2, f32) = @splat(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cleanup(visuals: ona.Write(Visuals)) !void {
|
fn cleanup(visuals: ona.Write(Visuals)) !void {
|
||||||
visuals.state.spawned_keyboards.deinit();
|
visuals.state.spawned.deinit();
|
||||||
visuals.state.spawned_mouses.deinit();
|
visuals.state.spawned.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(visuals: ona.Write(Visuals)) !void {
|
fn load(visuals: ona.Write(Visuals)) !void {
|
||||||
const initial_spawn_capacity = 1024;
|
const initial_spawn_capacity = 1024;
|
||||||
|
|
||||||
try visuals.state.spawned_keyboards.grow(initial_spawn_capacity);
|
try visuals.state.spawned.grow(initial_spawn_capacity);
|
||||||
try visuals.state.spawned_mouses.grow(initial_spawn_capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const main = ona.App.setup.
|
pub const main = ona.App.setup.
|
||||||
|
@ -42,8 +42,7 @@ pub const main = ona.App.setup.
|
||||||
with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build();
|
with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build();
|
||||||
|
|
||||||
fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void {
|
fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void {
|
||||||
update_spawned(&visuals.state.spawned_keyboards);
|
update_spawned(&visuals.state.spawned);
|
||||||
update_spawned(&visuals.state.spawned_mouses);
|
|
||||||
|
|
||||||
const random = visuals.state.random.random();
|
const random = visuals.state.random.random();
|
||||||
const width: f32 = @floatFromInt(display.state.width);
|
const width: f32 = @floatFromInt(display.state.width);
|
||||||
|
@ -53,26 +52,34 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
|
||||||
for (events.messages()) |event| {
|
for (events.messages()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.key_down => {
|
.key_down => {
|
||||||
try visuals.state.spawned_keyboards.push_grow(.{
|
try visuals.state.spawned.push_grow(.{
|
||||||
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
||||||
|
|
||||||
|
.visual = .{
|
||||||
|
.color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)},
|
||||||
|
|
||||||
.transform = gfx.transform_2d(.{
|
.transform = gfx.transform_2d(.{
|
||||||
.translation = .{width * random.float(f32), height},
|
.translation = .{width * random.float(f32), height},
|
||||||
.rotation = std.math.pi * random.float(f32),
|
.rotation = std.math.pi * random.float(f32),
|
||||||
.scale = icon_scale,
|
.scale = icon_scale,
|
||||||
}),
|
}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.mouse_down => {
|
.mouse_down => {
|
||||||
try visuals.state.spawned_mouses.push_grow(.{
|
try visuals.state.spawned.push_grow(.{
|
||||||
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
||||||
|
|
||||||
|
.visual = .{
|
||||||
|
.color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)},
|
||||||
|
|
||||||
.transform = gfx.transform_2d(.{
|
.transform = gfx.transform_2d(.{
|
||||||
.translation = visuals.state.mouse_position,
|
.translation = visuals.state.mouse_position,
|
||||||
.rotation = std.math.pi * random.float(f32),
|
.rotation = std.math.pi * random.float(f32),
|
||||||
.scale = icon_scale,
|
.scale = icon_scale,
|
||||||
}),
|
}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -88,8 +95,8 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
|
||||||
fn update_spawned(spawned: *ona.stack.Parallel(Spawned)) void {
|
fn update_spawned(spawned: *ona.stack.Parallel(Spawned)) void {
|
||||||
const float_speed = 6;
|
const float_speed = 6;
|
||||||
|
|
||||||
for (spawned.values.slice(.transform)) |*transform| {
|
for (spawned.values.slice(.visual)) |*visual| {
|
||||||
transform.* = transform.translated(.{0, -float_speed});
|
visual.transform = visual.transform.translated(.{0, -float_speed});
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -113,17 +120,10 @@ fn update_spawned(spawned: *ona.stack.Parallel(Spawned)) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(visuals: ona.Write(Visuals), commands: gfx.Commands) !void {
|
fn render(visuals: ona.Write(Visuals), commands: gfx.Commands) !void {
|
||||||
for (visuals.state.spawned_keyboards.values.slice(.transform)) |transform| {
|
for (visuals.state.spawned.values.slice(.visual)) |visual| {
|
||||||
try commands.draw_texture(.{
|
try commands.draw_rect(.{
|
||||||
.texture = visuals.state.keyboard_icon,
|
.transform = visual.transform,
|
||||||
.transform = transform,
|
.color = visual.color,
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (visuals.state.spawned_mouses.values.slice(.transform)) |transform| {
|
|
||||||
try commands.draw_texture(.{
|
|
||||||
.texture = visuals.state.keyboard_icon,
|
|
||||||
.transform = transform,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,35 +454,26 @@ pub fn init() !Self {
|
||||||
}
|
}
|
||||||
|
|
||||||
const assert = struct {
|
const assert = struct {
|
||||||
fn is_handle(expected: anytype, actual: @TypeOf(expected)) void {
|
fn is_default_handle(actual: anytype) void {
|
||||||
std.debug.assert(actual == expected);
|
std.debug.assert(actual == .default);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.is_handle(gfx.Effect.default, try pools.create_effect(.{
|
var descs = gfx.Descs.init(ona.heap.allocator);
|
||||||
|
|
||||||
|
defer {
|
||||||
|
descs.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.is_default_handle(try pools.create_effect(.{
|
||||||
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert.is_handle(gfx.Texture.default, try pools.create_texture(.{
|
assert.is_default_handle(try pools.create_texture(try descs.solid_texture(.{
|
||||||
.format = .rgba8,
|
|
||||||
|
|
||||||
.access = .{
|
|
||||||
.static = .{
|
|
||||||
.data = std.mem.asBytes(&[_]u32{
|
|
||||||
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
|
||||||
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
|
||||||
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
|
||||||
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
|
||||||
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
|
||||||
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
|
||||||
0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080,
|
|
||||||
0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000, 0xFF800080, 0xFF000000,
|
|
||||||
}),
|
|
||||||
|
|
||||||
.width = 8,
|
.width = 8,
|
||||||
},
|
.height = 8,
|
||||||
},
|
.color = gfx.colors.white,
|
||||||
}));
|
})));
|
||||||
|
|
||||||
return pools;
|
return pools;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
const gfx = @import("./gfx.zig");
|
|
||||||
|
|
||||||
pub const black = greyscale(0);
|
|
||||||
|
|
||||||
pub fn greyscale(v: f32) gfx.Color {
|
|
||||||
return .{v, v, v, 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rgb(r: f32, g: f32, b: f32) gfx.Color {
|
|
||||||
return .{r, g, b, 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const white = greyscale(1);
|
|
270
src/gfx/gfx.zig
270
src/gfx/gfx.zig
|
@ -1,5 +1,3 @@
|
||||||
pub const colors = @import("./colors.zig");
|
|
||||||
|
|
||||||
const hid = @import("hid");
|
const hid = @import("hid");
|
||||||
|
|
||||||
const ona = @import("ona");
|
const ona = @import("ona");
|
||||||
|
@ -14,7 +12,6 @@ const std = @import("std");
|
||||||
|
|
||||||
pub const Assets = struct {
|
pub const Assets = struct {
|
||||||
window: *ext.SDL_Window,
|
window: *ext.SDL_Window,
|
||||||
texture_formats: ona.stack.Sequential(TextureFormat),
|
|
||||||
frame_rendered: std.Thread.ResetEvent = .{},
|
frame_rendered: std.Thread.ResetEvent = .{},
|
||||||
pending_work: WorkQueue = .{},
|
pending_work: WorkQueue = .{},
|
||||||
has_worker_thread: ?std.Thread = null,
|
has_worker_thread: ?std.Thread = null,
|
||||||
|
@ -72,8 +69,6 @@ pub const Assets = struct {
|
||||||
worker_thread.join();
|
worker_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.texture_formats.deinit();
|
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +89,6 @@ pub const Assets = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.texture_formats = .{.allocator = ona.heap.allocator},
|
|
||||||
.window = window,
|
.window = window,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -143,24 +137,6 @@ pub const Assets = struct {
|
||||||
return loaded.get().*;
|
return loaded.get().*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_texture_file(self: *Assets, storage: ona.files.Storage, path: []const u8) LoadFileError!Texture {
|
|
||||||
var arena = std.heap.ArenaAllocator.init(ona.heap.allocator);
|
|
||||||
|
|
||||||
defer {
|
|
||||||
arena.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (self.texture_formats.values) |format| {
|
|
||||||
if (!std.mem.endsWith(u8, path, format.extension)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.load_texture(try format.load_file(&arena, storage, path));
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.FormatUnsupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const thread_restriction = .main;
|
pub const thread_restriction = .main;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,12 +145,18 @@ pub const Color = @Vector(4, f32);
|
||||||
pub const Commands = struct {
|
pub const Commands = struct {
|
||||||
pending: *List,
|
pending: *List,
|
||||||
|
|
||||||
const Command = union (enum) {
|
pub const Command = union (enum) {
|
||||||
|
draw_rect: DrawRectCommand,
|
||||||
draw_texture: DrawTextureCommand,
|
draw_texture: DrawTextureCommand,
|
||||||
set_effect: SetEffectCommand,
|
set_effect: SetEffectCommand,
|
||||||
set_target: SetTargetCommand,
|
set_target: SetTargetCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const DrawRectCommand = struct {
|
||||||
|
color: Color,
|
||||||
|
transform: Transform2D,
|
||||||
|
};
|
||||||
|
|
||||||
pub const DrawTextureCommand = struct {
|
pub const DrawTextureCommand = struct {
|
||||||
texture: Texture,
|
texture: Texture,
|
||||||
transform: Transform2D,
|
transform: Transform2D,
|
||||||
|
@ -264,6 +246,10 @@ pub const Commands = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_rect(self: Commands, command: DrawRectCommand) std.mem.Allocator.Error!void {
|
||||||
|
try self.pending.stack.push_grow(.{.draw_rect = command});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw_texture(self: Commands, command: DrawTextureCommand) std.mem.Allocator.Error!void {
|
pub fn draw_texture(self: Commands, command: DrawTextureCommand) std.mem.Allocator.Error!void {
|
||||||
try self.pending.stack.push_grow(.{.draw_texture = command});
|
try self.pending.stack.push_grow(.{.draw_texture = command});
|
||||||
}
|
}
|
||||||
|
@ -286,6 +272,150 @@ pub const Commands = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Descs = struct {
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
|
|
||||||
|
pub const CheckerTextureDesc = struct {
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
colors: [2]Color,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BmpTextureDesc = struct {
|
||||||
|
storage: ona.files.Storage,
|
||||||
|
path: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SolidTextureDesc = struct {
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
color: Color,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bmp_texture(self: *Descs, desc: BmpTextureDesc) (error { OutOfMemory, FormatUnsupported })!Texture.Desc {
|
||||||
|
const header = try desc.storage.read_little(desc.path, 0, extern struct {
|
||||||
|
type: [2]u8 align (1),
|
||||||
|
file_size: u32 align (1),
|
||||||
|
reserved: [2]u16 align (1),
|
||||||
|
image_offset: u32 align (1),
|
||||||
|
header_size: u32 align (1),
|
||||||
|
pixel_width: i32 align (1),
|
||||||
|
pixel_height: i32 align (1),
|
||||||
|
color_planes: u16 align (1),
|
||||||
|
bits_per_pixel: u16 align (1),
|
||||||
|
compression_method: u32 align (1),
|
||||||
|
image_size: u32 align(1),
|
||||||
|
pixels_per_meter_x: i32 align (1),
|
||||||
|
pixels_per_meter_y: i32 align (1),
|
||||||
|
palette_colors_used: u32 align (1),
|
||||||
|
important_colors_used: u32 align (1),
|
||||||
|
}) orelse {
|
||||||
|
return error.FormatUnsupported;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!std.mem.eql(u8, &header.type, "BM")) {
|
||||||
|
return error.FormatUnsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pixel_width = std.math.cast(u16, header.pixel_width) orelse {
|
||||||
|
return error.FormatUnsupported;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pixels = try self.arena.allocator().alloc(u8, header.image_size);
|
||||||
|
const bytes_per_pixel = header.bits_per_pixel / @bitSizeOf(u8);
|
||||||
|
const alignment = 4;
|
||||||
|
const byte_stride = pixel_width * bytes_per_pixel;
|
||||||
|
const padded_byte_stride = alignment * @divTrunc((byte_stride + alignment - 1), alignment);
|
||||||
|
const byte_padding = ona.scalars.sub(padded_byte_stride, byte_stride) orelse 0;
|
||||||
|
var buffer_offset: usize = 0;
|
||||||
|
var file_offset = @as(usize, header.image_offset);
|
||||||
|
|
||||||
|
switch (header.bits_per_pixel) {
|
||||||
|
32 => {
|
||||||
|
while (buffer_offset < pixels.len) {
|
||||||
|
const line = pixels[buffer_offset .. buffer_offset + byte_stride];
|
||||||
|
|
||||||
|
if (try desc.storage.read(desc.path, line, file_offset) != byte_stride) {
|
||||||
|
return error.FormatUnsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0 .. pixel_width) |i| {
|
||||||
|
const line_offset = i * 4;
|
||||||
|
const pixel = line[line_offset .. line_offset + 4];
|
||||||
|
|
||||||
|
std.mem.swap(u8, &pixel[0], &pixel[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_offset += line.len + byte_padding;
|
||||||
|
buffer_offset += padded_byte_stride;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return error.FormatUnsupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.format = .rgba8,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.static = .{
|
||||||
|
.width = pixel_width,
|
||||||
|
.data = pixels,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checker_texture(self: *Descs, desc: CheckerTextureDesc) std.mem.Allocator.Error!Texture.Desc {
|
||||||
|
const color_data = try self.arena.allocator().alloc(u32, desc.width * desc.height);
|
||||||
|
|
||||||
|
for (color_data, 0 .. color_data.len) |*color, i| {
|
||||||
|
const row = i / desc.width;
|
||||||
|
const col = i % desc.width;
|
||||||
|
|
||||||
|
color.* = colors.compress(desc.colors[(col + (row % 2)) % desc.colors.len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.access = .{
|
||||||
|
.static = .{
|
||||||
|
.data = std.mem.sliceAsBytes(color_data),
|
||||||
|
.width = desc.width,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
.format = .rgba8,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Descs) void {
|
||||||
|
self.arena.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) Descs {
|
||||||
|
return .{
|
||||||
|
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn solid_texture(self: *Descs, desc: SolidTextureDesc) std.mem.Allocator.Error!Texture.Desc {
|
||||||
|
const color_data = try self.arena.allocator().alloc(u32, desc.width * desc.height);
|
||||||
|
|
||||||
|
@memset(color_data, colors.compress(desc.color));
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.access = .{
|
||||||
|
.static = .{
|
||||||
|
.data = std.mem.sliceAsBytes(color_data),
|
||||||
|
.width = desc.width,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
.format = .rgba8,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Display = struct {
|
pub const Display = struct {
|
||||||
width: u16 = 1280,
|
width: u16 = 1280,
|
||||||
height: u16 = 720,
|
height: u16 = 720,
|
||||||
|
@ -398,79 +528,30 @@ pub const Transform2D = extern struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn load_bmp_texture(arena: *std.heap.ArenaAllocator, storage: ona.files.Storage, path: []const u8) !Texture.Desc {
|
pub const colors = struct {
|
||||||
const header = try storage.read_little(path, 0, extern struct {
|
pub const black = greyscale(0);
|
||||||
type: [2]u8 align (1),
|
|
||||||
file_size: u32 align (1),
|
|
||||||
reserved: [2]u16 align (1),
|
|
||||||
image_offset: u32 align (1),
|
|
||||||
header_size: u32 align (1),
|
|
||||||
pixel_width: i32 align (1),
|
|
||||||
pixel_height: i32 align (1),
|
|
||||||
color_planes: u16 align (1),
|
|
||||||
bits_per_pixel: u16 align (1),
|
|
||||||
compression_method: u32 align (1),
|
|
||||||
image_size: u32 align(1),
|
|
||||||
pixels_per_meter_x: i32 align (1),
|
|
||||||
pixels_per_meter_y: i32 align (1),
|
|
||||||
palette_colors_used: u32 align (1),
|
|
||||||
important_colors_used: u32 align (1),
|
|
||||||
}) orelse {
|
|
||||||
return error.FormatUnsupported;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!std.mem.eql(u8, &header.type, "BM")) {
|
pub fn compress(color: Color) u32 {
|
||||||
return error.FormatUnsupported;
|
const range: Color = @splat(255);
|
||||||
|
const r, const g, const b, const a = color * range;
|
||||||
|
|
||||||
|
return @bitCast([_]u8{@intFromFloat(r), @intFromFloat(g), @intFromFloat(b), @intFromFloat(a)});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pixel_width = std.math.cast(u16, header.pixel_width) orelse {
|
pub const grey = greyscale(0.5);
|
||||||
return error.FormatUnsupported;
|
|
||||||
};
|
|
||||||
|
|
||||||
const pixels = try arena.allocator().alloc(u8, header.image_size);
|
pub fn greyscale(v: f32) Color {
|
||||||
const bytes_per_pixel = header.bits_per_pixel / @bitSizeOf(u8);
|
return .{v, v, v, 1};
|
||||||
const alignment = 4;
|
|
||||||
const byte_stride = pixel_width * bytes_per_pixel;
|
|
||||||
const padded_byte_stride = alignment * @divTrunc((byte_stride + alignment - 1), alignment);
|
|
||||||
const byte_padding = ona.scalars.sub(padded_byte_stride, byte_stride) orelse 0;
|
|
||||||
var buffer_offset: usize = 0;
|
|
||||||
var file_offset = @as(usize, header.image_offset);
|
|
||||||
|
|
||||||
switch (header.bits_per_pixel) {
|
|
||||||
32 => {
|
|
||||||
while (buffer_offset < pixels.len) {
|
|
||||||
const line = pixels[buffer_offset .. buffer_offset + byte_stride];
|
|
||||||
|
|
||||||
if (try storage.read(path, line, file_offset) != byte_stride) {
|
|
||||||
return error.FormatUnsupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0 .. pixel_width) |i| {
|
pub const purple = rgb(0.5, 0, 0.5);
|
||||||
const line_offset = i * 4;
|
|
||||||
const pixel = line[line_offset .. line_offset + 4];
|
|
||||||
|
|
||||||
std.mem.swap(u8, &pixel[0], &pixel[2]);
|
pub fn rgb(r: f32, g: f32, b: f32) Color {
|
||||||
|
return .{r, g, b, 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_offset += line.len + byte_padding;
|
pub const white = greyscale(1);
|
||||||
buffer_offset += padded_byte_stride;
|
};
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
else => return error.FormatUnsupported,
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.format = .rgba8,
|
|
||||||
|
|
||||||
.access = .{
|
|
||||||
.static = .{
|
|
||||||
.width = pixel_width,
|
|
||||||
.data = pixels,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll(app: ona.Write(ona.App), events: ona.Send(hid.Event)) !void {
|
pub fn poll(app: ona.Write(ona.App), events: ona.Send(hid.Event)) !void {
|
||||||
var event = @as(ext.SDL_Event, undefined);
|
var event = @as(ext.SDL_Event, undefined);
|
||||||
|
@ -551,17 +632,6 @@ pub fn setup(world: *ona.World, events: ona.App.Events) (error {Unsupported} ||
|
||||||
assets.window,
|
assets.window,
|
||||||
});
|
});
|
||||||
|
|
||||||
const builtin_texture_formats = [_]Assets.TextureFormat{
|
|
||||||
.{
|
|
||||||
.extension = "bmp",
|
|
||||||
.load_file = load_bmp_texture,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (builtin_texture_formats) |format| {
|
|
||||||
try assets.texture_formats.push_grow(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
try world.set_state(Display{});
|
try world.set_state(Display{});
|
||||||
try world.on_event(events.pre_update, ona.system_fn(poll), .{.label = "poll gfx"});
|
try world.on_event(events.pre_update, ona.system_fn(poll), .{.label = "poll gfx"});
|
||||||
try world.on_event(events.exit, ona.system_fn(stop), .{.label = "stop gfx"});
|
try world.on_event(events.exit, ona.system_fn(stop), .{.label = "stop gfx"});
|
||||||
|
|
|
@ -69,14 +69,42 @@ const Frame = struct {
|
||||||
});
|
});
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.texture_batch_buffers = .{.allocator = ona.heap.allocator},
|
.texture_batch_buffers = .{},
|
||||||
.quad_index_buffer = quad_index_buffer,
|
.quad_index_buffer = quad_index_buffer,
|
||||||
.quad_vertex_buffer = quad_vertex_buffer,
|
.quad_vertex_buffer = quad_vertex_buffer,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_rect(self: *Frame, resources: *Resources, command: gfx.Commands.DrawRectCommand) !void {
|
||||||
|
if (self.current_source_texture != .default) {
|
||||||
|
self.flush(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_source_texture = .default;
|
||||||
|
|
||||||
|
const has_filled_current_buffer = (self.drawn_count % batches_per_buffer) == 0;
|
||||||
|
const buffer_count = self.drawn_count / batches_per_buffer;
|
||||||
|
|
||||||
|
if (has_filled_current_buffer and buffer_count == self.texture_batch_buffers.len()) {
|
||||||
|
const instance_buffer = sokol.gfx.makeBuffer(.{
|
||||||
|
.size = @sizeOf(DrawTexture) * batches_per_buffer,
|
||||||
|
.usage = .STREAM,
|
||||||
|
});
|
||||||
|
|
||||||
|
errdefer sokol.gfx.destroyBuffer(instance_buffer);
|
||||||
|
|
||||||
|
try self.texture_batch_buffers.push_grow(instance_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = sokol.gfx.appendBuffer(self.texture_batch_buffers.get().?.*, sokol.gfx.asRange(&DrawTexture{
|
||||||
|
.transform = command.transform,
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.drawn_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw_texture(self: *Frame, resources: *Resources, command: gfx.Commands.DrawTextureCommand) !void {
|
pub fn draw_texture(self: *Frame, resources: *Resources, command: gfx.Commands.DrawTextureCommand) !void {
|
||||||
if (command.texture != self.current_source_texture) {
|
if (self.current_source_texture != command.texture) {
|
||||||
self.flush(resources);
|
self.flush(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +405,7 @@ pub fn process_work(pending_work: *gfx.Assets.WorkQueue, window: *ext.SDL_Window
|
||||||
while (has_command_params) |command_params| : (has_command_params = command_params.has_next) {
|
while (has_command_params) |command_params| : (has_command_params = command_params.has_next) {
|
||||||
for (command_params.param.submitted_commands()) |command| {
|
for (command_params.param.submitted_commands()) |command| {
|
||||||
try switch (command) {
|
try switch (command) {
|
||||||
|
.draw_rect => |draw_rect| frame.draw_rect(&resources, draw_rect),
|
||||||
.draw_texture => |draw_texture| frame.draw_texture(&resources, draw_texture),
|
.draw_texture => |draw_texture| frame.draw_texture(&resources, draw_texture),
|
||||||
.set_effect => |set_effect| frame.set_effect(&resources, set_effect),
|
.set_effect => |set_effect| frame.set_effect(&resources, set_effect),
|
||||||
.set_target => |set_target| frame.set_target(&resources, set_target),
|
.set_target => |set_target| frame.set_target(&resources, set_target),
|
||||||
|
|
Loading…
Reference in New Issue