mach/src/gfx/Sprite.zig
Stephen Gutekanst d045b34f70 {gfx,examples}: update all to new mach.Core module API
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2024-04-17 11:27:48 -07:00

114 lines
4.3 KiB
Zig

const std = @import("std");
const mach = @import("../main.zig");
const gpu = mach.gpu;
const gfx = mach.gfx;
const Engine = mach.Engine;
const math = mach.math;
const vec2 = math.vec2;
const Vec2 = math.Vec2;
const Vec3 = math.Vec3;
const Mat3x3 = math.Mat3x3;
const Mat4x4 = math.Mat4x4;
pub const name = .mach_gfx_sprite;
pub const Mod = mach.Mod(@This());
pub const components = .{
.transform = .{ .type = Mat4x4, .description =
\\ The sprite model transformation matrix. A sprite is measured in pixel units, starting from
\\ (0, 0) at the top-left corner and extending to the size of the sprite. By default, the world
\\ origin (0, 0) lives at the center of the window.
\\
\\ Example: in a 500px by 500px window, a sprite located at (0, 0) with size (250, 250) will
\\ cover the top-right hand corner of the window.
},
.uv_transform = .{ .type = Mat3x3, .description =
\\ UV coordinate transformation matrix describing top-left corner / origin of sprite, in pixels.
},
.size = .{ .type = Vec2, .description =
\\ The size of the sprite, in pixels.
},
.pipeline = .{ .type = mach.EntityID, .description =
\\ Which render pipeline to use for rendering the sprite.
\\
\\ This determines which shader, textures, etc. are used for rendering the sprite.
},
};
pub const local_events = .{
.update = .{ .handler = update },
};
fn update(core: *mach.Core.Mod, sprite: *Mod, sprite_pipeline: *gfx.SpritePipeline.Mod) !void {
var archetypes_iter = sprite_pipeline.entities.query(.{ .all = &.{
.{ .mach_gfx_sprite_pipeline = &.{
.built,
} },
} });
while (archetypes_iter.next()) |archetype| {
const ids = archetype.slice(.entity, .id);
const built_pipelines = archetype.slice(.mach_gfx_sprite_pipeline, .built);
for (ids, built_pipelines) |pipeline_id, *built| {
try updatePipeline(core, sprite, sprite_pipeline, pipeline_id, built);
}
}
}
fn updatePipeline(
core: *mach.Core.Mod,
sprite: *Mod,
sprite_pipeline: *gfx.SpritePipeline.Mod,
pipeline_id: mach.EntityID,
built: *gfx.SpritePipeline.BuiltPipeline,
) !void {
const device = core.state().device;
const encoder = device.createCommandEncoder(null);
defer encoder.release();
var archetypes_iter = sprite.entities.query(.{ .all = &.{
.{ .mach_gfx_sprite = &.{
.uv_transform,
.transform,
.size,
.pipeline,
} },
} });
var num_sprites: u32 = 0;
var i: usize = 0;
while (archetypes_iter.next()) |archetype| {
const transforms = archetype.slice(.mach_gfx_sprite, .transform);
const uv_transforms = archetype.slice(.mach_gfx_sprite, .uv_transform);
const sizes = archetype.slice(.mach_gfx_sprite, .size);
const pipelines = archetype.slice(.mach_gfx_sprite, .pipeline);
// TODO: currently we cannot query all sprites which have a _single_ pipeline component
// value and get back contiguous memory for all of them. This is because all sprites with
// possibly different pipeline component values are stored as the same archetype. If we
// introduce a new concept of tagging-by-value to our entity storage then we can enforce
// that all entities with the same pipeline value are stored in contiguous memory, and
// skip this copy.
for (transforms, uv_transforms, sizes, pipelines) |transform, uv_transform, size, sprite_pipeline_id| {
if (sprite_pipeline_id == pipeline_id) {
gfx.SpritePipeline.cp_transforms[i] = transform;
gfx.SpritePipeline.cp_uv_transforms[i] = uv_transform;
gfx.SpritePipeline.cp_sizes[i] = size;
i += 1;
num_sprites += 1;
}
}
}
try sprite_pipeline.set(pipeline_id, .num_sprites, num_sprites);
if (num_sprites > 0) {
encoder.writeBuffer(built.transforms, 0, gfx.SpritePipeline.cp_transforms[0..i]);
encoder.writeBuffer(built.uv_transforms, 0, gfx.SpritePipeline.cp_uv_transforms[0..i]);
encoder.writeBuffer(built.sizes, 0, gfx.SpritePipeline.cp_sizes[0..i]);
var command = encoder.finish(null);
defer command.release();
core.state().queue.submit(&[_]*gpu.CommandBuffer{command});
}
}