// ZiRC, a Zig raycaster for fun // // Copyright (C) 2021, tslil clingman // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . const sf = struct { usingnamespace @import("sfml"); usingnamespace sf.graphics; usingnamespace sf.window; }; const isKeyPressed = sf.window.keyboard.isKeyPressed; const std = @import("std"); const constants = @import("constants.zig"); const raycast = @import("player.zig"); const level = @import("level.zig"); pub fn main() !void { const PI = std.math.pi; // Setup player and map //-------------------------------------------------------------------------- var player = try raycast.Player(constants.PlaneWidth, constants.PlaneHeight).new( 7, // x coord 1.5, // y coord PI / 2.0, // angle ); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); var map = try level.Map.new(16, 16, &gpa.allocator); defer map.deinit(); var y: u32 = 0; while (y < 16) : (y += 1) { map.cells.items[y * 16 + 00] = level.Cell{ .height = constants.MAX_HEIGHT }; map.cells.items[y * 16 + 15] = level.Cell{ .height = constants.MAX_HEIGHT, .wall_texture = 2 }; map.cells.items[15 * 16 + y] = level.Cell{ .height = constants.MAX_HEIGHT }; map.cells.items[00 * 16 + y] = level.Cell{ .height = constants.MAX_HEIGHT }; } map.cells.items[16 * 7 + 7] = level.Cell{ .height = constants.MAX_HEIGHT, .wall_texture = 1 }; map.cells.items[16 * 7 + 6] = level.Cell{ .height = constants.MAX_HEIGHT / 4, .wall_texture = 1, .floor_texture = 1 }; try map.objects.append(level.Object{ .pos_x = 5.5, .pos_y = 7.5, .pos_z = 1, .texture = 2, .height = 1, .width = 1 }); try map.objects.append(level.Object{ .pos_x = 4, .pos_y = 8.5, .texture = 1, .height = 1, .width = 1 }); try map.objects.append(level.Object{ .pos_x = 6.5, .pos_y = 8.5, .texture = 0, .height = 1, .width = 1 }); // Initialise SFML //-------------------------------------------------------------------------- var window = try sf.RenderWindow.create(.{ .x = constants.ScreenWidth, .y = constants.ScreenHeight }, 32, "zirc", sf.window.Style.none); defer window.destroy(); window.setFramerateLimit(60); // Load the background skybox, really `skycylinder', and assign it to a // sprite -- it's set to repeating so that it goes on forever. We need to // scale the sprite so that drawing 1/4 of backTextureWidth fills the Screen // horizontally, and drawing backTextureHeight fills half of the Screen // vertically. var back_texture = try sf.Texture.createFromFile("back.png"); defer back_texture.destroy(); back_texture.setSmooth(false); // looks worse => better! back_texture.setRepeated(true); var back_sprite = try sf.Sprite.createFromTexture(back_texture); defer back_sprite.destroy(); back_sprite.setScale(.{ .x = 4.0 * constants.ScreenWidth / constants.BackTextureWidth, .y = constants.ScreenHeight / constants.BackTextureHeight }); // Load wall textures var wall_textures = try sf.Texture.createFromFile("walls.png"); defer wall_textures.destroy(); wall_textures.setRepeated(true); wall_textures.setSmooth(false); // looks worse => better! var walls_sprite = try sf.Sprite.createFromTexture(wall_textures); defer walls_sprite.destroy(); // Load sprite textures var object_textures = try sf.Texture.createFromFile("objects.png"); defer object_textures.destroy(); object_textures.setSmooth(false); var objects_sprite = try sf.Sprite.createFromTexture(object_textures); defer objects_sprite.destroy(); // Load floor image var surfaces_image = try sf.Image.createFromFile("surfaces.png"); defer surfaces_image.destroy(); var rendered_surfaces_texture = try sf.Texture.create(.{ .x = constants.PlaneWidth, .y = constants.PlaneHeight }); defer rendered_surfaces_texture.destroy(); rendered_surfaces_texture.setSmooth(false); var rendered_surfaces_sprite = try sf.Sprite.createFromTexture(rendered_surfaces_texture); defer rendered_surfaces_sprite.destroy(); rendered_surfaces_sprite.setPosition(.{ .x = 0, .y = 0 }); rendered_surfaces_sprite.setScale(.{ .x = constants.HFact, .y = constants.VFact }); // Start your engines! //-------------------------------------------------------------------------- while (window.isOpen()) { while (window.pollEvent()) |event| { if (event == .closed) window.close(); } if (isKeyPressed(.Escape)) window.close(); if (isKeyPressed(.C)) player.height -= 0.1; if (isKeyPressed(.V)) player.height += 0.1; const rotation = 2.0 * PI / 100.0; if (isKeyPressed(.R)) player.ang += rotation; if (isKeyPressed(.T)) player.ang -= rotation; const cos = std.math.cos(player.ang); const sin = std.math.sin(player.ang); player.acc_x = 0; player.acc_y = 0; const acc_mag = 30; if (isKeyPressed(.Right)) { player.acc_x += sin; player.acc_y -= cos; } if (isKeyPressed(.Left)) { player.acc_x -= sin; player.acc_y += cos; } if (isKeyPressed(.Up) or isKeyPressed(.F)) { player.acc_x += cos; player.acc_y += sin; } if (isKeyPressed(.Down) or isKeyPressed(.S)) { player.acc_x -= cos; player.acc_y -= sin; } // normalise const a = std.math.sqrt(player.acc_x * player.acc_x + player.acc_y * player.acc_y); if (a > 0) { player.acc_x *= acc_mag / a; player.acc_y *= acc_mag / a; } // First the background const back_scroll = @floatToInt(i32, -constants.BackTextureWidth * player.ang / (2 * PI)); back_sprite.setTextureRect(sf.IntRect{ .left = back_scroll, // fix location of texture in the sky .top = 0, // scaling ensures that the next two dimensions paint it across the // width of the Screen, and for half the height of the screen .width = constants.BackTextureWidth / 4.0, // 1/4 of texture per screen .height = constants.BackTextureHeight, // full height of texture visible }); window.draw(back_sprite, null); try player.renderWorld( window, walls_sprite, objects_sprite, surfaces_image, rendered_surfaces_texture, rendered_surfaces_sprite, map, ); window.display(); player.tick(map); } }