Skip to content

Commit f8a7a9f

Browse files
authored
Merge pull request #54 from Interrupt/fixes/quakemdl
Getting the QuakeMdl and QuakeMap examples to run cleanly
2 parents 8e1ae9b + 38b2ccc commit f8a7a9f

File tree

3 files changed

+65
-27
lines changed

3 files changed

+65
-27
lines changed

src/examples/quakemap.zig

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const graphics = delve.platform.graphics;
88
const math = delve.math;
99

1010
var camera: delve.graphics.camera.Camera = undefined;
11+
var shader: delve.platform.graphics.Shader = undefined;
1112
var fallback_material: graphics.Material = undefined;
1213
var fallback_quake_material: delve.utils.quakemap.QuakeMaterial = undefined;
1314
var materials: std.StringHashMap(delve.utils.quakemap.QuakeMaterial) = undefined;
@@ -33,6 +34,7 @@ pub fn main() !void {
3334
.init_fn = on_init,
3435
.tick_fn = on_tick,
3536
.draw_fn = on_draw,
37+
.cleanup_fn = on_cleanup,
3638
};
3739

3840
// Pick the allocator to use depending on platform
@@ -42,7 +44,8 @@ pub fn main() !void {
4244
// See https://github.com/ziglang/zig/issues/19072
4345
try delve.init(std.heap.c_allocator);
4446
} else {
45-
try delve.init(gpa.allocator());
47+
// Using the default allocator will let us detect memory leaks
48+
try delve.init(delve.mem.createDefaultAllocator());
4649
}
4750

4851
try delve.modules.registerModule(example);
@@ -146,9 +149,11 @@ pub fn on_init() !void {
146149
var err: delve.utils.quakemap.ErrorInfo = undefined;
147150
quake_map = try delve.utils.quakemap.QuakeMap.read(allocator, test_map_file, map_transform, &err);
148151

152+
shader = graphics.Shader.initDefault(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() });
153+
149154
// Create a material out of the texture
150155
fallback_material = try graphics.Material.init(.{
151-
.shader = graphics.Shader.initDefault(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }),
156+
.shader = shader,
152157
.texture_0 = fallback_tex,
153158
.samplers = &[_]graphics.FilterMode{.NEAREST},
154159
});
@@ -163,7 +168,6 @@ pub fn on_init() !void {
163168
player_pos = camera.position;
164169

165170
materials = std.StringHashMap(delve.utils.quakemap.QuakeMaterial).init(allocator);
166-
const shader = graphics.Shader.initDefault(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() });
167171

168172
for (quake_map.worldspawn.solids.items) |solid| {
169173
for (solid.faces.items) |face| {
@@ -324,3 +328,12 @@ pub fn setGravityCmd(new_gravity: f32) void {
324328
gravity = new_gravity;
325329
delve.debug.log("Changed gravity to {d}", .{gravity});
326330
}
331+
332+
pub fn on_cleanup() !void {
333+
var it = materials.valueIterator();
334+
while (it.next()) |mat_ptr| {
335+
mat_ptr.material.deinit();
336+
}
337+
materials.deinit();
338+
shader.destroy();
339+
}

src/examples/quakemdl.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ pub fn main() !void {
2121
// See https://github.com/ziglang/zig/issues/19072
2222
try delve.init(std.heap.c_allocator);
2323
} else {
24-
try delve.init(gpa.allocator());
24+
// Using the default allocator will let us detect memory leaks
25+
try delve.init(delve.mem.createDefaultAllocator());
2526
}
2627

2728
const example = delve.modules.Module{

src/framework/utils/quakemdl.zig

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,33 @@ const graphics = @import("../platform/graphics.zig");
1111

1212
const assert = std.debug.assert;
1313
const Allocator = std.mem.Allocator;
14+
const ArenaAllocator = std.heap.ArenaAllocator;
1415
const File = std.fs.File;
1516

1617
pub const MDL = struct {
1718
frames: []MDLFrameType,
1819
skins: []MDLSkinType,
19-
material: *graphics.Material,
20-
allocator: Allocator,
20+
material: graphics.Material,
21+
arena_allocator: ArenaAllocator,
2122

2223
pub fn deinit(self: *MDL) void {
23-
self.allocator.free(self.frames);
24-
self.allocator.free(self.skins);
25-
self.allocator.destroy(self.material);
24+
for (self.skins) |skin| {
25+
switch (skin) {
26+
.single => |*frame| {
27+
// self.allocator.free(frame.pixels);
28+
@constCast(&frame.texture).destroy();
29+
},
30+
.group => |group| {
31+
for (group.textures) |tex| {
32+
@constCast(&tex).destroy();
33+
}
34+
// self.allocator.free(group.frames);
35+
},
36+
}
37+
}
38+
39+
self.arena_allocator.deinit();
40+
self.material.deinit();
2641
}
2742
};
2843

@@ -97,7 +112,7 @@ const MDLMeshBuildConfig_ = struct {
97112
skin_width: f32,
98113
skin_height: f32,
99114
transform: math.Mat4,
100-
material: *graphics.Material,
115+
material: graphics.Material,
101116
};
102117

103118
const MDLSkin_ = struct {
@@ -130,6 +145,7 @@ const MDLSkin_ = struct {
130145
pub fn toTexture(self: *const MDLSkin_, allocator: Allocator) !graphics.Texture {
131146
const size: u32 = self.width * self.height;
132147
const bytes = try allocator.alloc(u8, size * 4);
148+
defer allocator.free(bytes);
133149

134150
for (0.., self.pixels) |j, index| {
135151
const i = @as(u32, index);
@@ -150,6 +166,7 @@ const MDLSkinGroup_ = struct {
150166
count: u32,
151167
intervals: []f32,
152168
pixels: []u8,
169+
allocator: Allocator,
153170

154171
pub fn read(allocator: Allocator, file: File, width: u32, height: u32) !MDLSkinGroup_ {
155172
// Skin type
@@ -165,7 +182,7 @@ const MDLSkinGroup_ = struct {
165182
// Skin intervals
166183
const intervals_buff = try allocator.alloc(u8, count * @sizeOf(f32));
167184
_ = try file.read(intervals_buff);
168-
const intervals: []f32 = try bytesToStructArray(f32, intervals_buff);
185+
const intervals: []f32 = try bytesToStructArray(f32, allocator, intervals_buff);
169186

170187
// Skin pixels
171188
const size: u32 = width * height * count;
@@ -179,12 +196,19 @@ const MDLSkinGroup_ = struct {
179196
.count = count,
180197
.intervals = intervals,
181198
.pixels = pixels,
199+
.allocator = allocator,
182200
};
183201
}
184202

203+
pub fn deinit(self: *MDLSkinGroup_) void {
204+
self.allocator.free(self.intervals);
205+
self.allocator.free(self.pixels);
206+
}
207+
185208
pub fn toTexture(self: *const MDLSkinGroup_, allocator: Allocator, index: u32) !graphics.Texture {
186209
const size: u32 = self.width * self.height;
187210
const bytes = try allocator.alloc(u8, size * 4);
211+
defer allocator.free(bytes);
188212

189213
const start = index * size;
190214
const end = start + size;
@@ -225,7 +249,7 @@ const MDLFrame_ = struct {
225249
const vertbuff = try allocator.alloc(u8, @sizeOf(TriVertex_) * vertex_count);
226250
defer allocator.free(vertbuff);
227251
_ = try file.read(vertbuff);
228-
const trivertexes = try bytesToStructArray(TriVertex_, vertbuff);
252+
const trivertexes = try bytesToStructArray(TriVertex_, allocator, vertbuff);
229253

230254
return .{
231255
.min = min,
@@ -258,7 +282,7 @@ const MDLFrameGroup_ = struct {
258282
// Frame intervals
259283
const intervals_buff = try allocator.alloc(u8, count * @sizeOf(f32));
260284
_ = try file.read(intervals_buff);
261-
const intervals: []f32 = try bytesToStructArray(f32, intervals_buff);
285+
const intervals: []f32 = try bytesToStructArray(f32, allocator, intervals_buff);
262286

263287
// Frames
264288
const frames: []MDLFrame_ = try allocator.alloc(MDLFrame_, count);
@@ -307,8 +331,7 @@ const TriVertex_ = struct {
307331
}
308332
};
309333

310-
fn bytesToStructArray(comptime T: type, bytes: []u8) std.mem.Allocator.Error![]T {
311-
const allocator = mem.getAllocator();
334+
fn bytesToStructArray(comptime T: type, allocator: Allocator, bytes: []u8) std.mem.Allocator.Error![]T {
312335
const size: u32 = @sizeOf(T);
313336
const length: u32 = @as(u32, @intCast(bytes.len)) / size;
314337
const result: []T = try allocator.alloc(T, length);
@@ -392,7 +415,10 @@ fn makeMesh(allocator: Allocator, frame: MDLFrame_, config: MDLMeshBuildConfig_)
392415
return builder.buildMesh(config.material);
393416
}
394417

395-
pub fn open(allocator: Allocator, path: []const u8) !MDL {
418+
pub fn open(in_allocator: Allocator, path: []const u8) !MDL {
419+
var arena = ArenaAllocator.init(in_allocator);
420+
var allocator = arena.allocator();
421+
396422
var file = try std.fs.cwd().openFile(
397423
path,
398424
std.fs.File.OpenFlags{
@@ -416,9 +442,10 @@ pub fn open(allocator: Allocator, path: []const u8) !MDL {
416442
const skin_type: SkinType = @enumFromInt(@as(u32, @bitCast(work)));
417443

418444
if (skin_type == SkinType.SINGLE) {
419-
const skin = try MDLSkin_.read(allocator, file, header.skin_width, header.skin_height);
445+
var skin = try MDLSkin_.read(allocator, file, header.skin_width, header.skin_height);
420446
const texture = try skin.toTexture(allocator);
421447
skins[i] = .{ .single = .{ .texture = texture } };
448+
defer allocator.free(skin.pixels);
422449
} else if (skin_type == SkinType.GROUP) {
423450
const group = try MDLSkinGroup_.read(allocator, file, header.skin_width, header.skin_height);
424451

@@ -437,8 +464,9 @@ pub fn open(allocator: Allocator, path: []const u8) !MDL {
437464
}
438465

439466
// Material
440-
const default_material = graphics.Material.init(.{
467+
const default_material = try graphics.Material.init(.{
441468
.shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = mesh.getShaderAttributes() }, default_mesh),
469+
.own_shader = true,
442470
.texture_0 = skins[0].single.texture,
443471
.samplers = &[_]graphics.FilterMode{.NEAREST},
444472
});
@@ -447,20 +475,16 @@ pub fn open(allocator: Allocator, path: []const u8) !MDL {
447475
const stvert_buff: []u8 = try allocator.alloc(u8, @sizeOf(STVertex_) * header.vertex_count);
448476
defer allocator.free(stvert_buff);
449477
_ = try file.read(stvert_buff);
450-
const stvertices = try bytesToStructArray(STVertex_, stvert_buff);
478+
const stvertices = try bytesToStructArray(STVertex_, allocator, stvert_buff);
451479
defer allocator.free(stvertices);
452480

453481
// Triangles
454482
const triangle_buff: []u8 = try allocator.alloc(u8, @sizeOf(Triangle_) * header.triangle_count);
455483
defer allocator.free(triangle_buff);
456484
_ = try file.read(triangle_buff);
457-
const triangles = try bytesToStructArray(Triangle_, triangle_buff);
485+
const triangles = try bytesToStructArray(Triangle_, allocator, triangle_buff);
458486
defer allocator.free(triangles);
459487

460-
var material = try allocator.create(graphics.Material);
461-
material.* = default_material;
462-
material.textures[0] = skins[0].single.texture;
463-
464488
// Transform
465489
var m = math.Mat4.identity;
466490
m = m.mul(math.Mat4.translate(math.vec3(header.origin[0], header.origin[2], header.origin[1])));
@@ -478,7 +502,7 @@ pub fn open(allocator: Allocator, path: []const u8) !MDL {
478502
.stvertexes = stvertices,
479503
.triangles = triangles,
480504
.transform = m,
481-
.material = material,
505+
.material = default_material,
482506
};
483507

484508
// Frames
@@ -521,8 +545,8 @@ pub fn open(allocator: Allocator, path: []const u8) !MDL {
521545
const mdl: MDL = .{
522546
.frames = frames,
523547
.skins = skins,
524-
.material = material,
525-
.allocator = allocator,
548+
.material = default_material,
549+
.arena_allocator = arena,
526550
};
527551

528552
return mdl;

0 commit comments

Comments
 (0)