Skip to content

Instantly share code, notes, and snippets.

@ConnorRigby
Created July 3, 2023 23:12
Show Gist options
  • Save ConnorRigby/baf48efbfc75d23e75a6946b38c19158 to your computer and use it in GitHub Desktop.
Save ConnorRigby/baf48efbfc75d23e75a6946b38c19158 to your computer and use it in GitHub Desktop.
const std = @import("std");
const log = std.log.scoped(.Main);
const glfw = @import("glfw");
const zgl = @import("zgl");
const zlm = @import("zlm");
const Mat4 = zlm.Mat4;
const Shader = @import("Shader.zig");
const Texture = @import("Texture.zig");
const c = @cImport({
@cInclude("tmx.h");
});
export fn _tmx_load(path: [*c]const u8) callconv(.C) ?*anyopaque {
var texture = std.heap.c_allocator.create(Texture) catch @panic("out of memory");
var path_len = std.mem.span(path);
texture.* = Texture.init(path_len) catch @panic("texture load failure");
std.log.warn("_tmx_load({?s}) = {any}", .{ path, texture });
return @ptrCast(texture);
}
export fn _tmx_free(data: ?*anyopaque) callconv(.C) void {
var texture: *Texture = @ptrCast(@alignCast(data));
defer std.heap.c_allocator.destroy(texture);
}
fn draw_objects(shader: *Shader, objects: ?*c.tmx_object_group) void {
_ = objects;
_ = shader;
// std.log.warn("draw_objects({?*})", .{objects});
}
fn draw_image_layer(shader: *Shader, image: ?*c.tmx_image) void {
_ = shader;
if (image) |i| {
var texture: *Texture = @ptrCast(@alignCast(i.resource_image));
texture.bind();
std.log.warn("draw_image_layer({*})", .{texture});
} else unreachable;
}
fn draw_layer(shader: *Shader, map: *c.tmx_map, layer: *c.tmx_layer) void {
var i: usize = 0;
var j: usize = 0;
var op: f64 = undefined;
var gid: c_uint = undefined;
var x: c_uint = undefined;
var y: c_uint = undefined;
var w: c_uint = undefined;
var h: c_uint = undefined;
var flags: c_uint = 0;
var ts: *c.tmx_tileset = undefined;
var im: ?*c.tmx_image = undefined;
var image: ?*anyopaque = undefined;
op = layer.opacity;
while (i < map.height) : (i += 1) {
while (j < map.width) : (j += 1) {
gid = (layer.content.gids[(i * map.width) + j]) & c.TMX_FLIP_BITS_REMOVAL;
if (map.tiles[gid]) |g| {
ts = g[0].tileset;
im = g[0].image;
x = g[0].ul_x;
y = g[0].ul_y;
w = ts.tile_width;
h = ts.tile_height;
if (im) |im_| {
image = im_.resource_image;
} else {
image = ts.image[0].resource_image;
}
// flags = (layer.content.gids[(i * map.width) + j]) & ~c.TMX_FLIP_BITS_REMOVAL;
draw_tile(
shader,
image,
x,
y,
w,
h,
@intCast(j * ts.tile_width),
@intCast(i * ts.tile_height),
op,
flags,
);
}
}
}
}
fn draw_tile(shader: *Shader, image: ?*anyopaque, sx: c_uint, sy: c_uint, sw: c_uint, sh: c_uint, dx: c_uint, dy: c_uint, opacity: f64, flags: c_uint) void {
_ = flags;
_ = opacity;
var texture: *Texture = @ptrCast(@alignCast(image));
shader.setUniform1i("image", 0);
// shader.setUniform1i("image", @intFromEnum(texture.id));
zgl.activeTexture(.texture_0);
texture.bind();
// std.log.info("draw_tile({any}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{ texture, sx, sy, sw, sh, dx, dy, opacity, flags });
var position = zlm.Vec2{ .x = @floatFromInt(dx), .y = @floatFromInt(dy) };
var size = zlm.Vec2{ .x = @floatFromInt(sw), .y = @floatFromInt(sh) };
var rotation: f32 = 0;
var model = Mat4.identity;
model = Mat4.mul(model, Mat4.createTranslationXYZ(position.x, position.y, 0));
model = Mat4.mul(model, Mat4.createTranslationXYZ(0.5 * size.x, 0.5 * size.y, 0.0));
model = Mat4.mul(model, Mat4.createAngleAxis(.{ .x = 0, .y = 0, .z = 1.0 }, zlm.toRadians(rotation)));
model = Mat4.mul(model, Mat4.createTranslationXYZ(-0.5 * size.x, -0.5 * size.y, 0.0));
model = Mat4.mul(model, Mat4.createScale(size.x, size.y, 1.0));
shader.setMat4("model", model);
const vertices = [_]f32{
0.0, 1.0,
1.0, 0.0,
0.0, 0.0,
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,
};
var indices = [_]u32{
0, 1, 2, // Triangle 1 (Bottom-left to bottom-right to top-left)
2, 1, 3, // Triangle 2 (Top-left to bottom-right to top-right)
};
const texCoordLeft = @as(f32, @floatFromInt(sx));
const texCoordRight = @as(f32, @floatFromInt(sx + sw));
const texCoordTop = @as(f32, @floatFromInt(sy));
const texCoordBottom = @as(f32, @floatFromInt(sy + sh));
const texcoords = [_]f32{ texCoordLeft, texCoordTop, texCoordRight, texCoordTop, texCoordLeft, texCoordBottom, texCoordRight, texCoordBottom };
var vao = zgl.genVertexArray();
// defer zgl.deleteVertexArray(vao);
var vbo = zgl.genBuffer();
// defer zgl.deleteBuffer(vbo);
var texvbo = zgl.genBuffer();
// defer zgl.deleteBuffer(texvbo);
var ebo = zgl.genBuffer();
// defer zgl.deleteBuffer(ebo);
zgl.bindVertexArray(vao);
zgl.bindBuffer(vbo, .array_buffer);
zgl.bufferData(.array_buffer, f32, &vertices, .static_draw);
zgl.bindBuffer(texvbo, .array_buffer);
zgl.bufferData(.array_buffer, f32, &texcoords, .static_draw);
zgl.vertexAttribPointer(0, 2, .float, false, 2 * @sizeOf(f32), 0);
zgl.enableVertexAttribArray(0);
zgl.vertexAttribPointer(1, 2, .float, false, 2 * @sizeOf(f32), 0);
zgl.enableVertexAttribArray(1);
zgl.bindBuffer(ebo, .element_array_buffer);
zgl.bufferData(.element_array_buffer, u32, &indices, .static_draw);
zgl.bindVertexArray(vao);
zgl.drawElements(.triangles, 6, .u32, 0);
// zgl.drawArrays(.triangles, 0, vertices.len);
}
fn draw_all_layers(shader: *Shader, map: *c.tmx_map, layers: ?*c.tmx_layer) void {
zgl.clearColor(0.2, 0.3, 0.3, 1.0);
zgl.clear(.{ .color = true, .depth = true });
zgl.enable(.depth_test);
var layers_for_looping = layers;
while (layers_for_looping) |layer| {
if (layer.visible > 0) {
if (layer.type == c.L_GROUP) {
draw_all_layers(shader, map, layer.content.group_head);
} else if (layer.type == c.L_OBJGR) {
draw_objects(shader, layer.content.objgr);
} else if (layer.type == c.L_IMAGE) {
draw_image_layer(shader, layer.content.image);
} else if (layer.type == c.L_LAYER) {
draw_layer(shader, map, layer);
}
}
layers_for_looping = @ptrCast(layer.next);
}
zgl.disable(.depth_test);
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var allocator = gpa.allocator();
defer {
const deinit_status = gpa.deinit();
//fail test; can't try in defer as defer is executed after we return
if (deinit_status == .leak) @panic("leak detected");
}
glfw.setErrorCallback(errorCallback);
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate();
const window = glfw.Window.create(640, 480, "hello, world", null, null, .{
.context_version_major = 4,
.context_version_minor = 0,
.opengl_profile = .opengl_core_profile,
}) orelse {
std.log.err("failed to create GLFW window: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
};
defer window.destroy();
glfw.makeContextCurrent(window);
const proc: glfw.GLProc = undefined;
try zgl.loadExtensions(proc, glGetProcAddress);
glfw.swapInterval(1);
var shader = try Shader.init(
@embedFile("shaders/test.vs"),
@embedFile("shaders/test.fs"),
allocator,
);
defer shader.deinit();
shader.use();
zgl.activeTexture(.texture_0);
var projection = Mat4.identity;
projection = Mat4.createOrthogonal(0, 640, 0, 480, -1, 1);
shader.setMat4("projection", projection);
c.tmx_img_load_func = _tmx_load;
c.tmx_img_free_func = _tmx_free;
var map = c.tmx_load("/home/connorrigby/Downloads/tiled-master/examples/rpg/island.tmx");
defer c.tmx_map_free(map);
while (!window.shouldClose()) {
if (map) |map_root| {
var col = c.tmx_col_to_floats(map_root[0].backgroundcolor);
shader.setVec3("spriteColor", .{ .x = col.r, .y = col.g, .z = col.b });
draw_all_layers(&shader, @ptrCast(map_root), @ptrCast(map_root[0].ly_head));
} else unreachable;
window.swapBuffers();
glfw.pollEvents();
}
}
/// Default GLFW error handling callback
fn errorCallback(error_code: glfw.ErrorCode, description: [:0]const u8) void {
log.err("glfw: {}: {s}\n", .{ error_code, description });
}
fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?*const anyopaque {
_ = p;
return glfw.getProcAddress(proc);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment