Skip to content

Commit

Permalink
utils.tiled: object layer support (issue #36)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Ji committed Dec 21, 2024
1 parent 133b469 commit 8607493
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 8 deletions.
21 changes: 20 additions & 1 deletion examples/assets/tiled/sample_urban.tmx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.8" tiledversion="1.8.2" orientation="orthogonal" renderorder="right-down" width="48" height="32" tilewidth="16" tileheight="16" infinite="1" backgroundcolor="#77767b" nextlayerid="6" nextobjectid="1">
<map version="1.8" tiledversion="1.8.2" orientation="orthogonal" renderorder="right-down" width="48" height="32" tilewidth="16" tileheight="16" infinite="1" backgroundcolor="#77767b" nextlayerid="7" nextobjectid="9">
<properties>
<property name="fancycolor" type="color" value="#ff33d17a"/>
</properties>
Expand Down Expand Up @@ -160,4 +160,23 @@
</layer>
</group>
</group>
<objectgroup id="6" name="object layer" opacity="0.5" tintcolor="#00fff5">
<object id="1" x="146.95" y="111.135" width="128.842" height="42.0854" rotation="52.089"/>
<object id="2" gid="307" x="450.495" y="485.717" width="70.789" height="56.5844" rotation="-30"/>
<object id="3" x="280.227" y="321.732">
<point/>
</object>
<object id="4" x="288.057" y="368.62">
<point/>
</object>
<object id="5" x="277.734" y="435.007">
<point/>
</object>
<object id="6" x="126.106" y="70.0431" width="60" height="60">
<ellipse/>
</object>
<object id="7" x="559.894" y="406.747" rotation="45">
<polygon points="0,0 92.931,0 45.9434,75.7022 -60.0397,46.9876 -27.1484,14.0963"/>
</object>
</objectgroup>
</map>
4 changes: 2 additions & 2 deletions src/j2d.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1182,12 +1182,12 @@ pub const ConvexPoly = struct {

pub fn point(self: *ConvexPoly, p: jok.Vertex) !void {
assert(!self.finished);
try self.cmd.points.append(p);
try self.points.append(p);
}

pub fn npoints(self: *ConvexPoly, ps: []jok.Vertex) !void {
assert(!self.finished);
try self.cmd.points.appendSlice(ps);
try self.points.appendSlice(ps);
}
};

Expand Down
181 changes: 176 additions & 5 deletions src/utils/tiled.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const Error = error{
UnsupportedLayerEncoding,
UnsupportedLayerCompression,
UnsupportedPropertyType,
UnsupportedObjectType,
};

pub const Orientation = enum {
Expand Down Expand Up @@ -291,20 +292,84 @@ const TileLayer = struct {
}
};

const Object = struct {
layer: *const ObjectGroup,
geom: union(enum) {
rect: jok.Rectangle,
circle: jok.Circle,
point: jok.Point,
polygon: j2d.ConvexPoly,
polyline: j2d.Polyline,
},
rotate_degree: f32,
gid: ?GlobalTileID,
visible: bool,
props: PropertyTree,
};

const ObjectGroup = struct {
map: *const TiledMap,
offset: jok.Point,
parallax: jok.Point,
tint_color: jok.Color,
objects: []Object,
visible: bool,
props: PropertyTree,

pub fn render(self: ObjectGroup, b: *j2d.Batch) !void {
if (!self.visible) return;
assert(self.map.orientation == .orthogonal);
_ = b;
// TODO
return error.UnsupportedLayerType;
for (self.objects) |o| {
if (!o.visible) continue;
switch (o.geom) {
.rect => |r| {
if (o.gid) |gid| {
const tile = self.map.getTile(gid);
var sp = tile.getSprite();
sp.width = r.width;
sp.height = r.height;
try b.sprite(sp, .{
.pos = .{ .x = r.x, .y = r.y },
.rotate_degree = o.rotate_degree,
.anchor_point = .{ .x = 0, .y = 1.0 },
});
} else {
try b.pushTransform(j2d.AffineTransform.init().rotateByPoint(
.{ .x = r.x, .y = r.y },
std.math.degreesToRadians(o.rotate_degree),
));
defer b.popTransform();
try b.rectFilled(r, self.tint_color, .{});
}
},
.circle => |c| {
try b.circleFilled(c, self.tint_color, .{});
},
.point => |p| {
try b.rectFilled(
.{ .x = p.x - 5, .y = p.y - 5, .width = 10, .height = 10 },
self.tint_color,
.{},
);
},
.polygon => |poly| {
try b.pushTransform(j2d.AffineTransform.init().rotateByPoint(
poly.points.items[0].pos,
std.math.degreesToRadians(o.rotate_degree),
));
defer b.popTransform();
try b.convexPolyFilled(poly, .{});
},
.polyline => |poly| {
try b.pushTransform(j2d.AffineTransform.init().rotateByPoint(
poly.points.items[0],
std.math.degreesToRadians(o.rotate_degree),
));
defer b.popTransform();
try b.polyline(poly, self.tint_color, .{ .depth = 2 });
},
}
}
}
};

Expand Down Expand Up @@ -966,9 +1031,115 @@ fn loadLayers(
.props = props,
};
} else if (std.mem.eql(u8, e.tag, "objectgroup")) {
// TODO
layer.* = .{ .object_layer = undefined };
return error.UnsupportedLayerType;

var objects = try std.ArrayList(Object).initCapacity(arena_allocator, 20);
var it = e.findChildrenByTag("object");
while (it.next()) |oe| {
var o: Object = .{
.layer = &layer.object_layer,
.geom = undefined,
.rotate_degree = 0,
.gid = null,
.visible = true,
.props = PropertyTree.init(arena_allocator),
};

var x: f32 = 0;
var y: f32 = 0;
var width: f32 = 0;
var height: f32 = 0;

for (oe.attributes) |a| {
if (std.mem.eql(u8, a.name, "x")) {
x = try std.fmt.parseFloat(f32, a.value);
continue;
}
if (std.mem.eql(u8, a.name, "y")) {
y = try std.fmt.parseFloat(f32, a.value);
continue;
}
if (std.mem.eql(u8, a.name, "width")) {
width = try std.fmt.parseFloat(f32, a.value);
continue;
}
if (std.mem.eql(u8, a.name, "height")) {
height = try std.fmt.parseFloat(f32, a.value);
continue;
}
if (std.mem.eql(u8, a.name, "rotation")) {
o.rotate_degree = try std.fmt.parseFloat(f32, a.value);
continue;
}
if (std.mem.eql(u8, a.name, "gid")) {
o.gid = .{
._id = try std.fmt.parseInt(u32, a.value, 0),
};
continue;
}
if (std.mem.eql(u8, a.name, "visible")) {
o.visible = if (try std.fmt.parseInt(u8, a.value, 0) == 1) true else false;
continue;
}
}

if (oe.findChildByTag("ellipse") != null) {
assert(width == height and width > 0);
o.geom = .{
.circle = .{
.center = .{
.x = x + width / 2,
.y = y + width / 2,
},
.radius = width / 2,
},
};
} else if (oe.findChildByTag("point") != null) {
o.geom = .{
.point = .{ .x = x, .y = y },
};
} else if (oe.findChildByTag("polygon")) |pe| {
assert(pe.attributes.len == 1);
assert(std.mem.eql(u8, pe.attributes[0].name, "points"));
var convex = j2d.ConvexPoly.begin(arena_allocator, null);
var point_it = std.mem.splitScalar(u8, pe.attributes[0].value, ' ');
while (point_it.next()) |xs| {
const idx = std.mem.indexOfScalar(u8, xs, ',').?;
try convex.point(.{
.pos = .{
.x = x + try std.fmt.parseFloat(f32, xs[0..idx]),
.y = y + try std.fmt.parseFloat(f32, xs[idx + 1 ..]),
},
.color = tint_color,
});
}
convex.end();
o.geom = .{ .polygon = convex };
} else if (oe.findChildByTag("polyline") != null) {
return error.UnsupportedObjectType;
} else {
o.geom = .{
.rect = .{
.x = x,
.y = y,
.width = width,
.height = height,
},
};
}

try initPropertyTree(oe, arena_allocator, &o.props);
try objects.append(o);
}
layer.object_layer = .{
.map = tmap,
.offset = offset,
.parallax = parallax,
.tint_color = tint_color,
.objects = try objects.toOwnedSlice(),
.visible = visible,
.props = props,
};
} else if (std.mem.eql(u8, e.tag, "imagelayer")) {
// TODO
layer.* = .{ .image_layer = undefined };
Expand Down

0 comments on commit 8607493

Please sign in to comment.