render trees

This commit is contained in:
Rokas Puzonas 2026-07-19 02:19:04 +03:00
parent 892fbb38be
commit b356038f1f
2 changed files with 195 additions and 78 deletions

View File

@ -51,6 +51,7 @@ const terrain_origin_x: i32 = -80;
const terrain_origin_y: i32 = -60; const terrain_origin_y: i32 = -60;
const terrain_width = 160; const terrain_width = 160;
const terrain_height = 70; const terrain_height = 70;
const terrain_tile_size = Vec2.init(32, 32);
const EntitySlotMap = SlotMapType(u16, u16, Entity); const EntitySlotMap = SlotMapType(u16, u16, Entity);
const EntityId = EntitySlotMap.Id; const EntityId = EntitySlotMap.Id;
@ -179,6 +180,7 @@ const Entity = struct {
tree_health: u32 = 0, tree_health: u32 = 0,
tree_max_health: u32 = 0, tree_max_health: u32 = 0,
tree_seed: u64 = 0,
station_spawn_tree_cooldown: Nanoseconds = 0, station_spawn_tree_cooldown: Nanoseconds = 0,
station_owned_tree: ?EntityId = null, station_owned_tree: ?EntityId = null,
@ -279,9 +281,19 @@ const Upgrades = struct {
}; };
const Shop = struct { const Shop = struct {
minion_upgrades: []Item, minion_count: Shop.Item,
tree_upgrades: []Item, minion_carry_capacity: Shop.Item,
fire_upgrades: []Item,
tree_count: Shop.Item,
tree_level: Shop.Item,
fire_speed: Shop.Item,
fire_heat_per_wood: Shop.Item,
fire_stack_size: Shop.Item,
minion_upgrades: []*Item,
tree_upgrades: []*Item,
fire_upgrades: []*Item,
const Item = struct { const Item = struct {
label: []const u8, label: []const u8,
@ -491,8 +503,8 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
} }
fn initShop(self: *App, arena: Allocator) !void { fn initShop(self: *App, arena: Allocator) !void {
const minion_upgrades = try arena.dupe(Shop.Item, &.{ self.shop = .{
Shop.Item{ .minion_count = Shop.Item{
.label = "Minion count", .label = "Minion count",
.limit = 50, .limit = 50,
.unlocked = true, .unlocked = true,
@ -503,9 +515,8 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
Shop.Item{ .minion_carry_capacity = Shop.Item{
.label = "Carry capacity", .label = "Carry capacity",
.unlocked = true,
.value_ref = .initU32(&self.upgrades.minion_carry_capacity), .value_ref = .initU32(&self.upgrades.minion_carry_capacity),
.value_scaling = .initFixed(1), .value_scaling = .initFixed(1),
@ -513,12 +524,9 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
});
const tree_upgrades = try arena.dupe(Shop.Item, &.{ .tree_count = Shop.Item{
Shop.Item{
.label = "Tree count", .label = "Tree count",
.unlocked = true,
.value_ref = .initU32(&self.upgrades.tree_station_count), .value_ref = .initU32(&self.upgrades.tree_station_count),
.value_scaling = .initFixed(1), .value_scaling = .initFixed(1),
@ -526,10 +534,9 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
Shop.Item{ .tree_level = Shop.Item{
.label = "Tree level", .label = "Tree level",
.limit = Upgrades.tree_levels.len, .limit = Upgrades.tree_levels.len,
.unlocked = true,
.value_ref = .initU32(&self.upgrades.tree_level), .value_ref = .initU32(&self.upgrades.tree_level),
.value_scaling = .initFixed(1), .value_scaling = .initFixed(1),
@ -537,12 +544,9 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
});
const fire_upgrades = try arena.dupe(Shop.Item, &.{ .fire_speed = Shop.Item{
Shop.Item{
.label = "Burn speed", .label = "Burn speed",
.unlocked = true,
.value_ref = .initF32(&self.upgrades.burn_speed), .value_ref = .initF32(&self.upgrades.burn_speed),
.value_scaling = .initFixed(1), .value_scaling = .initFixed(1),
@ -550,9 +554,8 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
Shop.Item{ .fire_heat_per_wood = Shop.Item{
.label = "Heat per wood", .label = "Heat per wood",
.unlocked = true,
.value_ref = .initU32(&self.upgrades.burn_heat_per_wood), .value_ref = .initU32(&self.upgrades.burn_heat_per_wood),
.value_scaling = .initFixed(1), .value_scaling = .initFixed(1),
@ -560,9 +563,8 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
Shop.Item{ .fire_stack_size = Shop.Item{
.label = "Max stack size", .label = "Stack size",
.unlocked = true,
.value_ref = .initU32(&self.upgrades.burn_max_stack_size), .value_ref = .initU32(&self.upgrades.burn_max_stack_size),
.value_scaling = .initFixed(1), .value_scaling = .initFixed(1),
@ -570,12 +572,20 @@ fn initShop(self: *App, arena: Allocator) !void {
.cost = 1, .cost = 1,
.cost_scaling = .initFixed(1), .cost_scaling = .initFixed(1),
}, },
});
self.shop = .{ .minion_upgrades = try arena.dupe(*Shop.Item, &.{
.minion_upgrades = minion_upgrades, &self.shop.minion_count,
.tree_upgrades = tree_upgrades, &self.shop.minion_carry_capacity
.fire_upgrades = fire_upgrades, }),
.tree_upgrades = try arena.dupe(*Shop.Item, &.{
&self.shop.tree_count,
&self.shop.tree_level
}),
.fire_upgrades = try arena.dupe(*Shop.Item, &.{
&self.shop.fire_speed,
&self.shop.fire_heat_per_wood,
&self.shop.fire_stack_size,
}),
}; };
} }
@ -617,6 +627,28 @@ fn initTerrain(self: *App) void {
tile.sprite = self.pickRandom(Gfx.Sprite.Id, underground_tiles); tile.sprite = self.pickRandom(Gfx.Sprite.Id, underground_tiles);
} }
} }
const grass_tiles = &[_]Gfx.Sprite.Id{
self.terrain_sheet.get(113, 45),
self.terrain_sheet.get(114, 45),
self.terrain_sheet.get(115, 45),
self.terrain_sheet.get(116, 45),
self.terrain_sheet.get(117, 45),
self.terrain_sheet.get(122, 45),
self.terrain_sheet.get(123, 45),
self.terrain_sheet.get(124, 45),
self.terrain_sheet.get(125, 45),
Gfx.getNilSprite(),
Gfx.getNilSprite(),
Gfx.getNilSprite(),
Gfx.getNilSprite(),
Gfx.getNilSprite(),
Gfx.getNilSprite(),
};
for (0..terrain_width) |x| {
const tile = self.getTerrainTile(@as(i32, @intCast(x)) + terrain_origin_x, -1);
tile.sprite = self.pickRandom(Gfx.Sprite.Id, grass_tiles);
}
} }
fn getTerrainTile(self: *App, x: i32, y: i32) *Tile { fn getTerrainTile(self: *App, x: i32, y: i32) *Tile {
@ -633,8 +665,6 @@ fn getTerrainTile(self: *App, x: i32, y: i32) *Tile {
return &self.terrain_tiles[y_u32 * terrain_width + x_u32]; return &self.terrain_tiles[y_u32 * terrain_width + x_u32];
} }
// fn getTerrainEdgeLeft(self: *App)
fn spawnEntity(self: *App, e: Entity) ?EntityId { fn spawnEntity(self: *App, e: Entity) ?EntityId {
return self.entities.insert(e) catch { return self.entities.insert(e) catch {
log.warn("Failed to spawn entity {}, entity limit reached", .{e.type}); log.warn("Failed to spawn entity {}, entity limit reached", .{e.type});
@ -679,11 +709,14 @@ fn spawnTree(self: *App, station_id: EntityId) void {
return; return;
} }
const rng = self.prng.random();
const health = self.upgrades.tree_health; const health = self.upgrades.tree_health;
if (self.spawnEntity(Entity{ if (self.spawnEntity(Entity{
.type = .tree, .type = .tree,
.tree_health = health, .tree_health = health,
.tree_max_health = health, .tree_max_health = health,
.tree_seed = rng.int(u64),
.pos = station.pos .pos = station.pos
})) |id| { })) |id| {
@ -1104,10 +1137,10 @@ fn showShopPanel(
plt: Platform.Frame, plt: Platform.Frame,
panel_label: []const u8, panel_label: []const u8,
panel_pos: Vec2, panel_pos: Vec2,
shop_items: []Shop.Item shop_items: []*Shop.Item
) !void { ) !void {
var unlocked_count: f32 = 0; var unlocked_count: f32 = 0;
for (shop_items) |*shop_item| { for (shop_items) |shop_item| {
if (shop_item.unlocked) { if (shop_item.unlocked) {
unlocked_count += 1; unlocked_count += 1;
} }
@ -1154,7 +1187,7 @@ fn showShopPanel(
Gfx.transformTranslate(.init(10, -5)); Gfx.transformTranslate(.init(10, -5));
var index: usize = 0; var index: usize = 0;
for (shop_items) |*shop_item| { for (shop_items) |shop_item| {
if (!shop_item.unlocked) { if (!shop_item.unlocked) {
continue; continue;
} }
@ -1228,7 +1261,7 @@ fn showShopPanel(
const new_value = shop_item.value_scaling.apply(shop_item.value_ref.deref()); const new_value = shop_item.value_scaling.apply(shop_item.value_ref.deref());
Gfx.drawText(self.font, .{ Gfx.drawText(self.font, .{
.text = try allocPrint(plt.arena, "> {f}", .{new_value}), .text = try allocPrint(plt.arena, "> {f}", .{new_value}),
.pos = .init(240, 0), .pos = .init(250, 0),
.height = 24, .height = 24,
.color = text_color .color = text_color
}); });
@ -1245,7 +1278,7 @@ fn showShopPanel(
} }
fn showUI(self: *App, plt: Platform.Frame) !void { fn showUI(self: *App, plt: Platform.Frame) !void {
var heat_pos = Vec2.init(0, -200); var heat_pos = Vec2.init(50, -200);
{ // Draw heat counter { // Draw heat counter
var builder = Gfx.TextBuilder.init(plt.arena); var builder = Gfx.TextBuilder.init(plt.arena);
@ -1285,13 +1318,13 @@ fn showDebugScaling(label: []const u8, scaling: *Shop.Item.Scaling) void {
_ = ImGUI.inputF32Drag(.{ .label = "quadratic", .value = &scaling.quadratic }); _ = ImGUI.inputF32Drag(.{ .label = "quadratic", .value = &scaling.quadratic });
} }
fn showDebugShopItems(self: *App, label: []const u8, shop_items: []Shop.Item) void { fn showDebugShopItems(self: *App, label: []const u8, shop_items: []*Shop.Item) void {
_ = self; // autofix _ = self; // autofix
ImGUI.pushID(.{ .string = label }); ImGUI.pushID(.{ .string = label });
defer ImGUI.popID(); defer ImGUI.popID();
_ = ImGUI.text("{s}", .{label}); _ = ImGUI.text("{s}", .{label});
for (0.., shop_items) |i, *shop_item| { for (0.., shop_items) |i, shop_item| {
ImGUI.pushID(.{ .int = @intCast(i) }); ImGUI.pushID(.{ .int = @intCast(i) });
defer ImGUI.popID(); defer ImGUI.popID();
@ -1397,6 +1430,66 @@ fn showDebug(self: *App, plt: Platform.Frame) !void {
} }
} }
fn drawTree(self: *App, seed: u64, pos: Vec2) void {
var prng = Prng.init(seed);
const rng = prng.random();
Gfx.transformPush();
defer Gfx.transformPop();
Gfx.transformTranslate(pos.sub(.init(terrain_tile_size.x/2, terrain_tile_size.y)));
Gfx.transformScale(terrain_tile_size);
Gfx.transformTranslate(.init(rng.float(f32)-0.5, 0));
const tree_middle = &[_]Gfx.Sprite.Id{
self.terrain_sheet.get(125, 41),
self.terrain_sheet.get(125, 40),
self.terrain_sheet.get(125, 39),
};
const trunk_height = rng.uintAtMost(u32, 4) + 2;
Gfx.drawSprite(self.terrain_sheet.get(125, 42), .init(0, 0), .init(1, 1), .white);
for (0..trunk_height) |i| {
const sprite = tree_middle[rng.uintLessThan(u32, tree_middle.len)];
var trunk_pos = Vec2.init(0, -@as(f32, @floatFromInt(i)));
trunk_pos.x += (rng.float(f32)-0.5)*2 * 0.15;
Gfx.drawSprite(sprite, trunk_pos, .init(1, 1), .white);
}
Gfx.transformTranslate(Vec2.initFromInt(u32, 0, trunk_height).multiplyScalar(-1));
Gfx.drawSprite(
self.terrain_sheet.get(125, 38),
.init(0, 0),
.init(1, 1),
.white
);
const inner_leaves = self.terrain_sheet.get(123, 42);
const bottom_leaves = self.terrain_sheet.get(124, 39);
const left_leaves = self.terrain_sheet.get(123, 38);
const corner_leaves = self.terrain_sheet.get(123, 39);
Gfx.drawSprite(bottom_leaves, .init(0, -1), .init(1, 1), .white);
Gfx.drawSprite(corner_leaves, .init(-1, -1), .init(1, 1), .white);
Gfx.drawSprite(corner_leaves, .init(2, -1), .init(-1, 1), .white);
const leaves_height = rng.uintAtMost(u32, 3) + 1;
for (0..leaves_height) |i| {
var base = Vec2.initFromInt(usize, 0, i).multiplyScalar(-1);
base.x += (rng.float(f32)-0.5)*2 * 0.2;
Gfx.drawSprite(inner_leaves, base.add(.init(0, -2)), .init(1, 1), .white);
Gfx.drawSprite(left_leaves, base.add(.init(2, -2)), .init(-1, 1), .white);
Gfx.drawSprite(left_leaves, base.add(.init(-1, -2)), .init(1, 1), .white);
}
Gfx.transformTranslate(Vec2.initFromInt(u32, 0, leaves_height).multiplyScalar(-1));
Gfx.drawSprite(bottom_leaves, .init(0, -1), .init(1, -1), .white);
Gfx.drawSprite(corner_leaves, .init(-1, -1), .init(1, -1), .white);
Gfx.drawSprite(corner_leaves, .init(2, -1), .init(-1, -1), .white);
}
pub fn frame(self: *App, plt: Platform.Frame) !void { pub fn frame(self: *App, plt: Platform.Frame) !void {
const input = plt.input; const input = plt.input;
_ = input; // autofix _ = input; // autofix
@ -1456,7 +1549,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
} }
const wood_station_first_pos = -100; const wood_station_first_pos = -100;
const wood_station_step = -50; const wood_station_step = -70;
if (station_count > self.upgrades.tree_station_count) { if (station_count > self.upgrades.tree_station_count) {
for (self.upgrades.tree_station_count..station_count) |i| { for (self.upgrades.tree_station_count..station_count) |i| {
@ -1695,9 +1788,8 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
continue; continue;
} }
const tile_size = Vec2.init(32, 32);
const tile_pos = Vec2.initFromInt(usize, x, y).add(.initFromInt(i32, terrain_origin_x, terrain_origin_y)); const tile_pos = Vec2.initFromInt(usize, x, y).add(.initFromInt(i32, terrain_origin_x, terrain_origin_y));
Gfx.drawSprite(tile.sprite, tile_pos.multiply(tile_size), tile_size, .white); Gfx.drawSprite(tile.sprite, tile_pos.multiply(terrain_tile_size), terrain_tile_size, .white);
} }
} }
} }
@ -1707,8 +1799,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
while (tree_iter.next()) |id| { while (tree_iter.next()) |id| {
const e = self.entities.getAssumeExists(id); const e = self.entities.getAssumeExists(id);
const tree_size = Vec2.init(40, 100); self.drawTree(e.tree_seed, e.pos);
Gfx.drawRectangle(e.pos.sub(.init(tree_size.x/2, tree_size.y)), tree_size, .rgb(40, 200, 40));
const progress = @as(f32, @floatFromInt(e.tree_health)) / @as(f32, @floatFromInt(e.tree_max_health)); const progress = @as(f32, @floatFromInt(e.tree_health)) / @as(f32, @floatFromInt(e.tree_max_health));
drawProgressBar(progress, e.pos.add(.init(0, 20)), .init(40, 10), .rgb(0, 100, 20)); drawProgressBar(progress, e.pos.add(.init(0, 20)), .init(40, 10), .rgb(0, 100, 20));
@ -1788,9 +1879,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
} }
} }
if (e.type == .wood_station) { if (e.type == .burn_station) {
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(20, 200, 20));
} else if (e.type == .burn_station) {
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(200, 20, 20)); Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(200, 20, 20));
} else if (e.type == .minion) { } else if (e.type == .minion) {
Gfx.drawText(self.font, .{ Gfx.drawText(self.font, .{

View File

@ -39,8 +39,8 @@ const State = struct {
nearest_sampler: sg.Sampler, nearest_sampler: sg.Sampler,
default_sampler: Sampler, default_sampler: Sampler,
quads_buffer: [2048]Quad, quads_buffer: [2048]VertexQuad,
quads: std.ArrayList(Quad), quads: std.ArrayList(VertexQuad),
clear_color: Color, clear_color: Color,
@ -126,7 +126,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
const max_quads = self.quads_buffer.len; const max_quads = self.quads_buffer.len;
bindings.vertex_buffers[0] = sg.makeBuffer(.{ bindings.vertex_buffers[0] = sg.makeBuffer(.{
.size = @sizeOf(Quad) * max_quads, .size = @sizeOf(VertexQuad) * max_quads,
.usage = .{ .vertex_buffer = true, .stream_update = true }, .usage = .{ .vertex_buffer = true, .stream_update = true },
.label = "quad-vertices" .label = "quad-vertices"
}); });
@ -543,42 +543,42 @@ pub fn drawRectangleOutline(pos: Vec2, size: Vec2, color: Color, width: f32, ali
drawSpriteQuad( drawSpriteQuad(
self.default_sprite, self.default_sprite,
.{ .init(.{
.init(outer_rect.right(), outer_rect.top()), .init(outer_rect.right(), outer_rect.top()),
.init(outer_rect.left(), outer_rect.top()), .init(outer_rect.left(), outer_rect.top()),
.init(inner_rect.right(), inner_rect.top()), .init(inner_rect.right(), inner_rect.top()),
.init(inner_rect.left(), inner_rect.top()), .init(inner_rect.left(), inner_rect.top()),
}, }),
color color
); );
drawSpriteQuad( drawSpriteQuad(
self.default_sprite, self.default_sprite,
.{ .init(.{
.init(outer_rect.right(), outer_rect.top()), .init(outer_rect.right(), outer_rect.top()),
.init(inner_rect.right(), inner_rect.top()), .init(inner_rect.right(), inner_rect.top()),
.init(outer_rect.right(), outer_rect.bottom()), .init(outer_rect.right(), outer_rect.bottom()),
.init(inner_rect.right(), inner_rect.bottom()), .init(inner_rect.right(), inner_rect.bottom()),
}, }),
color color
); );
drawSpriteQuad( drawSpriteQuad(
self.default_sprite, self.default_sprite,
.{ .init(.{
.init(outer_rect.left(), outer_rect.top()), .init(outer_rect.left(), outer_rect.top()),
.init(inner_rect.left(), inner_rect.top()), .init(inner_rect.left(), inner_rect.top()),
.init(outer_rect.left(), outer_rect.bottom()), .init(outer_rect.left(), outer_rect.bottom()),
.init(inner_rect.left(), inner_rect.bottom()), .init(inner_rect.left(), inner_rect.bottom()),
}, }),
color color
); );
drawSpriteQuad( drawSpriteQuad(
self.default_sprite, self.default_sprite,
.{ .init(.{
.init(outer_rect.right(), outer_rect.bottom()), .init(outer_rect.right(), outer_rect.bottom()),
.init(outer_rect.left(), outer_rect.bottom()), .init(outer_rect.left(), outer_rect.bottom()),
.init(inner_rect.right(), inner_rect.bottom()), .init(inner_rect.right(), inner_rect.bottom()),
.init(inner_rect.left(), inner_rect.bottom()), .init(inner_rect.left(), inner_rect.bottom()),
}, }),
color color
); );
} }
@ -588,14 +588,14 @@ pub fn drawLine(from: Vec2, to: Vec2, color: Color, width: f32) void {
const step = to.sub(from).normalized().multiplyScalar(width/2); const step = to.sub(from).normalized().multiplyScalar(width/2);
const top_left = from.add(step.rotateLeft90()); const top_left = from.add(step.rotateLeft90());
const bottom_left = from.add(step.rotateRight90()); const bottom_left = from.add(step.rotateRight90());
const top_right = to.add(step.rotateLeft90()); const top_right = to.add(step.rotateLeft90());
const bottom_right = to.add(step.rotateRight90()); const bottom_right = to.add(step.rotateRight90());
drawSpriteQuad( drawSpriteQuad(
self.default_sprite, self.default_sprite,
.{ top_right, top_left, bottom_right, bottom_left }, .init(.{ top_right, top_left, bottom_right, bottom_left }),
color color
); );
} }
@ -755,7 +755,7 @@ pub fn setTexture(id: Texture.Id, texture_data: ImageData) void {
} }
pub fn drawTexture(id: Texture.Id, pos: Vec2, size: Vec2, color: Color) void { pub fn drawTexture(id: Texture.Id, pos: Vec2, size: Vec2, color: Color) void {
var quad: Quad = undefined; var quad: VertexQuad = undefined;
quad.setColor(color); quad.setColor(color);
quad.setRect(.{ .pos = pos, .size = size }); quad.setRect(.{ .pos = pos, .size = size });
quad.setUVRect(.init(0, 0, 1, 1)); quad.setUVRect(.init(0, 0, 1, 1));
@ -795,7 +795,7 @@ pub const Sampler = enum {
const DrawOptions = struct { const DrawOptions = struct {
sampler: ?Sampler = null, sampler: ?Sampler = null,
texture: Texture.Id, texture: Texture.Id,
quad: Quad quad: VertexQuad
}; };
pub fn draw(opts: DrawOptions) void { pub fn draw(opts: DrawOptions) void {
@ -1180,15 +1180,15 @@ pub fn getSpriteUVRect(id: Sprite.Id) ?Rect {
return uv_rect; return uv_rect;
} }
fn drawSpriteQuad(id: Sprite.Id, points: [4]Vec2, color: Color) void { pub fn drawSpriteQuad(id: Sprite.Id, points: Quad, color: Color) void {
const self = &g_state; const self = &g_state;
var quad: Quad = undefined; var quad: VertexQuad = undefined;
quad.setColor(color); quad.setColor(color);
quad.vertices[0].position = points[0]; quad.vertices[0].position = points.positions[0];
quad.vertices[1].position = points[1]; quad.vertices[1].position = points.positions[1];
quad.vertices[2].position = points[2]; quad.vertices[2].position = points.positions[2];
quad.vertices[3].position = points[3]; quad.vertices[3].position = points.positions[3];
var texture: ?Texture.Id = null; var texture: ?Texture.Id = null;
if (id != self.nil_sprite) { if (id != self.nil_sprite) {
@ -1208,12 +1208,7 @@ fn drawSpriteQuad(id: Sprite.Id, points: [4]Vec2, color: Color) void {
} }
pub fn drawSprite(id: Sprite.Id, pos: Vec2, size: Vec2, color: Color) void { pub fn drawSprite(id: Sprite.Id, pos: Vec2, size: Vec2, color: Color) void {
const rect = .{ const rect = Quad.initRect(pos, size);
pos,
pos.add(.init(size.x, 0)),
pos.add(.init(0, size.y)),
pos.add(size),
};
drawSpriteQuad(id, rect, color); drawSpriteQuad(id, rect, color);
} }
@ -2296,16 +2291,16 @@ pub const Vertex = extern struct {
color: Vec4, color: Vec4,
}; };
pub const Quad = extern struct { pub const VertexQuad = extern struct {
vertices: [4]Vertex, vertices: [4]Vertex,
pub fn init(vertices: [4]Vertex) Quad { pub fn init(vertices: [4]Vertex) VertexQuad {
return Quad{ return VertexQuad{
.vertices = vertices .vertices = vertices
}; };
} }
pub fn setColor(self: *Quad, color: Color) void { pub fn setColor(self: *VertexQuad, color: Color) void {
for (&self.vertices) |*vertex| { for (&self.vertices) |*vertex| {
vertex.color = Vec4{ vertex.color = Vec4{
.x = color.r, .x = color.r,
@ -2316,7 +2311,7 @@ pub const Quad = extern struct {
} }
} }
pub fn setUVRect(self: *Quad, rect: Rect) void { pub fn setUVRect(self: *VertexQuad, rect: Rect) void {
const pos = rect.pos; const pos = rect.pos;
const size = rect.size; const size = rect.size;
self.vertices[0].texcoord = pos; self.vertices[0].texcoord = pos;
@ -2325,7 +2320,7 @@ pub const Quad = extern struct {
self.vertices[3].texcoord = pos.add(size); self.vertices[3].texcoord = pos.add(size);
} }
pub fn setRect(self: *Quad, rect: Rect) void { pub fn setRect(self: *VertexQuad, rect: Rect) void {
const pos = rect.pos; const pos = rect.pos;
const size = rect.size; const size = rect.size;
self.vertices[0].position = pos; self.vertices[0].position = pos;
@ -2335,6 +2330,39 @@ pub const Quad = extern struct {
} }
}; };
pub const Quad = struct {
positions: [4]Vec2,
pub fn init(positions: [4]Vec2) Quad {
return Quad{
.positions = positions
};
}
pub fn initRect(pos: Vec2, size: Vec2) Quad {
return Quad{
.positions = .{
pos,
pos.add(.init(size.x, 0)),
pos.add(.init(0, size.y)),
pos.add(size),
}
};
}
pub fn rotate(self: *Quad, rad: f32, origin: Vec2) void {
for (&self.positions) |*pos| {
pos.* = pos.sub(origin).rotate(rad).add(origin);
}
}
pub fn offset(self: *Quad, vec2: Vec2) void {
for (&self.positions) |*pos| {
pos.* = pos.add(vec2);
}
}
};
pub const TransformFrame = struct { pub const TransformFrame = struct {
offset: Vec2, offset: Vec2,
scale: Vec2, scale: Vec2,