├── .gitattributes ├── res ├── arial.ttf ├── funnysong.mp3 ├── readme.md ├── cute_image.png ├── distortion_map.png └── heat_shader.fs ├── .gitignore ├── src ├── audio │ ├── sound_status.zig │ ├── audio_module.zig │ ├── sound_buffer.zig │ ├── Music.zig │ └── Sound.zig ├── window │ ├── Style.zig │ ├── clipboard.zig │ ├── window_module.zig │ ├── context_settings.zig │ ├── mouse.zig │ ├── keyboard.zig │ ├── Joystick.zig │ ├── Cursor.zig │ └── event.zig ├── network │ ├── network_module.zig │ ├── Socket.zig │ ├── TcpListener.zig │ ├── SocketSelector.zig │ ├── IpAddress.zig │ ├── TcpSocket.zig │ ├── UdpSocket.zig │ └── Packet.zig ├── graphics │ ├── glsl.zig │ ├── RenderStates.zig │ ├── render_target.zig │ ├── vertex.zig │ ├── graphics_module.zig │ ├── primitive_type.zig │ ├── transform.zig │ ├── blend_mode.zig │ ├── VertexBuffer.zig │ ├── Font.zig │ ├── View.zig │ ├── Shader.zig │ ├── VertexArray.zig │ ├── Image.zig │ ├── Sprite.zig │ ├── color.zig │ ├── RenderTexture.zig │ ├── rect.zig │ ├── RectangleShape.zig │ ├── ConvexShape.zig │ ├── CircleShape.zig │ ├── RenderWindow.zig │ ├── Text.zig │ └── texture.zig ├── system │ ├── system_module.zig │ ├── TimeSpan.zig │ ├── Buffer.zig │ ├── Clock.zig │ ├── Time.zig │ └── vector.zig ├── examples │ ├── green_circle.zig │ ├── sfml_example.zig │ └── heat_haze.zig ├── root.zig └── unicode.zig ├── LICENCE ├── CSFML-LICENCE └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text eol=lf -------------------------------------------------------------------------------- /res/arial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Guigui220D/zig-sfml-wrapper/HEAD/res/arial.ttf -------------------------------------------------------------------------------- /res/funnysong.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Guigui220D/zig-sfml-wrapper/HEAD/res/funnysong.mp3 -------------------------------------------------------------------------------- /res/readme.md: -------------------------------------------------------------------------------- 1 | # Ressources for the examples 2 | 3 | These are the ressources used in the examples -------------------------------------------------------------------------------- /res/cute_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Guigui220D/zig-sfml-wrapper/HEAD/res/cute_image.png -------------------------------------------------------------------------------- /res/distortion_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Guigui220D/zig-sfml-wrapper/HEAD/res/distortion_map.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | csfml/ 2 | zig-cache/ 3 | .zig-cache/ 4 | zig-out/ 5 | docs/ 6 | 7 | NUL 8 | *.dll 9 | test.wav 10 | -------------------------------------------------------------------------------- /src/audio/sound_status.zig: -------------------------------------------------------------------------------- 1 | /// Represents the status of a sound playing 2 | pub const SoundStatus = enum(c_int) { 3 | stopped, 4 | paused, 5 | playing, 6 | }; 7 | -------------------------------------------------------------------------------- /src/window/Style.zig: -------------------------------------------------------------------------------- 1 | pub const none: u32 = 0; 2 | pub const titlebar: u32 = 1; 3 | pub const resize: u32 = 2; 4 | pub const close: u32 = 4; 5 | pub const fullscreen: u32 = 8; 6 | pub const defaultStyle = titlebar | resize | close; 7 | -------------------------------------------------------------------------------- /src/audio/audio_module.zig: -------------------------------------------------------------------------------- 1 | pub const Music = @import("Music.zig"); 2 | pub const SoundBuffer = @import("sound_buffer.zig").SoundBuffer; 3 | pub const Sound = @import("Sound.zig"); 4 | pub const SoundStatus = @import("sound_status.zig").SoundStatus; 5 | -------------------------------------------------------------------------------- /src/network/network_module.zig: -------------------------------------------------------------------------------- 1 | pub const Packet = @import("Packet.zig"); 2 | pub const IpAddress = @import("IpAddress.zig"); 3 | pub const IpAndPort = struct { ip: IpAddress, port: u16 }; 4 | pub const ip = IpAddress.init; 5 | 6 | pub const Socket = @import("Socket.zig"); 7 | pub const UdpSocket = @import("UdpSocket.zig"); 8 | pub const TcpSocket = @import("TcpSocket.zig"); 9 | pub const TcpListener = @import("TcpListener.zig"); 10 | pub const SocketSelector = @import("SocketSelector.zig"); 11 | -------------------------------------------------------------------------------- /src/graphics/glsl.zig: -------------------------------------------------------------------------------- 1 | const sf = @import("../root.zig").system; 2 | 3 | pub const FVec2 = sf.Vector2(f32); 4 | pub const FVec3 = sf.Vector3(f32); 5 | pub const FVec4 = sf.Vector4(f32); 6 | 7 | pub const IVec2 = sf.Vector2(c_int); 8 | pub const IVec3 = sf.Vector3(c_int); 9 | pub const IVec4 = sf.Vector4(c_int); 10 | 11 | pub const BVec2 = sf.Vector2(bool); 12 | pub const BVec3 = sf.Vector3(bool); 13 | pub const BVec4 = sf.Vector4(bool); 14 | 15 | pub const Mat3 = [3 * 3]f32; 16 | pub const Mat4 = [4 * 4]f32; 17 | -------------------------------------------------------------------------------- /src/window/clipboard.zig: -------------------------------------------------------------------------------- 1 | const sf = struct { 2 | const sfml = @import("../root.zig"); 3 | pub usingnamespace sfml; 4 | pub usingnamespace sfml.window; 5 | }; 6 | 7 | const std = @import("std"); 8 | 9 | pub const Clipboard = struct { 10 | pub fn getString() ?[]const u8 { 11 | const c_str = sf.c.sfClipboard_getString(); 12 | return if (c_str != null) std.mem.span(c_str) else null; 13 | } 14 | 15 | pub fn setString(text: []const u8) void { 16 | sf.c.sfClipboard_setString(text.ptr); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/window/window_module.zig: -------------------------------------------------------------------------------- 1 | pub const Event = @import("event.zig").Event; 2 | pub const Style = @import("Style.zig"); 3 | pub const ContextSettings = @import("context_settings.zig").ContextSettings; 4 | pub const keyboard = @import("keyboard.zig"); 5 | pub const mouse = @import("mouse.zig"); 6 | pub const Joystick = @import("Joystick.zig"); 7 | pub const Cursor = @import("Cursor.zig"); 8 | pub const Clipboard = @import("Clipboard.zig").Clipboard; 9 | 10 | //pub const touch = @compileError("touch not available yet"); 11 | //pub const sensor = @compileError("Sensor not available yet"); 12 | -------------------------------------------------------------------------------- /src/graphics/RenderStates.zig: -------------------------------------------------------------------------------- 1 | //! Defines settings for drawing things on a target 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.graphics; 7 | }; 8 | 9 | blend_mode: sf.BlendMode = sf.BlendMode.BlendAlpha, 10 | transform: sf.Transform = sf.Transform.Identity, 11 | texture: ?sf.Texture = null, 12 | shader: ?sf.Shader = null, 13 | 14 | pub fn _toCSFML(self: @This()) sf.c.sfRenderStates { 15 | return .{ .blendMode = self.blend_mode._toCSFML(), .transform = self.transform._toCSFML(), .texture = if (self.texture) |t| t._get() else null, .shader = if (self.shader) |s| s._ptr else null }; 16 | } 17 | -------------------------------------------------------------------------------- /src/system/system_module.zig: -------------------------------------------------------------------------------- 1 | const vector = @import("vector.zig"); 2 | pub const Vector2 = vector.Vector2; 3 | pub const Vector3 = vector.Vector3; 4 | pub const Vector4 = vector.Vector4; 5 | pub const Vector2i = Vector2(c_int); 6 | pub const vector2i = Vector2i.new; 7 | pub const Vector2u = Vector2(c_uint); 8 | pub const vector2u = Vector2u.new; 9 | pub const Vector2f = Vector2(f32); 10 | pub const vector2f = Vector2f.new; 11 | pub const Vector3f = Vector3(f32); 12 | pub const vector3f = Vector3f.new; 13 | 14 | pub const Time = @import("Time.zig"); 15 | pub const TimeSpan = @import("TimeSpan.zig"); 16 | pub const Clock = @import("Clock.zig"); 17 | pub const Buffer = @import("Buffer.zig"); 18 | -------------------------------------------------------------------------------- /src/examples/green_circle.zig: -------------------------------------------------------------------------------- 1 | //! This is a translation of the c++ code the sfml website gives you to test if SFML works 2 | //! for instance, in this page: https://www.sfml-dev.org/tutorials/2.6/start-vc.php 3 | 4 | const sf = @import("sfml").graphics; 5 | 6 | pub fn main() !void { 7 | var window = try sf.RenderWindow.createDefault(.{ .x = 200, .y = 200 }, "SFML works!"); 8 | defer window.destroy(); 9 | 10 | var shape = try sf.CircleShape.create(100.0); 11 | defer shape.destroy(); 12 | shape.setFillColor(sf.Color.Green); 13 | 14 | while (window.isOpen()) { 15 | while (window.pollEvent()) |event| { 16 | if (event == .closed) 17 | window.close(); 18 | } 19 | 20 | window.clear(sf.Color.Black); 21 | window.draw(shape, null); 22 | window.display(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/network/Socket.zig: -------------------------------------------------------------------------------- 1 | //! Contains socket utils for all socket types (not a subtype for polymorphism) 2 | 3 | const sf = struct { 4 | pub usingnamespace @import("../root.zig"); 5 | pub usingnamespace sf.network; 6 | }; 7 | 8 | /// Miscellaneous errors that can occur when handling sockets (connecting, sending, receiving, etc...) 9 | pub const Error = error{ notReady, partial, disconnected, otherError }; 10 | 11 | /// Turns a csfml error code into a proper zig error 12 | /// For this wrapper's internal workings 13 | pub fn _codeToErr(code: sf.c.sfSocketStatus) Error!void { 14 | switch (code) { 15 | sf.c.sfSocketDone => return, 16 | sf.c.sfSocketNotReady => return error.notReady, 17 | sf.c.sfSocketPartial => return error.partial, 18 | sf.c.sfSocketDisconnected => return error.disconnected, 19 | sf.c.sfSocketError => return error.otherError, 20 | else => unreachable, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/window/context_settings.zig: -------------------------------------------------------------------------------- 1 | const sf = @import("../root.zig"); 2 | 3 | pub const ContextSettings = packed struct { 4 | const PaddingType = @import("std").meta.Int(.unsigned, @bitSizeOf(c_int) - @bitSizeOf(bool)); 5 | 6 | depth_bits: c_uint = 0, 7 | stencil_bits: c_uint = 0, 8 | antialiasing_level: c_uint = 0, 9 | major_version: c_uint = 1, 10 | minor_version: c_uint = 1, 11 | attribute_flags: u32 = 0, 12 | srgb_capable: bool = false, 13 | _padding: PaddingType = 0, 14 | 15 | pub const Attribute = struct { 16 | pub const default: u32 = 0; 17 | pub const core: u32 = 1; 18 | pub const debug: u32 = 4; 19 | }; 20 | 21 | pub fn _toCSFML(self: ContextSettings) sf.c.sfContextSettings { 22 | return @as(sf.c.sfContextSettings, @bitCast(self)); 23 | } 24 | 25 | pub fn _fromCSFML(context_settings: sf.c.sfContextSettings) ContextSettings { 26 | return @as(ContextSettings, @bitCast(context_settings)); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2021 Guigui220D 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 | OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | This project does not contain the sources of CSFML. The licence of CSFML can be found in CSFML-LICENSE 16 | 17 | funnysong.mp3 from https://www.bensound.com/ 18 | cute_image.png from https://github.com/ziglang/logo -------------------------------------------------------------------------------- /CSFML-LICENCE: -------------------------------------------------------------------------------- 1 | # CSFML 2 | 3 | CSFML - Copyright (C) 2007-2024 Laurent Gomila - laurent@sfml-dev.org 4 | 5 | This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 10 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 11 | 3. This notice may not be removed or altered from any source distribution. 12 | 13 | ## External libraries used by CSFML 14 | 15 | * _SFML_ is under the zlib/png license 16 | -------------------------------------------------------------------------------- /src/graphics/render_target.zig: -------------------------------------------------------------------------------- 1 | //! Target for generalizing draw functions 2 | 3 | const sf = @import("../root.zig").graphics; 4 | 5 | pub const RenderTarget = union(enum) { 6 | window: *sf.RenderWindow, 7 | texture: *sf.RenderTexture, 8 | 9 | pub fn draw(self: *const RenderTarget, to_draw: anytype, states: ?sf.RenderStates) void { 10 | switch (self.*) { 11 | .window => |w| w.draw(to_draw, states), 12 | .texture => |t| t.draw(to_draw, states), 13 | } 14 | } 15 | }; 16 | 17 | test "rendertarget tests" { 18 | // var window = try sf.RenderWindow.createDefault(.{ .x = 10, .y = 10 }, "yo"); 19 | // defer window.destroy(); 20 | var texture = try sf.RenderTexture.create(.{ .x = 10, .y = 10 }); 21 | defer texture.destroy(); 22 | var rect = try sf.RectangleShape.create(.{ .x = 10, .y = 10 }); 23 | defer rect.destroy(); 24 | 25 | var target: RenderTarget = undefined; 26 | 27 | // target = .{ .window = &window }; 28 | // target.draw(rect, null); 29 | 30 | target = .{ .texture = &texture }; 31 | target.draw(rect, null); 32 | } 33 | -------------------------------------------------------------------------------- /src/system/TimeSpan.zig: -------------------------------------------------------------------------------- 1 | //! Represents a time span. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | }; 8 | 9 | const TimeSpan = @This(); 10 | 11 | // Constructors 12 | 13 | /// Construcs a time span 14 | pub fn init(begin: sf.Time, length: sf.Time) TimeSpan { 15 | return sf.TimeSpan{ 16 | .offset = begin, 17 | .length = length, 18 | }; 19 | } 20 | 21 | /// Converts a timespan from a csfml object 22 | /// For inner workings 23 | pub fn _fromCSFML(span: sf.c.sfTimeSpan) TimeSpan { 24 | return sf.TimeSpan{ 25 | .offset = sf.Time._fromCSFML(span.offset), 26 | .length = sf.Time._fromCSFML(span.length), 27 | }; 28 | } 29 | 30 | /// Converts a timespan to a csfml object 31 | /// For inner workings 32 | pub fn _toCSFML(self: sf.TimeSpan) sf.c.sfTimeSpan { 33 | return sf.c.sfTimeSpan{ 34 | .offset = self.offset._toCSFML(), 35 | .length = self.length._toCSFML(), 36 | }; 37 | } 38 | 39 | /// The beginning of this span 40 | offset: sf.Time, 41 | /// The length of this time span 42 | length: sf.Time, 43 | -------------------------------------------------------------------------------- /src/graphics/vertex.zig: -------------------------------------------------------------------------------- 1 | //! Define a point with color and texture coordinates. 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | pub const Vertex = extern struct { 6 | /// Position of the vertex 7 | position: sf.system.Vector2f = sf.system.Vector2f{ .x = 0, .y = 0 }, 8 | /// Color of the vertex 9 | color: sf.graphics.Color = sf.graphics.Color.White, 10 | /// Texture coordinates of the vertex 11 | tex_coords: sf.system.Vector2f = sf.system.Vector2f{ .x = 0, .y = 0 }, 12 | 13 | /// Allows iterating over a slice of vertices as if they were primitives 14 | /// See primitive_type 15 | pub fn verticesAsPrimitives(vertices: []Vertex, comptime primitive_type: sf.graphics.PrimitiveType) []primitive_type.Type() { 16 | if (vertices.len % (comptime primitive_type.packedBy()) != 0) 17 | @panic("The total number of vertices must be a multiple of the primitive type number of vertices"); 18 | 19 | var ret: []primitive_type.Type() = undefined; 20 | ret.len = vertices.len / (comptime primitive_type.packedBy()); 21 | ret.ptr = @as([*]primitive_type.Type(), @ptrCast(vertices.ptr)); 22 | 23 | return ret; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/window/mouse.zig: -------------------------------------------------------------------------------- 1 | //! Give access to the real-time state of the mouse. 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | /// Mouse buttons 6 | pub const Button = enum(c_uint) { left, right, middle, x_button1, x_button2 }; 7 | /// Mouse wheels 8 | pub const Wheel = enum(c_uint) { vertical, horizontal }; 9 | 10 | /// Returns true if the specified mouse button is pressed 11 | pub fn isButtonPressed(button: Button) bool { 12 | return sf.c.sfMouse_isButtonPressed(@intFromEnum(button)) == 1; 13 | } 14 | 15 | /// Gets the position of the mouse cursor relative to the window passed or desktop 16 | pub fn getPosition(window: ?sf.graphics.RenderWindow) sf.system.Vector2i { 17 | if (window) |w| { 18 | return sf.system.Vector2i._fromCSFML(sf.c.sfMouse_getPosition(@as(*sf.c.sfWindow, @ptrCast(w._ptr)))); 19 | } else return sf.system.Vector2i._fromCSFML(sf.c.sfMouse_getPosition(null)); 20 | } 21 | /// Set the position of the mouse cursor relative to the window passed or desktop 22 | pub fn setPosition(position: sf.system.Vector2i, window: ?sf.graphics.RenderWindow) void { 23 | if (window) |w| { 24 | sf.c.sfMouse_setPosition(position._toCSFML(), @as(*sf.c.sfWindow, @ptrCast(w._ptr))); 25 | } else sf.c.sfMouse_setPosition(position._toCSFML(), null); 26 | } 27 | -------------------------------------------------------------------------------- /src/root.zig: -------------------------------------------------------------------------------- 1 | //! Import this to get all the sfml wrapper classes 2 | 3 | pub const system = @import("system/system_module.zig"); 4 | pub const window = @import("window/window_module.zig"); 5 | pub const graphics = @import("graphics/graphics_module.zig"); 6 | pub const audio = @import("audio/audio_module.zig"); 7 | pub const network = @import("network/network_module.zig"); 8 | 9 | pub usingnamespace @import("unicode.zig"); 10 | 11 | pub const c = @cImport({ 12 | @cInclude("SFML/Graphics.h"); 13 | @cInclude("SFML/Window.h"); 14 | @cInclude("SFML/System.h"); 15 | @cInclude("SFML/Audio.h"); 16 | @cInclude("SFML/Network.h"); 17 | }); 18 | 19 | pub const Error = error{ 20 | nullptrUnknownReason, 21 | windowCreationFailed, 22 | resourceLoadingError, 23 | notEnoughData, 24 | areaDoesNotFit, 25 | outOfBounds, 26 | savingInFileFailed, 27 | cannotWriteToPacket, 28 | noMoreData, 29 | couldntRead, 30 | timeout, 31 | wrongDataSize, 32 | unimplemented, 33 | }; 34 | 35 | test "all sfml tests" { 36 | const tst = @import("std").testing; 37 | tst.refAllDeclsRecursive(system); 38 | tst.refAllDeclsRecursive(window); 39 | tst.refAllDeclsRecursive(graphics); 40 | tst.refAllDeclsRecursive(audio); 41 | //tst.refAllDeclsRecursive(network); 42 | } 43 | -------------------------------------------------------------------------------- /src/graphics/graphics_module.zig: -------------------------------------------------------------------------------- 1 | pub const Color = @import("color.zig").Color; 2 | 3 | pub const RenderWindow = @import("RenderWindow.zig"); 4 | pub const RenderTexture = @import("RenderTexture.zig"); 5 | pub const RenderTarget = @import("render_target.zig").RenderTarget; 6 | 7 | pub const View = @import("View.zig"); 8 | 9 | pub const Image = @import("Image.zig"); 10 | pub const Texture = @import("texture.zig").Texture; 11 | 12 | pub const Sprite = @import("Sprite.zig"); 13 | 14 | pub const CircleShape = @import("CircleShape.zig"); 15 | pub const RectangleShape = @import("RectangleShape.zig"); 16 | pub const ConvexShape = @import("ConvexShape.zig"); 17 | 18 | pub const Vertex = @import("vertex.zig").Vertex; 19 | pub const VertexArray = @import("VertexArray.zig"); 20 | pub const VertexBuffer = @import("VertexBuffer.zig"); 21 | pub const PrimitiveType = @import("primitive_type.zig").PrimitiveType; 22 | 23 | pub const Rect = @import("rect.zig").Rect; 24 | pub const IntRect = Rect(c_int); 25 | pub const FloatRect = Rect(f32); 26 | 27 | pub const Font = @import("Font.zig"); 28 | pub const Text = @import("Text.zig"); 29 | 30 | pub const glsl = @import("glsl.zig"); 31 | pub const Shader = @import("Shader.zig"); 32 | pub const BlendMode = @import("blend_mode.zig").BlendMode; 33 | pub const RenderStates = @import("RenderStates.zig"); 34 | pub const Transform = @import("transform.zig").Transform; 35 | -------------------------------------------------------------------------------- /src/system/Buffer.zig: -------------------------------------------------------------------------------- 1 | //! Buffer classed used for saveToMemory functions 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | }; 7 | 8 | const Buffer = @This(); 9 | 10 | // Constructor/destructor 11 | 12 | /// Inits an empty buffer. 13 | /// Std.time timer also is a good alternative 14 | pub fn create() !Buffer { 15 | const buffer = sf.c.sfBuffer_create(); 16 | if (buffer == null) 17 | return sf.Error.nullptrUnknownReason; 18 | 19 | return Buffer{ ._ptr = buffer.? }; 20 | } 21 | 22 | /// Destroys this clock 23 | pub fn destroy(self: *Buffer) void { 24 | sf.c.sfBuffer_destroy(self._ptr); 25 | self._ptr = undefined; 26 | } 27 | 28 | // Getters 29 | /// Get the size of the data stored in the buffer 30 | pub fn getSize(self: Buffer) usize { 31 | return @intCast(sf.c.sfBuffer_getSize(self._ptr)); 32 | } 33 | /// Get the data stored in the buffer as a slice (you don't own the slice) 34 | /// This doesn't seem to work if the buffer wasn't filled by a function 35 | pub fn getData(self: Buffer) []const u8 { 36 | const size = self.getSize(); 37 | const ptr: [*]const u8 = @ptrCast(sf.c.sfBuffer_getData(self._ptr)); 38 | return ptr[0..size]; 39 | } 40 | 41 | /// Pointer to the csfml structure 42 | _ptr: *sf.c.sfBuffer, 43 | 44 | // Tests 45 | 46 | test "buffer: functions compile" { 47 | const tst = @import("std").testing; 48 | 49 | var buf = try Buffer.create(); 50 | defer buf.destroy(); 51 | 52 | try tst.expectEqual(0, buf.getSize()); 53 | 54 | // This doesn't seem to work if the buffer wasn't filled by a function 55 | //const slice = buf.getData(); 56 | //try tst.expectEqual(0, slice.len); 57 | } 58 | -------------------------------------------------------------------------------- /src/unicode.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub inline fn toUnicodeComptime(utf8: []const u8) [:0]const u32 { 4 | comptime { 5 | var view = std.unicode.Utf8View.initComptime(utf8); 6 | var iter = view.iterator(); 7 | 8 | var result: [:0]const u32 = &.{}; 9 | while (iter.nextCodepoint()) |c| { 10 | result = result ++ [1]u32{c}; 11 | } 12 | 13 | return result; 14 | } 15 | } 16 | 17 | pub fn toUnicodeAlloc(alloc: std.mem.Allocator, utf8: []const u8) ![:0]const u32 { 18 | var arr = try std.ArrayList(u32).initCapacity(alloc, utf8.len + 1); 19 | errdefer arr.deinit(); 20 | 21 | var view = try std.unicode.Utf8View.init(utf8); 22 | var iter = view.iterator(); 23 | 24 | while (iter.nextCodepoint()) |c| { 25 | arr.append(c) catch unreachable; // We already allocated enough space 26 | } 27 | 28 | arr.append(0) catch unreachable; 29 | 30 | var ret: [:0]const u32 = @ptrCast(try arr.toOwnedSlice()); 31 | ret.len -= 1; // I have to exclude manually the 0? 32 | return ret; 33 | } 34 | 35 | test "Utf8 to Utf32 comptime" { 36 | const tst = std.testing; 37 | 38 | const str1 = toUnicodeComptime("Hi! 😁 你好"); 39 | try tst.expectEqualSlices(u32, &[_]u32{ 'H', 'i', '!', ' ', 0x1f601, ' ', 0x4f60, 0x597d }, str1); 40 | try tst.expectEqual(0, str1[str1.len]); 41 | } 42 | 43 | test "Utf8 to Utf32" { 44 | const tst = std.testing; 45 | const alloc = tst.allocator; 46 | 47 | const str1 = try toUnicodeAlloc(alloc, "é👓ب"); 48 | defer alloc.free(str1); 49 | 50 | try tst.expectEqualSlices(u32, &[_]u32{ 0xe9, 0x1f453, 0x628 }, str1); 51 | try tst.expectEqual(0, str1[str1.len]); 52 | } 53 | -------------------------------------------------------------------------------- /src/graphics/primitive_type.zig: -------------------------------------------------------------------------------- 1 | //! The type of primitives in a vertex array of buffer 2 | 3 | const Vertex = @import("vertex.zig").Vertex; 4 | 5 | pub const PrimitiveType = enum(c_uint) { 6 | points, 7 | lines, 8 | line_strip, 9 | triangles, 10 | triangle_strip, 11 | triangle_fan, 12 | quads, 13 | 14 | /// Gives the corresponding primitive type for primitive iteration 15 | /// See Vertex.verticesAsPrimitives() 16 | pub fn Type(comptime primitive_type: PrimitiveType) type { 17 | return switch (primitive_type) { 18 | .points => PointPrimitive, 19 | .lines => LinePrimitive, 20 | .triangles => TrianglePrimitive, 21 | .quads => QuadPrimitive, 22 | else => @compileError("Primitive type not supported"), 23 | }; 24 | } 25 | /// Says how many vertices each primitive is composed of 26 | pub fn vertexCount(primitive_type: PrimitiveType) usize { 27 | return switch (primitive_type) { 28 | .points => 1, 29 | .lines => 2, 30 | .triangles => 3, 31 | .quads => 4, 32 | else => @panic("Primitive type not supported"), 33 | }; 34 | } 35 | 36 | //TODO: Note: these structs should be packed but zig has bugs with that as of now and it doesn't work 37 | 38 | /// A single point 39 | pub const PointPrimitive = struct { a: Vertex }; 40 | /// A line between two points 41 | pub const LinePrimitive = struct { a: Vertex, b: Vertex }; 42 | /// A triangle between three points 43 | pub const TrianglePrimitive = struct { a: Vertex, b: Vertex, c: Vertex }; 44 | /// A quad between four points 45 | pub const QuadPrimitive = struct { a: Vertex, b: Vertex, c: Vertex, d: Vertex }; 46 | }; 47 | -------------------------------------------------------------------------------- /src/examples/sfml_example.zig: -------------------------------------------------------------------------------- 1 | //! This the same example as presented on the sfml website 2 | //! https://www.sfml-dev.org/documentation/2.6.1/ 3 | 4 | const sf = struct { 5 | const sfml = @import("sfml"); 6 | usingnamespace sfml; 7 | usingnamespace sfml.audio; 8 | usingnamespace sfml.graphics; 9 | usingnamespace sfml.window; 10 | usingnamespace sfml.system; 11 | }; 12 | 13 | pub fn main() !void { 14 | // Create the main window 15 | var window = try sf.RenderWindow.create(.{ .x = 800, .y = 600 }, 32, "SFML window", sf.Style.defaultStyle, null); 16 | defer window.destroy(); 17 | 18 | // Load a sprite to display 19 | var texture = try sf.Texture.createFromFile("res/cute_image.png"); 20 | defer texture.destroy(); 21 | var sprite = try sf.Sprite.createFromTexture(texture); 22 | defer sprite.destroy(); 23 | 24 | // Create a graphical text to display 25 | var font = try sf.Font.createFromFile("res/arial.ttf"); 26 | defer font.destroy(); 27 | var text = try sf.Text.createWithText("Hello SFML", font, 50); 28 | defer text.destroy(); 29 | 30 | // Loads a music to play 31 | var music = try sf.Music.createFromFile("res/funnysong.mp3"); 32 | defer music.destroy(); 33 | music.play(); 34 | 35 | // Start the game loop 36 | while (window.isOpen()) { 37 | // Process events 38 | while (window.pollEvent()) |event| { 39 | // Close window: exit 40 | if (event == .closed) 41 | window.close(); 42 | } 43 | 44 | // Clear screen 45 | window.clear(sf.Color.Black); 46 | 47 | // Draw the sprite 48 | window.draw(sprite, null); 49 | 50 | // Draw the string 51 | window.draw(text, null); 52 | 53 | //Update the window 54 | window.display(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/system/Clock.zig: -------------------------------------------------------------------------------- 1 | //! Utility class that measures the elapsed time. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const Clock = @This(); 11 | 12 | // Constructor/destructor 13 | 14 | /// Inits a clock. The clock will have its time set at 0 at this point, and automatically starts 15 | /// Std.time timer also is a good alternative 16 | pub fn create() !Clock { 17 | const clock = sf.c.sfClock_create(); 18 | if (clock == null) 19 | return sf.Error.nullptrUnknownReason; 20 | 21 | return Clock{ ._ptr = clock.? }; 22 | } 23 | 24 | /// Destroys this clock 25 | pub fn destroy(self: *Clock) void { 26 | sf.c.sfClock_destroy(self._ptr); 27 | self._ptr = undefined; 28 | } 29 | 30 | // Clock control 31 | /// Gets the elapsed seconds 32 | pub fn getElapsedTime(self: Clock) sf.Time { 33 | const time = sf.c.sfClock_getElapsedTime(self._ptr).microseconds; 34 | return sf.Time{ .us = time }; 35 | } 36 | 37 | /// Gets the elapsed seconds and restarts the timer 38 | pub fn restart(self: *Clock) sf.Time { 39 | const time = sf.c.sfClock_restart(self._ptr).microseconds; 40 | return sf.Time{ .us = time }; 41 | } 42 | 43 | /// Pointer to the csfml structure 44 | _ptr: *sf.c.sfClock, 45 | 46 | test "clock: sleep test" { 47 | const tst = @import("std").testing; 48 | 49 | // This tests just sleeps and check what the timer measured (not very accurate but eh) 50 | var clk = try Clock.create(); 51 | defer clk.destroy(); 52 | 53 | sf.Time.milliseconds(500).sleep(); 54 | 55 | try tst.expectApproxEqAbs(@as(f32, 0.5), clk.getElapsedTime().asSeconds(), 0.1); 56 | 57 | sf.Time.sleep(sf.Time.seconds(0.2)); 58 | 59 | try tst.expectApproxEqAbs(@as(f32, 0.7), clk.restart().asSeconds(), 0.1); 60 | try tst.expectApproxEqAbs(@as(f32, 0), clk.getElapsedTime().asSeconds(), 0.01); 61 | } 62 | -------------------------------------------------------------------------------- /src/window/keyboard.zig: -------------------------------------------------------------------------------- 1 | //! Give access to the real-time state of the keyboard. 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | /// Keycodes 6 | pub const KeyCode = enum(c_int) { 7 | unknown = -1, 8 | A = 0, 9 | B, 10 | C, 11 | D, 12 | E, 13 | F, 14 | G, 15 | H, 16 | I, 17 | J, 18 | K, 19 | L, 20 | M, 21 | N, 22 | O, 23 | P, 24 | Q, 25 | R, 26 | S, 27 | T, 28 | U, 29 | V, 30 | W, 31 | X, 32 | Y, 33 | Z, 34 | num0, 35 | num1, 36 | num2, 37 | num3, 38 | num4, 39 | num5, 40 | num6, 41 | num7, 42 | num8, 43 | num9, 44 | escape, 45 | l_control, 46 | l_shift, 47 | l_alt, 48 | l_system, 49 | r_control, 50 | r_shift, 51 | r_alt, 52 | r_system, 53 | menu, 54 | l_bracket, 55 | r_bracket, 56 | semicolon, 57 | comma, 58 | period, 59 | quote, 60 | slash, 61 | backslash, 62 | tilde, 63 | equal, 64 | hyphen, 65 | space, 66 | enter, 67 | backspace, 68 | tab, 69 | page_up, 70 | page_down, 71 | end, 72 | home, 73 | insert, 74 | delete, 75 | add, 76 | subtract, 77 | multiply, 78 | divide, 79 | left, 80 | right, 81 | up, 82 | down, 83 | numpad0, 84 | numpad1, 85 | numpad2, 86 | numpad3, 87 | numpad4, 88 | numpad5, 89 | numpad6, 90 | numpad7, 91 | numpad8, 92 | numpad9, 93 | F1, 94 | F2, 95 | F3, 96 | F4, 97 | F5, 98 | F6, 99 | F7, 100 | F8, 101 | F9, 102 | F10, 103 | F11, 104 | F12, 105 | F13, 106 | F14, 107 | F15, 108 | pause, 109 | key_count, 110 | }; 111 | 112 | /// Returns true if the specified key is pressed 113 | pub fn isKeyPressed(key: KeyCode) bool { 114 | return sf.c.sfKeyboard_isKeyPressed(@intFromEnum(key)) == 1; 115 | } 116 | -------------------------------------------------------------------------------- /src/window/Joystick.zig: -------------------------------------------------------------------------------- 1 | //! Give access to the real-time state of the joysticks. 2 | 3 | const std = @import("std"); 4 | const sf = @import("../root.zig"); 5 | 6 | const Joystick = @This(); 7 | 8 | /// Constants related to joysticks capabilities 9 | pub const MaxJoystickCount = 8; 10 | pub const MaxButtonCount = 32; 11 | pub const MaxAxisCount = 8; 12 | 13 | /// Joystick axis 14 | pub const Axis = enum(c_uint) { x, y, z, r, u, v, pov_x, pov_y }; 15 | 16 | /// Gets a joystick if it is connected, null if it is not 17 | /// Technically, you can use a joystick's getters if it's not connected or before it connects 18 | /// If you wish to do so, just construct the joystick struct by setting _joystick_number to what you want. 19 | pub fn get(joystick: c_uint) ?Joystick { 20 | std.debug.assert(joystick < MaxJoystickCount); 21 | if (sf.c.sfJoystick_isConnected(joystick) != 0) { 22 | return Joystick{ ._joystick_number = joystick }; 23 | } else return null; 24 | } 25 | 26 | /// Check if this joystick is still connected 27 | pub fn isConnected(self: Joystick) bool { 28 | return sf.c.sfJoystick_isConnected(self._joystick_number) != 0; 29 | } 30 | 31 | /// Gets the button count of this joystick 32 | pub fn getButtonCount(joystick: Joystick) usize { 33 | return sf.c.sfJoystick_getButtonCount(joystick._joystick_number); 34 | } 35 | /// Checks if the joystick has a given axis 36 | pub fn hasAxis(self: Joystick, axis: Axis) bool { 37 | return sf.c.sfJoystick_hasAxis(self._joystick_number, @intFromEnum(axis)) != 0; 38 | } 39 | 40 | /// Gets the value of an axis 41 | pub fn getAxisPosition(self: Joystick, axis: Axis) f32 { 42 | return sf.c.sfJoystick_getAxisPosition(self._joystick_number, @intFromEnum(axis)); 43 | } 44 | /// Checks if a joystick button is pressed 45 | pub fn isButtonPressed(self: Joystick, button: c_uint) bool { 46 | std.debug.assert(button < MaxButtonCount); 47 | return sf.c.sfJoystick_isButtonPressed(self._joystick_number, button) != 0; 48 | } 49 | 50 | /// ID of this joystick 51 | _joystick_number: c_uint 52 | -------------------------------------------------------------------------------- /src/system/Time.zig: -------------------------------------------------------------------------------- 1 | //! Represents a time value. 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | const Time = @This(); 6 | 7 | // Constructors 8 | 9 | /// Converts a time from a csfml object 10 | /// For inner workings 11 | pub fn _fromCSFML(time: sf.c.sfTime) Time { 12 | return Time{ .us = time.microseconds }; 13 | } 14 | 15 | /// Converts a time to a csfml object 16 | /// For inner workings 17 | pub fn _toCSFML(self: Time) sf.c.sfTime { 18 | return sf.c.sfTime{ .microseconds = self.us }; 19 | } 20 | 21 | /// Creates a time object from a seconds count 22 | pub fn seconds(s: f32) Time { 23 | return Time{ .us = @as(i64, @intFromFloat(s * 1_000)) * 1_000 }; 24 | } 25 | 26 | /// Creates a time object from milliseconds 27 | pub fn milliseconds(ms: i32) Time { 28 | return Time{ .us = @as(i64, @intCast(ms)) * 1_000 }; 29 | } 30 | 31 | /// Creates a time object from microseconds 32 | pub fn microseconds(us: i64) Time { 33 | return Time{ .us = us }; 34 | } 35 | 36 | // Getters 37 | 38 | /// Gets this time measurement as microseconds 39 | pub fn asMicroseconds(self: Time) i64 { 40 | return self.us; 41 | } 42 | 43 | /// Gets this time measurement as milliseconds 44 | pub fn asMilliseconds(self: Time) i32 { 45 | return @as(i32, @truncate(@divFloor(self.us, 1_000))); 46 | } 47 | 48 | /// Gets this time measurement as seconds (as a float) 49 | pub fn asSeconds(self: Time) f32 { 50 | return @as(f32, @floatFromInt(self.us)) / 1_000_000; 51 | } 52 | 53 | // Misc 54 | 55 | /// Sleeps the amount of time specified 56 | pub fn sleep(time: Time) void { 57 | sf.c.sfSleep(time._toCSFML()); 58 | } 59 | 60 | /// A time of zero 61 | pub const Zero = microseconds(0); 62 | 63 | us: i64, 64 | 65 | test "time: conversion" { 66 | const tst = @import("std").testing; 67 | 68 | var t = Time.microseconds(5_120_000); 69 | 70 | try tst.expectEqual(@as(i32, 5_120), t.asMilliseconds()); 71 | try tst.expectApproxEqAbs(@as(f32, 5.12), t.asSeconds(), 0.0001); 72 | 73 | t = Time.seconds(12); 74 | 75 | try tst.expectApproxEqAbs(@as(f32, 12), t.asSeconds(), 0.0001); 76 | 77 | t = Time.microseconds(800); 78 | try tst.expectApproxEqAbs(@as(f32, 0.0008), t.asSeconds(), 0.0001); 79 | } 80 | -------------------------------------------------------------------------------- /res/heat_shader.fs: -------------------------------------------------------------------------------- 1 | // heat_shader.fs 2 | // for the heat_haze example by by binary1248 3 | 4 | #version 130 5 | 6 | uniform sampler2D currentTexture; // Our render texture 7 | uniform sampler2D distortionMapTexture; // Our heat distortion map texture 8 | 9 | uniform float time; // Time used to scroll the distortion map 10 | uniform float distortionFactor; // Factor used to control severity of the effect 11 | uniform float riseFactor; // Factor used to control how fast air rises 12 | 13 | void main() 14 | { 15 | vec2 distortionMapCoordinate = gl_TexCoord[0].st; 16 | 17 | // We use the time value to scroll our distortion texture upwards 18 | // Since we enabled texture repeating, OpenGL takes care of 19 | // coordinates that lie outside of [0, 1] by discarding 20 | // the integer part and keeping the fractional part 21 | // Basically performing a "floating point modulo 1" 22 | // 1.1 = 0.1, 2.4 = 0.4, 10.3 = 0.3 etc. 23 | distortionMapCoordinate.t -= time * riseFactor; 24 | 25 | vec4 distortionMapValue = texture2D(distortionMapTexture, distortionMapCoordinate); 26 | 27 | // The values are normalized by OpenGL to lie in the range [0, 1] 28 | // We want negative offsets too, so we subtract 0.5 and multiply by 2 29 | // We end up with values in the range [-1, 1] 30 | vec2 distortionPositionOffset = distortionMapValue.xy; 31 | distortionPositionOffset -= vec2(0.5f, 0.5f); 32 | distortionPositionOffset *= 2.f; 33 | 34 | // The factor scales the offset and thus controls the severity 35 | distortionPositionOffset *= distortionFactor; 36 | 37 | // The latter 2 channels of the texture are unused... be creative 38 | vec2 distortionUnused = distortionMapValue.zw; 39 | 40 | // Since we all know that hot air rises and cools, 41 | // the effect loses its severity the higher up we get 42 | // We use the t (a.k.a. y) texture coordinate of the original texture 43 | // to tell us how "high up" we are and damp accordingly 44 | // Remember, OpenGL 0 is at the bottom 45 | distortionPositionOffset *= (1.f - gl_TexCoord[0].t); 46 | 47 | vec2 distortedTextureCoordinate = gl_TexCoord[0].st + distortionPositionOffset; 48 | 49 | gl_FragColor = gl_Color * texture2D(currentTexture, distortedTextureCoordinate); 50 | } -------------------------------------------------------------------------------- /src/network/TcpListener.zig: -------------------------------------------------------------------------------- 1 | //! A TCP listener socket 2 | 3 | const sf = struct { 4 | pub usingnamespace @import("../root.zig"); 5 | pub usingnamespace sf.network; 6 | }; 7 | 8 | const TcpListener = @This(); 9 | 10 | // Constructor/destructor 11 | 12 | /// Creates a new tcp listener socket 13 | pub fn create() !TcpListener { 14 | const sock = sf.c.sfTcpListener_create(); 15 | if (sock) |s| { 16 | return TcpListener{ ._ptr = s }; 17 | } else return sf.Error.nullptrUnknownReason; 18 | } 19 | /// Destroys this socket 20 | pub fn destroy(self: *TcpListener) void { 21 | sf.c.sfTcpListener_destroy(self._ptr); 22 | } 23 | 24 | // Methods 25 | 26 | /// Enables or disables blocking mode (true for blocking) 27 | /// In blocking mode, receive waits for data 28 | pub fn setBlocking(self: *TcpListener, blocking: bool) void { 29 | sf.c.sfTcpListener_setBlocking(self._ptr, @intFromBool(blocking)); 30 | } 31 | /// Tells whether or not the socket is in blocking mode 32 | pub fn isBlocking(self: TcpListener) bool { 33 | return sf.c.sfTcpListener_isBlocking(self._ptr) != 0; 34 | } 35 | 36 | /// Gets the port this socket is listening on 37 | /// Error if the socket is not listening 38 | pub fn getLocalPort(self: TcpListener) error{notListening}!u16 { 39 | const port = sf.c.sfTcpListener_getLocalPort(self._ptr); 40 | if (port == 0) 41 | return error.notListening; 42 | return port; 43 | } 44 | 45 | /// Starts listening for incoming connections on a given port (and address) 46 | /// If address is null, it listens on any address of this machine 47 | pub fn listen(self: *TcpListener, port: u16, address: ?sf.IpAddress) sf.Socket.Error!void { 48 | const ip = if (address) |i| i._ip else sf.c.sfIpAddress_Any; 49 | const code = sf.c.sfTcpListener_listen(self._ptr, port, ip); 50 | try sf.Socket._codeToErr(code); 51 | } 52 | /// Accepts a new connection, returns a tcp socket if it works 53 | /// If the tcp is in blocking mode, it will wait 54 | pub fn accept(self: *TcpListener) sf.Socket.Error!?sf.TcpSocket { 55 | var ret: sf.TcpSocket = undefined; 56 | const retptr = @as([*c]?*sf.c.sfTcpSocket, @ptrCast(&(ret._ptr))); 57 | const code = sf.c.sfTcpListener_accept(self._ptr, retptr); 58 | if (!self.isBlocking() and code == sf.c.sfSocketNotReady) 59 | return null; 60 | try sf.Socket._codeToErr(code); 61 | return ret; 62 | } 63 | 64 | /// Pointer to the csfml structure 65 | _ptr: *sf.c.sfTcpListener, 66 | 67 | // TODO: write tests 68 | -------------------------------------------------------------------------------- /src/graphics/transform.zig: -------------------------------------------------------------------------------- 1 | //! Define a 3x3 transformation matrix 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | pub const Transform = extern struct { 6 | /// Identity matrix (doesn't do anything) 7 | pub const Identity = Transform{ .matrix = .{ 1, 0, 0, 0, 1, 0, 0, 0, 1 } }; 8 | 9 | /// Converts this transform to a csfml one 10 | /// For inner workings 11 | pub fn _toCSFML(self: Transform) sf.c.sfTransform { 12 | return @as(sf.c.sfTransform, @bitCast(self)); 13 | } 14 | /// Converts a transform from a csfml object 15 | /// For inner workings 16 | pub fn _fromCSFML(transform: sf.c.sfTransform) Transform { 17 | return @as(Transform, @bitCast(transform)); 18 | } 19 | 20 | // Matrix transformations 21 | /// Transforms a point by this matrix 22 | pub fn transformPoint(self: Transform, point: sf.system.Vector2f) sf.system.Vector2f { 23 | const ptr = @as(*const sf.c.sfTransform, @ptrCast(&self)); 24 | return sf.system.Vector2f._fromCSFML(sf.c.sfTransform_transformPoint(ptr, point._toCSFML())); 25 | } 26 | /// Transforms a rectangle by this matrix 27 | pub fn transformRect(self: Transform, rect: sf.graphics.FloatRect) sf.graphics.FloatRect { 28 | const ptr = @as(*const sf.c.sfTransform, @ptrCast(&self)); 29 | return sf.graphics.FloatRect._fromCSFML(sf.c.sfTransform_transformRect(ptr, rect._toCSFML())); 30 | } 31 | 32 | // Operations 33 | 34 | /// Gets the inverse of this transformation 35 | /// Returns the identity if it can't be calculated 36 | pub fn getInverse(self: Transform) Transform { 37 | const ptr = @as(*const sf.c.sfTransform, @ptrCast(&self)); 38 | return _fromCSFML(sf.c.sfTransform_getInverse(ptr)); 39 | } 40 | /// Combines two transformations 41 | pub fn combine(self: *Transform, other: Transform) void { 42 | sf.c.sfTransform_combine(@ptrCast(self), &other._toCSFML()); 43 | } 44 | 45 | /// Translates this transform by x and y 46 | pub fn translate(self: *Transform, translation: sf.system.Vector2f) void { 47 | const ptr = @as(*sf.c.sfTransform, @ptrCast(self)); 48 | sf.c.sfTransform_translate(ptr, translation.x, translation.y); 49 | } 50 | /// Rotates this transform 51 | pub fn rotate(self: *Transform, angle: f32) void { 52 | const ptr = @as(*sf.c.sfTransform, @ptrCast(self)); 53 | sf.c.sfTransform_rotate(ptr, angle); 54 | } 55 | /// Scales this transform 56 | pub fn scale(self: *Transform, factor: sf.system.Vector2f) void { 57 | const ptr = @as(*sf.c.sfTransform, @ptrCast(self)); 58 | sf.c.sfTransform_scale(ptr, factor.x, factor.y); 59 | } 60 | 61 | /// The matrix defining this transform 62 | matrix: [9]f32, 63 | }; 64 | -------------------------------------------------------------------------------- /src/graphics/blend_mode.zig: -------------------------------------------------------------------------------- 1 | //! Blending modes for drawing (for render states) 2 | 3 | pub const BlendMode = extern struct { 4 | // Enums 5 | pub const Factor = enum(c_int) { 6 | zero, 7 | one, 8 | src_color, 9 | one_minus_src_color, 10 | dst_color, 11 | one_minus_dst_color, 12 | src_alpha, 13 | one_minus_src_alpha, 14 | dst_alpha, 15 | one_minus_dst_alpha, 16 | }; 17 | 18 | pub const Equation = enum(c_int) { 19 | add, 20 | subtract, 21 | reverseSubtract, 22 | min, 23 | max, 24 | }; 25 | 26 | // Preset blend modes 27 | pub const BlendAlpha = BlendMode{ 28 | .color_src_factor = .src_alpha, 29 | .color_dst_factor = .one_minus_src_alpha, 30 | .color_equation = .add, 31 | .alpha_src_factor = .one, 32 | .alpha_dst_factor = .one_minus_src_alpha, 33 | .alpha_equation = .add, 34 | }; 35 | 36 | pub const BlendAdd = BlendMode{ 37 | .color_src_factor = .src_alpha, 38 | .color_dst_factor = .one, 39 | .color_equation = .add, 40 | .alpha_src_factor = .one, 41 | .alpha_dst_factor = .one, 42 | .alpha_equation = .add, 43 | }; 44 | 45 | pub const BlendMultiply = BlendMode{ 46 | .color_src_factor = .dst_color, 47 | .color_dst_factor = .zero, 48 | .color_equation = .add, 49 | .alpha_src_factor = .dst_color, 50 | .alpha_dst_factor = .zero, 51 | .alpha_equation = .add, 52 | }; 53 | 54 | pub const BlendMin = BlendMode{ 55 | .color_src_factor = .one, 56 | .color_dst_factor = .one, 57 | .color_equation = .min, 58 | .alpha_src_factor = .one, 59 | .alpha_dst_factor = .one, 60 | .alpha_equation = .min, 61 | }; 62 | 63 | pub const BlendMax = BlendMode{ 64 | .color_src_factor = .one, 65 | .color_dst_factor = .one, 66 | .color_equation = .max, 67 | .alpha_src_factor = .one, 68 | .alpha_dst_factor = .one, 69 | .alpha_equation = .max, 70 | }; 71 | 72 | pub const BlendNone = BlendMode{ 73 | .color_src_factor = .one, 74 | .color_dst_factor = .zero, 75 | .color_equation = .add, 76 | .alpha_src_factor = .one, 77 | .alpha_dst_factor = .zero, 78 | .alpha_equation = .add, 79 | }; 80 | 81 | const sfBlendMode = @import("../root.zig").c.sfBlendMode; 82 | /// Bitcasts this blendmode to the csfml struct 83 | /// For inner workings 84 | pub fn _toCSFML(self: BlendMode) sfBlendMode { 85 | return @as(sfBlendMode, @bitCast(self)); 86 | } 87 | 88 | color_src_factor: Factor, 89 | color_dst_factor: Factor, 90 | color_equation: Equation, 91 | alpha_src_factor: Factor, 92 | alpha_dst_factor: Factor, 93 | alpha_equation: Equation, 94 | }; 95 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Zig [SFML](https://www.sfml-dev.org/) Wrapper 2 | 3 | ## A pretty interface to use CSFML in a way that looks Object-Oriented in zig! 4 | 5 | ### What this is 6 | 7 | This is a wrapper for CSFML. Theres no problem importing CSFML in Zig, but the resulting code can be a little bit messy. 8 | My goal is to make things close enough to SFML, with nice methods. 9 | 10 | This currently for Zig 0.12 (should work with 0.13) and CSFML 2.6.1. 11 | 12 | ### Adding to your project 13 | 14 | Add using Zig's package manager like so: `zig fetch --save https://github.com/Guigui220D/zig-sfml-wrapper/archive/d5272051a937c8a3756bb96eab8276b76a271de4.tar.gz` (replace the commit hash if you want an other version). 15 | 16 | Add this to your exe compile in `build.zig`: 17 | 18 | ```zig 19 | const sfml = @import("sfml"); // AT THE TOP OF THE FILE 20 | 21 | exe.root_module.addImport("sfml", b.dependency("sfml", .{}).module("sfml")); 22 | sfml.link(exe); 23 | ``` 24 | 25 | > **Note:** Your project must know the location of the CSFML2.6.1 include, lib, and binaries directories for successful building and running. For more information, please refer to the wiki 👇 26 | 27 | Check the [wiki](../../wiki) for more details on how to compile your project or the examples. 28 | 29 | ### Small example 30 | 31 | This is a small example of how you use this sfml wrapper: 32 | 33 | ```zig 34 | //! This is a translation of the c++ code the sfml website gives you to test if SFML works 35 | //! for instance, in this page: https://www.sfml-dev.org/tutorials/2.6/start-vc.php 36 | 37 | const sf = struct { 38 | const sfml = @import("sfml"); 39 | usingnamespace sfml; 40 | usingnamespace sfml.graphics; 41 | }; 42 | 43 | pub fn main() !void { 44 | var window = try sf.RenderWindow.createDefault(.{ .x = 200, .y = 200 }, "SFML works!"); 45 | defer window.destroy(); 46 | 47 | var shape = try sf.CircleShape.create(100.0); 48 | defer shape.destroy(); 49 | shape.setFillColor(sf.Color.Green); 50 | 51 | while (window.isOpen()) { 52 | while (window.pollEvent()) |event| { 53 | if (event == .closed) 54 | window.close(); 55 | } 56 | 57 | window.clear(sf.Color.Black); 58 | window.draw(shape, null); 59 | window.display(); 60 | } 61 | } 62 | ``` 63 | 64 | ### Projects made with this wrapper 65 | 66 | Feel free to add your project to this list! 67 | 68 | - [Pong clone I made](https://github.com/Guigui220D/sfml-pong-zig) 69 | - [Minez](https://github.com/Guigui220D/minez) an arcade looking minecraft inspired mining game 70 | - [quran-warsh](https://github.com/muslimDevCommunity/quran-warsh) a desktop quran app 71 | 72 | ### How much is done 73 | 74 | Most of the classes are wrapped and you should be able to write games with this wrapper. 75 | The network module is a recent addition and does not contain all classes yet (HTTP, FTP, ...). 76 | Threads are not available yet. 77 | -------------------------------------------------------------------------------- /src/examples/heat_haze.zig: -------------------------------------------------------------------------------- 1 | //! This is a translation of the code present on this page: https://github.com/SFML/SFML/wiki/Source%3A-HeatHazeShader 2 | //! This example is here to show shaders work 3 | //! Original code by binary1248 4 | 5 | const sf = struct { 6 | const sfml = @import("sfml"); 7 | usingnamespace sfml; 8 | usingnamespace sfml.graphics; 9 | usingnamespace sfml.window; 10 | usingnamespace sfml.system; 11 | }; 12 | 13 | pub fn main() !void { 14 | var window = try sf.RenderWindow.createDefault(.{ .x = 600, .y = 600 }, "Heat"); 15 | defer window.destroy(); 16 | window.setVerticalSyncEnabled(true); 17 | 18 | var object_texture = try sf.Texture.createFromFile("res/cute_image.png"); 19 | defer object_texture.destroy(); 20 | 21 | var object = try sf.Sprite.createFromTexture(object_texture); 22 | defer object.destroy(); 23 | //object.setScale(.{ .x = 0.5, .y = 0.5 }); 24 | 25 | var distortion_map = try sf.Texture.createFromFile("res/distortion_map.png"); 26 | defer distortion_map.destroy(); 27 | distortion_map.setRepeated(true); 28 | distortion_map.setSmooth(true); 29 | 30 | var render_texture = try sf.RenderTexture.create(.{ .x = 400, .y = 300 }); 31 | defer render_texture.destroy(); 32 | 33 | var sprite = try sf.Sprite.createFromTexture(render_texture.getTexture()); 34 | sprite.setPosition(.{ .x = 100, .y = 150 }); 35 | 36 | //var shader = try sf.Shader.createFromFile(null, null, "heat_shader.fs"); 37 | var shader = try sf.Shader.createFromFile(null, null, "res/heat_shader.fs"); 38 | defer shader.destroy(); 39 | shader.setUniform("currentTexture", sf.Shader.CurrentTexture); 40 | shader.setUniform("distortionMapTexture", distortion_map); 41 | 42 | var distortion_factor: f32 = 0.05; 43 | var rise_factor: f32 = 2; 44 | 45 | var clock = try sf.Clock.create(); 46 | defer clock.destroy(); 47 | 48 | while (true) { 49 | while (window.pollEvent()) |event| { 50 | switch (event) { 51 | .closed => return, 52 | .key_pressed => |kp| { 53 | switch (kp.code) { 54 | .escape => return, 55 | .up => distortion_factor *= 2, 56 | .down => distortion_factor /= 2, 57 | .right => rise_factor *= 2, 58 | .left => rise_factor /= 2, 59 | else => {}, 60 | } 61 | }, 62 | else => {}, 63 | } 64 | } 65 | 66 | shader.setUniform("time", clock.getElapsedTime().asSeconds()); 67 | shader.setUniform("distortionFactor", distortion_factor); 68 | shader.setUniform("riseFactor", rise_factor); 69 | 70 | render_texture.clear(sf.Color.Black); 71 | render_texture.draw(object, null); 72 | render_texture.display(); 73 | 74 | window.clear(sf.Color.Black); 75 | window.draw(sprite, sf.RenderStates{ .shader = shader }); 76 | window.display(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/graphics/VertexBuffer.zig: -------------------------------------------------------------------------------- 1 | //! Define a set of one or more 2D primitives. The vertices are stored in the graphic memory. 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | const VertexBuffer = @This(); 6 | 7 | pub const Usage = enum(c_uint) { static = 0, dynamic = 1, stream = 2 }; 8 | 9 | // Constructors/destructors 10 | 11 | /// Creates a vertex buffer of a given size. Specify its usage and the primitive type. 12 | pub fn createFromSlice(vertices: []const sf.graphics.Vertex, primitive: sf.graphics.PrimitiveType, usage: Usage) !VertexBuffer { 13 | const ptr = sf.c.sfVertexBuffer_create(@as(c_uint, @truncate(vertices.len)), @intFromEnum(primitive), @intFromEnum(usage)) orelse return sf.Error.nullptrUnknownReason; 14 | if (sf.c.sfVertexBuffer_update(ptr, @as([*]const sf.c.sfVertex, @ptrCast(@alignCast(vertices.ptr))), @as(c_uint, @truncate(vertices.len)), 0) != 1) 15 | return sf.Error.resourceLoadingError; 16 | return VertexBuffer{ ._ptr = ptr }; 17 | } 18 | 19 | /// Destroyes this vertex buffer 20 | pub fn destroy(self: *VertexBuffer) void { 21 | sf.c.sfVertexBuffer_destroy(self._ptr); 22 | self._ptr = undefined; 23 | } 24 | 25 | // Getters/setters and methods 26 | 27 | /// Updates the vertex buffer with new data 28 | pub fn updateFromSlice(self: VertexBuffer, vertices: []const sf.graphics.Vertex) !void { 29 | if (sf.c.sfVertexBuffer_update(self._ptr, @as([*]const sf.c.sfVertex, @ptrCast(@alignCast(vertices.ptr))), @as(c_uint, @truncate(vertices.len)), 0) != 1) 30 | return sf.Error.resourceLoadingError; 31 | } 32 | 33 | /// Gets the vertex count of this vertex buffer 34 | pub fn getVertexCount(self: VertexBuffer) usize { 35 | return sf.c.sfVertexBuffer_getVertexCount(self._ptr); 36 | } 37 | 38 | /// Gets the primitive type of this vertex buffer 39 | pub fn getPrimitiveType(self: VertexBuffer) sf.graphics.PrimitiveType { 40 | return @as(sf.graphics.PrimitiveType, @enumFromInt(sf.c.sfVertexBuffer_getPrimitiveType(self._ptr))); 41 | } 42 | 43 | /// Gets the usage of this vertex buffer 44 | pub fn getUsage(self: VertexBuffer) Usage { 45 | return @as(Usage, @enumFromInt(sf.c.sfVertexBuffer_getUsage(self._ptr))); 46 | } 47 | 48 | /// Tells whether or not vertex buffers are available in the system 49 | pub fn isAvailable() bool { 50 | return sf.c.sfVertexBuffer_isAvailable() != 0; 51 | } 52 | 53 | pub const draw_suffix = "VertexBuffer"; 54 | 55 | /// Pointer to the csfml structure 56 | _ptr: *sf.c.sfVertexBuffer, 57 | 58 | test "VertexBuffer: sane getters and setters" { 59 | const tst = @import("std").testing; 60 | 61 | const va_slice = [_]sf.graphics.Vertex{ 62 | .{ .position = .{ .x = -1, .y = 0 }, .color = sf.graphics.Color.Red }, 63 | .{ .position = .{ .x = 1, .y = 0 }, .color = sf.graphics.Color.Green }, 64 | .{ .position = .{ .x = -1, .y = 1 }, .color = sf.graphics.Color.Blue }, 65 | }; 66 | var va = try createFromSlice(va_slice[0..], sf.graphics.PrimitiveType.triangles, Usage.static); 67 | defer va.destroy(); 68 | 69 | try tst.expectEqual(@as(usize, 3), va.getVertexCount()); 70 | try tst.expectEqual(sf.graphics.PrimitiveType.triangles, va.getPrimitiveType()); 71 | try tst.expectEqual(Usage.static, va.getUsage()); 72 | } 73 | -------------------------------------------------------------------------------- /src/network/SocketSelector.zig: -------------------------------------------------------------------------------- 1 | //! Multiplexer that allows to read from multiple sockets. 2 | 3 | const sf = struct { 4 | pub usingnamespace @import("../root.zig"); 5 | pub usingnamespace sf.network; 6 | }; 7 | 8 | const SocketSelector = @This(); 9 | 10 | // Constructor/destructor 11 | 12 | /// Creates a new socket selector 13 | pub fn create() SocketSelector { 14 | const sock = sf.c.sfSocketSelector_create(); 15 | if (sock) |s| { 16 | return SocketSelector{ ._ptr = s }; 17 | } else return sf.Error.nullptrUnknownReason; 18 | } 19 | /// Destroys this socket selector 20 | pub fn destroy(self: *SocketSelector) void { 21 | sf.c.sfSocketSelector_destroy(self._ptr); 22 | } 23 | /// Copies this socket selector 24 | pub fn copy(self: SocketSelector) SocketSelector { 25 | const sock = sf.c.sfSocketSelector_copy(self._ptr); 26 | if (sock) |s| { 27 | return SocketSelector{ ._ptr = s }; 28 | } else return sf.Error.nullptrUnknownReason; 29 | } 30 | 31 | // Methods 32 | 33 | /// Adds a socket to the selector 34 | pub fn addSocket(self: *SocketSelector, socket: anytype) void { 35 | switch (@TypeOf(socket)) { 36 | sf.UdpSocket => sf.c.sfSocketSelector_addUdpSocket(self._ptr, socket._ptr), 37 | sf.TcpSocket => sf.c.sfSocketSelector_addTcpSocket(self._ptr, socket._ptr), 38 | sf.TcpListener => sf.c.sfSocketSelector_addTcpListener(self._ptr, socket._ptr), 39 | else => @compileError("Socket has to be a tcp socket, tcp listener or udp socket"), 40 | } 41 | } 42 | /// Removes a socket from the selector 43 | pub fn removeSocket(self: *SocketSelector, socket: anytype) void { 44 | switch (@TypeOf(socket)) { 45 | sf.UdpSocket => sf.c.sfSocketSelector_removeUdpSocket(self._ptr, socket._ptr), 46 | sf.TcpSocket => sf.c.sfSocketSelector_removeTcpSocket(self._ptr, socket._ptr), 47 | sf.TcpListener => sf.c.sfSocketSelector_removeTcpListener(self._ptr, socket._ptr), 48 | else => @compileError("Socket has to be a tcp socket, tcp listener or udp socket"), 49 | } 50 | } 51 | /// Removes all sockets from the selector 52 | pub fn clear(self: *SocketSelector) void { 53 | sf.c.sfSocketSelector_clear(self._ptr); 54 | } 55 | 56 | /// Wait until one of the sockets is ready to receive something (or timeout) 57 | /// Returns true if one of the sockets is ready and false if timeout 58 | pub fn wait(self: SocketSelector, timeout: sf.system.Time) bool { 59 | const time = timeout orelse sf.system.Time{ .us = 0 }; 60 | const ret = sf.c.sfSocketSelector_wait(self._ptr, time._toCSFML()); 61 | return ret != 0; 62 | } 63 | 64 | /// Checks one socket to know if it's ready to read data 65 | pub fn isSocketReady(self: SocketSelector, socket: anytype) bool { 66 | switch (@TypeOf(socket)) { 67 | sf.UdpSocket => sf.c.sfSocketSelector_isUdpSocketReady(self._ptr, socket._ptr), 68 | sf.TcpSocket => sf.c.sfSocketSelector_isTcpSocketReady(self._ptr, socket._ptr), 69 | sf.TcpListener => sf.c.sfSocketSelector_isTcpListenerReady(self._ptr, socket._ptr), 70 | else => @compileError("Socket has to be a tcp socket, tcp listener or udp socket"), 71 | } 72 | } 73 | 74 | /// Pointer to the csfml structure 75 | _ptr: *sf.c.sfSocketSelector, 76 | -------------------------------------------------------------------------------- /src/window/Cursor.zig: -------------------------------------------------------------------------------- 1 | //! Cursor defines the appearance of a system cursor. 2 | 3 | const std = @import("std"); 4 | const sf = struct { 5 | const sfml = @import("../root.zig"); 6 | pub usingnamespace sfml; 7 | pub usingnamespace sfml.system; 8 | }; 9 | 10 | const Cursor = @This(); 11 | 12 | /// Enumeration of the native system cursor types 13 | pub const Type = enum(c_uint) { 14 | /// Arrow cursor (default) 15 | arrow, 16 | /// Busy arrow cursor 17 | arrow_wait, 18 | /// Busy cursor 19 | wait, 20 | /// I-beam, cursor when hovering over a field allowing text entry 21 | text, 22 | /// Pointing hand cursor 23 | hand, 24 | /// Horizontal double arrow cursor 25 | size_horizontal, 26 | /// Vertical double arrow cursor 27 | size_vertical, 28 | /// Double arrow cursor going from top-left to bottom-right 29 | size_top_left_bottom_right, 30 | /// Double arrow cursor going from bottom-left to top-right 31 | size_bottom_left_top_right, 32 | /// Left arrow cursor on Linux, same as sizeHorizontal on other platforms 33 | size_left, 34 | /// Right arrow cursor on Linux, same as sizeHorizontal on other platforms 35 | size_right, 36 | /// Up arrow cursor on Linux, same as sizeVertical on other platforms 37 | size_top, 38 | /// Down arrow cursor on Linux, same as sizeVertical on other platforms 39 | size_bottom, 40 | /// Top-left arrow cursor on Linux, same as sizeTopLeftBottomRight on other platforms 41 | size_top_left, 42 | /// Bottom-right arrow cursor on Linux, same as sizeTopLeftBottomRight on other platforms 43 | size_bottom_right, 44 | /// Bottom-left arrow cursor on Linux, same as sizeBottomLeftTopRight on other platforms 45 | size_bottom_left, 46 | /// Top-right arrow cursor on Linux, same as sizeBottomLeftTopRight on other platforms 47 | size_top_right, 48 | /// Combination of sizeHorizontal and sizeVertical 49 | size_all, 50 | /// Crosshair cursor 51 | cross, 52 | /// Help cursor 53 | help, 54 | /// Action not allowed cursor 55 | not_allowed, 56 | }; 57 | 58 | // Constructors and destructors 59 | 60 | /// Create a cursor with the provided image 61 | pub fn createFromPixels(pixels: []const u8, size: sf.Vector2u, hotspot: sf.Vector2u) !Cursor { 62 | // Check slice length 63 | if (pixels.len != size.x * size.y * @sizeOf(u32)) 64 | return sf.Error.wrongDataSize; 65 | // Create the cursor 66 | const cursor = sf.c.sfCursor_createFromPixels(@ptrCast(pixels.ptr), size._toCSFML(), hotspot._toCSFML()); 67 | if (cursor == null) 68 | return sf.Error.nullptrUnknownReason; 69 | return Cursor{ ._ptr = cursor.? }; 70 | } 71 | /// Create a native system cursor 72 | pub fn createFromSystem(cursor_type: Type) !Cursor { 73 | const cursor = sf.c.sfCursor_createFromSystem(@intFromEnum(cursor_type)); 74 | if (cursor == null) 75 | return sf.Error.nullptrUnknownReason; 76 | return Cursor{ ._ptr = cursor.? }; 77 | } 78 | 79 | /// Destroy a cursor 80 | pub fn destroy(self: *Cursor) void { 81 | sf.c.sfCursor_destroy(self._ptr); 82 | self._ptr = undefined; 83 | } 84 | 85 | /// Pointer to the csfml structure 86 | _ptr: *sf.c.sfCursor, 87 | 88 | test "cursor: createFromSystem" { 89 | var cursor = try Cursor.createFromSystem(.arrow); 90 | defer cursor.destroy(); 91 | } 92 | -------------------------------------------------------------------------------- /src/graphics/Font.zig: -------------------------------------------------------------------------------- 1 | //! Class for loading and manipulating character fonts. 2 | 3 | const sf = @import("../root.zig"); 4 | 5 | const Font = @This(); 6 | 7 | // Constructor/destructor 8 | 9 | /// Loads a font from a file 10 | pub fn createFromFile(path: [:0]const u8) !Font { 11 | const font = sf.c.sfFont_createFromFile(path); 12 | if (font) |f| { 13 | return Font{ ._ptr = f }; 14 | } else return sf.Error.resourceLoadingError; 15 | } 16 | /// Loads a font from a file in memory 17 | pub fn createFromMemory(data: []const u8) !Font { 18 | const font = sf.c.sfFont_createFromMemory(@as(?*const anyopaque, @ptrCast(data.ptr)), data.len); 19 | if (font) |f| { 20 | return Font{ ._ptr = f }; 21 | } else return sf.Error.resourceLoadingError; 22 | } 23 | /// Destroys a font 24 | pub fn destroy(self: *Font) void { 25 | sf.c.sfFont_destroy(self._ptr); 26 | self._ptr = undefined; 27 | } 28 | 29 | /// Gets the family name of this font 30 | /// Normally, this is done through getInfo, but as info only contains this data, this makes more sense 31 | pub fn getFamily(self: Font) [*:0]const u8 { 32 | return sf.c.sfFont_getInfo(self._ptr).family; 33 | } 34 | 35 | /// Gets the kerning offset of two glyphs 36 | pub fn getKerning(self: Font, first: u32, second: u32, character_size: usize) f32 { 37 | return sf.c.sfFont_getKerning(self._ptr, first, second, @as(c_uint, @intCast(character_size))); 38 | } 39 | 40 | /// Gets the default spacing between two lines 41 | pub fn getLineSpacing(self: Font, character_size: usize) f32 { 42 | return sf.c.sfFont_getLineSpacing(self._ptr, @as(c_uint, @intCast(character_size))); 43 | } 44 | 45 | /// Gets the vertical offset of the underline 46 | pub fn getUnderlinePosition(self: Font, character_size: usize) f32 { 47 | return sf.c.sfFont_getUnderlinePosition(self._ptr, @as(c_uint, @intCast(character_size))); 48 | } 49 | /// Gets the underline thickness 50 | pub fn getUnderlineThickness(self: Font, character_size: usize) f32 { 51 | return sf.c.sfFont_getUnderlineThickness(self._ptr, @as(c_uint, @intCast(character_size))); 52 | } 53 | 54 | /// Check if the font has the following glyph 55 | pub fn hasGlyph(self: Font, codepoint: u21) bool { 56 | return sf.c.sfFont_hasGlyph(self._ptr, @intCast(codepoint)) != 0; 57 | } 58 | 59 | /// Enable or disable the smooth filter 60 | pub fn setSmooth(self: *Font, smooth: bool) void { 61 | sf.c.sfFont_setSmooth(self._ptr, @intFromBool(smooth)); 62 | } 63 | /// Tell whether the smooth filter is enabled or disabled 64 | pub fn isSmooth(self: Font) bool { 65 | return sf.c.sfFont_isSmooth(self._ptr) != 0; 66 | } 67 | 68 | // TODO 69 | //pub const getGlyph = @compileError("Function is not implemented yet."); 70 | //pub const initFromStream = @compileError("Function is not implemented yet."); 71 | 72 | /// Pointer to the csfml font 73 | _ptr: *sf.c.sfFont, 74 | 75 | test "Font: sane getters and setters" { 76 | const std = @import("std"); 77 | const tst = std.testing; 78 | 79 | // TODO: is it a good idea to have a test rely on resources? 80 | var font = try createFromFile("res/arial.ttf"); 81 | defer font.destroy(); 82 | 83 | font.setSmooth(true); 84 | 85 | try tst.expect(std.mem.eql(u8, "Arial", std.mem.span(font.getFamily()))); 86 | // TODO: how to test that? 87 | _ = font.getLineSpacing(12); 88 | _ = font.getUnderlinePosition(12); 89 | _ = font.getUnderlineThickness(12); 90 | _ = font.getKerning('a', 'b', 12); 91 | 92 | try tst.expect(font.hasGlyph('a')); 93 | try tst.expect(font.isSmooth()); 94 | } 95 | -------------------------------------------------------------------------------- /src/graphics/View.zig: -------------------------------------------------------------------------------- 1 | //! 2D camera that defines what region is shown on screen. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const View = @This(); 11 | 12 | /// Creates a view from a rectangle 13 | pub fn fromRect(rect: sf.FloatRect) View { 14 | var ret: View = undefined; 15 | ret.center = rect.getCorner(); 16 | ret.size = rect.getSize(); 17 | ret.center = ret.center.add(ret.size.scale(0.5)); 18 | ret.viewport = sf.FloatRect.init(0, 0, 1, 1); 19 | return ret; 20 | } 21 | 22 | /// Creates a view from a CSFML object 23 | /// This is mainly for the inner workings of this wrapper 24 | pub fn _fromCSFML(view: *const sf.c.sfView) View { 25 | var ret: View = undefined; 26 | ret.center = sf.Vector2f._fromCSFML(sf.c.sfView_getCenter(view)); 27 | ret.size = sf.Vector2f._fromCSFML(sf.c.sfView_getSize(view)); 28 | ret.viewport = sf.FloatRect._fromCSFML(sf.c.sfView_getViewport(view)); 29 | return ret; 30 | } 31 | 32 | /// Creates a CSFML view from this view 33 | /// This is mainly for the inner workings of this wrapper 34 | /// The resulting view must be destroyed! 35 | pub fn _toCSFML(self: View) *sf.c.sfView { 36 | const view = sf.c.sfView_create().?; 37 | sf.c.sfView_setCenter(view, self.center._toCSFML()); 38 | sf.c.sfView_setSize(view, self.size._toCSFML()); 39 | sf.c.sfView_setViewport(view, self.viewport._toCSFML()); 40 | return view; 41 | } 42 | 43 | pub fn getRect(self: View) sf.FloatRect { 44 | return sf.FloatRect.init( 45 | self.center.x - self.size.x / 2, 46 | self.center.y - self.size.y / 2, 47 | self.size.x, 48 | self.size.y, 49 | ); 50 | } 51 | 52 | pub fn setSize(self: *View, size: sf.Vector2f) void { 53 | self.size = size; 54 | } 55 | 56 | pub fn setCenter(self: *View, center: sf.Vector2f) void { 57 | self.center = center; 58 | } 59 | 60 | pub fn zoom(self: *View, factor: f32) void { 61 | self.size = .{ .x = self.size.x * factor, .y = self.size.y * factor }; 62 | } 63 | 64 | // View variables 65 | /// Center of the view, what this view "looks" at 66 | center: sf.Vector2f, 67 | /// Width and height of the view 68 | size: sf.Vector2f, 69 | /// The viewport of this view 70 | viewport: sf.FloatRect, 71 | 72 | test "view: from rect" { 73 | const tst = @import("std").testing; 74 | 75 | // Testing if the view from rect initialization works 76 | var rect = sf.FloatRect.init(10, -15, 700, 600); 77 | 78 | const view = sf.c.sfView_createFromRect(rect._toCSFML()); 79 | defer sf.c.sfView_destroy(view); 80 | 81 | var view2 = View.fromRect(rect); 82 | 83 | const center = sf.Vector2f._fromCSFML(sf.c.sfView_getCenter(view)); 84 | const size = sf.Vector2f._fromCSFML(sf.c.sfView_getSize(view)); 85 | 86 | try tst.expectApproxEqAbs(center.x, view2.center.x, 0.00001); 87 | try tst.expectApproxEqAbs(center.y, view2.center.y, 0.00001); 88 | try tst.expectApproxEqAbs(size.x, view2.size.x, 0.00001); 89 | try tst.expectApproxEqAbs(size.y, view2.size.y, 0.00001); 90 | 91 | var rect_ret = view2.getRect(); 92 | 93 | try tst.expectApproxEqAbs(rect.left, rect_ret.left, 0.00001); 94 | try tst.expectApproxEqAbs(rect.top, rect_ret.top, 0.00001); 95 | try tst.expectApproxEqAbs(rect.width, rect_ret.width, 0.00001); 96 | try tst.expectApproxEqAbs(rect.height, rect_ret.height, 0.00001); 97 | 98 | view2.setCenter(.{ .x = 400, .y = 300 }); 99 | view2.setSize(.{ .x = 800, .y = 600 }); 100 | rect_ret = view2.getRect(); 101 | try tst.expectApproxEqAbs(@as(f32, 0), rect_ret.left, 0.00001); 102 | try tst.expectApproxEqAbs(@as(f32, 0), rect_ret.top, 0.00001); 103 | try tst.expectApproxEqAbs(@as(f32, 800), rect_ret.width, 0.00001); 104 | try tst.expectApproxEqAbs(@as(f32, 600), rect_ret.height, 0.00001); 105 | } 106 | -------------------------------------------------------------------------------- /src/network/IpAddress.zig: -------------------------------------------------------------------------------- 1 | //! Encapsulate an IPv4 network address. 2 | 3 | const std = @import("std"); 4 | const sf = @import("../root.zig"); 5 | 6 | const IpAddress = @This(); 7 | 8 | // Const addresses 9 | // TODO: make those comptime 10 | /// Any address or no address 11 | pub fn any() IpAddress { 12 | return IpAddress.initFromInt(0); 13 | } 14 | pub const none = any; 15 | /// Broadcast address 16 | pub fn broadcast() IpAddress { 17 | return IpAddress.initFromInt(0xFFFFFFFF); 18 | } 19 | /// Loopback 20 | pub fn localhost() IpAddress { 21 | return IpAddress.init(127, 0, 0, 1); 22 | } 23 | 24 | // Constructor/destructor 25 | 26 | /// Inits an ip address from bytes 27 | pub fn init(a: u8, b: u8, c: u8, d: u8) IpAddress { 28 | return .{ ._ip = sf.c.sfIpAddress_fromBytes(a, b, c, d) }; 29 | } 30 | /// Inits an ip address from an integer 31 | pub fn initFromInt(int: u32) IpAddress { 32 | return .{ ._ip = sf.c.sfIpAddress_fromInteger(int) }; 33 | } 34 | /// Inits an ip address from a string (network name or ip) 35 | pub fn initFromString(str: [*:0]const u8) IpAddress { 36 | return .{ ._ip = sf.c.sfIpAddress_fromString(str) }; 37 | } 38 | 39 | // Local/global addresses 40 | 41 | /// Gets the local address of this pc (takes no time) 42 | pub fn getLocalAddress() IpAddress { 43 | return .{ ._ip = sf.c.sfIpAddress_getLocalAddress() }; 44 | } 45 | /// Gets the public address of this pc (from outside) 46 | /// Takes time because it needs to contact an internet server 47 | /// Specify a timeout in case it takes too much time 48 | pub fn getPublicAddress(timeout: ?sf.system.Time) !IpAddress { 49 | const time = timeout orelse sf.system.Time{ .us = 0 }; 50 | const ip = IpAddress{ ._ip = sf.c.sfIpAddress_getPublicAddress(time._toCSFML()) }; 51 | if (ip.equals(IpAddress.none())) 52 | return sf.Error.timeout; 53 | return ip; 54 | } 55 | 56 | // Compare 57 | 58 | /// Compares two ip addresses 59 | pub fn equals(self: IpAddress, other: IpAddress) bool { 60 | return std.mem.eql(u8, self.bytes(), other.bytes()); 61 | } 62 | /// Gets the full slice of the contents 63 | fn bytes(self: IpAddress) []const u8 { 64 | return @as(*const [16]u8, @ptrCast(&self._ip))[0..]; 65 | } 66 | 67 | // Getter 68 | 69 | /// Gets the ip as it is stored but as a slice 70 | pub fn toString(self: IpAddress) []const u8 { 71 | var slice = self.bytes(); 72 | for (slice, 0..) |s, i| { 73 | if (s == 0) { 74 | slice.len = i; 75 | break; 76 | } 77 | } 78 | return slice; 79 | } 80 | 81 | /// Gets the ip as an int 82 | pub fn toInt(self: IpAddress) u32 { 83 | // TODO: This is a workaround to 84 | //return sf.c.sfIpAddress_toInteger(self._ip); 85 | const str = self.toString(); 86 | var iter = std.mem.split(u8, str, "."); 87 | const a = (std.fmt.parseInt(u32, iter.next().?, 10) catch unreachable) << 24; 88 | const b = (std.fmt.parseInt(u32, iter.next().?, 10) catch unreachable) << 16; 89 | const c = (std.fmt.parseInt(u32, iter.next().?, 10) catch unreachable) << 8; 90 | const d = (std.fmt.parseInt(u32, iter.next().?, 10) catch unreachable) << 0; 91 | if (iter.next()) |_| 92 | unreachable; 93 | return a | b | c | d; 94 | } 95 | 96 | /// Prints this ip address as a string 97 | pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { 98 | _ = options; 99 | _ = fmt; 100 | 101 | try writer.writeAll(self.toString()); 102 | } 103 | 104 | /// Csfml structure 105 | _ip: sf.c.sfIpAddress, 106 | 107 | test "ipaddress" { 108 | const tst = std.testing; 109 | 110 | var ip = IpAddress.init(0x01, 0x23, 0x45, 0x67); 111 | try tst.expectEqual(@as(u32, 0x01234567), ip.toInt()); 112 | ip = IpAddress.initFromInt(0xabababab); 113 | try tst.expect(ip.equals(IpAddress.init(0xab, 0xab, 0xab, 0xab))); 114 | ip = IpAddress.initFromString("localhost"); 115 | try tst.expectEqualStrings("127.0.0.1", ip.toString()); 116 | 117 | //toInt(); 118 | 119 | _ = getLocalAddress(); 120 | _ = getPublicAddress(sf.system.Time.microseconds(1)) catch {}; 121 | } 122 | -------------------------------------------------------------------------------- /src/network/TcpSocket.zig: -------------------------------------------------------------------------------- 1 | //! Socket using the TCP protocol 2 | 3 | const std = @import("std"); 4 | const sf = struct { 5 | pub usingnamespace @import("../root.zig"); 6 | pub usingnamespace sf.network; 7 | }; 8 | 9 | const TcpSocket = @This(); 10 | 11 | // Constructor/destructor 12 | 13 | /// Creates a new udp socket 14 | pub fn create() !TcpSocket { 15 | const sock = sf.c.sfTcpSocket_create(); 16 | if (sock) |s| { 17 | return TcpSocket{ ._ptr = s }; 18 | } else return sf.Error.nullptrUnknownReason; 19 | } 20 | /// Destroys this socket 21 | pub fn destroy(self: *TcpSocket) void { 22 | sf.c.sfTcpSocket_destroy(self._ptr); 23 | } 24 | 25 | // Methods 26 | 27 | /// Enables or disables blocking mode (true for blocking) 28 | /// In blocking mode, receive waits for data 29 | pub fn setBlocking(self: *TcpSocket, blocking: bool) void { 30 | sf.c.sfTcpSocket_setBlocking(self._ptr, @intFromBool(blocking)); 31 | } 32 | /// Tells whether or not the socket is in blocking mode 33 | pub fn isBlocking(self: TcpSocket) bool { 34 | return sf.c.sfTcpSocket_isBlocking(self._ptr) != 0; 35 | } 36 | 37 | /// Gets the port this socket is bound to (null for no port) 38 | pub fn getLocalPort(self: TcpSocket) ?u16 { 39 | const port = sf.c.sfTcpSocket_getLocalPort(self._ptr); 40 | return if (port == 0) null else port; 41 | } 42 | /// Gets the address of the other tcp socket that is currently connected 43 | pub fn getRemote(self: TcpSocket) error{notConnected}!sf.IpAndPort { 44 | const port = sf.c.sfTcpSocket_getRemotePort(self._ptr); 45 | if (port == 0) 46 | return error.notConnected; 47 | const ip = sf.c.sfTcpSocket_getRemoteAddress(self._ptr); 48 | const ip_and_port = sf.IpAndPort{ .ip = .{ ._ip = ip }, .port = port }; 49 | std.debug.assert(!ip_and_port.ip.equals(sf.IpAddress.none())); 50 | return ip_and_port; 51 | } 52 | 53 | /// Connects to a server (the server typically has a Tcp Listener) 54 | /// To connect to clients, use a tcp listener instead and wait for connections 55 | pub fn connect(self: *TcpSocket, remote: sf.IpAndPort, timeout: sf.system.Time) sf.Socket.Error!void { 56 | const code = sf.c.sfTcpSocket_connect(self._ptr, remote.ip._ip, remote.port, timeout._toCSFML()); 57 | try sf.Socket._codeToErr(code); 58 | } 59 | /// Disconnects from the remote 60 | pub fn disconnect(self: *TcpSocket) void { 61 | sf.c.sfTcpSocket_disconnect(self._ptr); 62 | } 63 | 64 | /// Sends raw data to the remote 65 | pub fn send(self: *TcpSocket, data: []const u8) sf.Socket.Error!void { 66 | const code = sf.c.sfTcpSocket_send(self._ptr, data.ptr, data.len); 67 | try sf.Socket._codeToErr(code); 68 | } 69 | /// Sends part of the buffer to the remote 70 | /// Returns the slice of the rest of the data that hasn't been sent, what is left to send 71 | pub fn sendPartial(self: *TcpSocket, data: []const u8) sf.Socket.Error![]const u8 { 72 | var sent: usize = undefined; 73 | var ret = data; 74 | const code = sf.c.sfTcpSocket_sendPartial(self._ptr, data.ptr, data.len, &sent); 75 | try sf.Socket._codeToErr(code); 76 | ret.ptr += sent; 77 | ret.len -= sent; 78 | return ret; 79 | } 80 | /// Sends a packet to the remote 81 | pub fn sendPacket(self: *TcpSocket, packet: sf.Packet) sf.Socket.Error!void { 82 | const code = sf.c.sfTcpSocket_sendPacket(self._ptr, packet._ptr); 83 | try sf.Socket._codeToErr(code); 84 | } 85 | 86 | /// Receives raw data from the remote 87 | /// Pass in a buffer large enough 88 | /// Returns the slice of the received data 89 | pub fn receive(self: *TcpSocket, buf: []u8) sf.Socket.Error![]const u8 { 90 | var size: usize = undefined; 91 | const code = sf.c.sfUdpSocket_receive(self._ptr, buf.ptr, buf.len, &size); 92 | try sf.Socket._codeToErr(code); 93 | return buf[0..size]; 94 | } 95 | // TODO: consider receiveAlloc 96 | // TODO: should this return its own new packet 97 | /// Receives a packet from the remote 98 | /// Pass the packet to fill with the data 99 | pub fn receivePacket(self: *TcpSocket, packet: *sf.Packet) sf.Socket.Error!void { 100 | const code = sf.c.sfUdpSocket_receivePacket(self._ptr, packet._ptr); 101 | try sf.Socket._codeToErr(code); 102 | } 103 | 104 | /// Pointer to the csfml structure 105 | _ptr: *sf.c.sfTcpSocket, 106 | 107 | // TODO: write tests 108 | -------------------------------------------------------------------------------- /src/audio/sound_buffer.zig: -------------------------------------------------------------------------------- 1 | //! Storage for audio samples defining a sound. 2 | 3 | const sf = @import("../root.zig"); 4 | const std = @import("std"); 5 | 6 | pub const SoundBuffer = union(enum) { 7 | // Constructor/destructor 8 | /// Loads sound from a file 9 | pub fn createFromFile(path: [:0]const u8) !SoundBuffer { 10 | const sound = sf.c.sfSoundBuffer_createFromFile(path); 11 | if (sound) |s| { 12 | return SoundBuffer{ ._ptr = s }; 13 | } else return sf.Error.resourceLoadingError; 14 | } 15 | /// Loads sound from a file in memory 16 | pub fn createFromMemory(data: []const u8) !SoundBuffer { 17 | const sound = sf.c.sfSoundBuffer_createFromMemory(@as(?*const anyopaque, @ptrCast(data.ptr)), data.len); 18 | if (sound) |s| { 19 | return SoundBuffer{ ._ptr = s }; 20 | } else return sf.Error.resourceLoadingError; 21 | } 22 | /// Creates a sound buffer from sample data 23 | pub fn createFromSamples(samples: []const i16, channel_count: usize, sample_rate: usize) !SoundBuffer { 24 | const sound = sf.c.sfSoundBuffer_createFromSamples(@as([*c]const c_short, @ptrCast(samples.ptr)), samples.len, @as(c_uint, @intCast(channel_count)), @as(c_uint, @intCast(sample_rate))); 25 | if (sound == null) 26 | return sf.Error.resourceLoadingError; 27 | return SoundBuffer{ ._ptr = sound.? }; 28 | } 29 | 30 | // TODO 31 | //pub const initFromStream = @compileError("Function is not implemented yet."); 32 | 33 | /// Destroys this sound buffer 34 | /// You can only destroy non const sound buffers 35 | pub fn destroy(self: *SoundBuffer) void { 36 | std.debug.assert(self.* == ._ptr); 37 | sf.c.sfSoundBuffer_destroy(self._ptr); 38 | self._ptr = undefined; 39 | } 40 | 41 | // Getters / Setters 42 | 43 | /// Gets the duration of the sound 44 | pub fn getDuration(self: SoundBuffer) sf.system.Time { 45 | return sf.system.Time._fromCSFML(sf.c.sfSoundBuffer_getDuration(self._get())); 46 | } 47 | 48 | /// Gets the sample count of this sound 49 | pub fn getSampleCount(self: SoundBuffer) usize { 50 | return @as(usize, @intCast(sf.c.sfSoundBuffer_getSampleCount(self._get()))); 51 | } 52 | 53 | /// Gets the sample rate of this sound (n° of samples per second, often 44100) 54 | pub fn getSampleRate(self: SoundBuffer) usize { 55 | return @as(usize, @intCast(sf.c.sfSoundBuffer_getSampleRate(self._get()))); 56 | } 57 | 58 | /// Gets the channel count (2 is stereo for instance) 59 | pub fn getChannelCount(self: SoundBuffer) usize { 60 | return @as(usize, @intCast(sf.c.sfSoundBuffer_getChannelCount(self._get()))); 61 | } 62 | 63 | // Misc 64 | 65 | /// Save the sound buffer to an audio file 66 | pub fn saveToFile(self: SoundBuffer, path: [:0]const u8) !void { 67 | if (sf.c.sfSoundBuffer_saveToFile(self._get(), path) != 1) 68 | return sf.Error.savingInFileFailed; 69 | } 70 | 71 | /// Makes this sound bufer constant (I don't know why you would do that) 72 | pub fn makeConst(self: *SoundBuffer) void { 73 | self.* = SoundBuffer{ ._const_ptr = self._get() }; 74 | } 75 | 76 | /// Gets a const reference to this sound buffer 77 | pub fn getConst(self: SoundBuffer) SoundBuffer { 78 | var cpy = self; 79 | cpy.makeConst(); 80 | return cpy; 81 | } 82 | 83 | /// Gets a const pointer to this sound buffer 84 | /// For inner workings 85 | pub fn _get(self: SoundBuffer) *const sf.c.sfSoundBuffer { 86 | return switch (self) { 87 | ._ptr => self._ptr, 88 | ._const_ptr => self._const_ptr, 89 | }; 90 | } 91 | 92 | /// Pointer to the csfml texture 93 | _ptr: *sf.c.sfSoundBuffer, 94 | /// Const pointer to the csfml texture 95 | _const_ptr: *const sf.c.sfSoundBuffer, 96 | }; 97 | 98 | test "sound buffer: sane getter and setters" { 99 | const tst = std.testing; 100 | const allocator = std.heap.page_allocator; 101 | 102 | const samples = try allocator.alloc(i16, 44100 * 3); 103 | defer allocator.free(samples); 104 | 105 | var buffer = try SoundBuffer.createFromSamples(samples, 1, 44100); 106 | defer buffer.destroy(); 107 | 108 | try tst.expectApproxEqAbs(@as(f32, 3), buffer.getDuration().asSeconds(), 0.001); 109 | try tst.expectEqual(@as(usize, 44100 * 3), buffer.getSampleCount()); 110 | try tst.expectEqual(@as(usize, 44100), buffer.getSampleRate()); 111 | try tst.expectEqual(@as(usize, 1), buffer.getChannelCount()); 112 | } 113 | -------------------------------------------------------------------------------- /src/network/UdpSocket.zig: -------------------------------------------------------------------------------- 1 | //! Socket using the UDP protocol 2 | 3 | const sf = struct { 4 | pub usingnamespace @import("../root.zig"); 5 | pub usingnamespace sf.network; 6 | }; 7 | 8 | const UdpSocket = @This(); 9 | 10 | // Constructor/destructor 11 | 12 | /// Creates a new udp socket 13 | pub fn create() !UdpSocket { 14 | const sock = sf.c.sfUdpSocket_create(); 15 | if (sock) |s| { 16 | return UdpSocket{ ._ptr = s }; 17 | } else return sf.Error.nullptrUnknownReason; 18 | } 19 | /// Destroys this socket 20 | pub fn destroy(self: *UdpSocket) void { 21 | sf.c.sfUdpSocket_destroy(self._ptr); 22 | } 23 | 24 | // Methods 25 | 26 | /// Enables or disables blocking mode (true for blocking) 27 | /// In blocking mode, receive waits for data 28 | pub fn setBlocking(self: *UdpSocket, blocking: bool) void { 29 | sf.c.sfUdpSocket_setBlocking(self._ptr, @intFromBool(blocking)); 30 | } 31 | /// Tells whether or not the socket is in blocking mode 32 | pub fn isBlocking(self: UdpSocket) bool { 33 | return sf.c.sfUdpSocket_isBlocking(self._ptr) != 0; 34 | } 35 | /// Gets the port this socket is bound to (null for no port) 36 | pub fn getLocalPort(self: UdpSocket) ?u16 { 37 | const port = sf.c.sfUdpSocket_getLocalPort(self._ptr); 38 | return if (port == 0) null else port; 39 | } 40 | /// Binds the socket to a specified port and ip 41 | /// port: the port to bind to (null to let the os choose) 42 | /// ip: the interface to bind to (null for any interface) 43 | pub fn bind(self: *UdpSocket, port: ?u16, ip: ?sf.IpAddress) sf.Socket.Error!void { 44 | const p = port orelse 0; 45 | const i = ip orelse sf.IpAddress.any(); 46 | const code = sf.c.sfUdpSocket_bind(self._ptr, p, i._ip); 47 | try sf.Socket._codeToErr(code); 48 | } 49 | /// Unbinds the socket from the port it's bound to 50 | pub fn unbind(self: *UdpSocket) void { 51 | sf.c.sfUdpSocket_unbind(self._ptr); 52 | } 53 | /// Sends raw data to a recipient 54 | pub fn send(self: *UdpSocket, data: []const u8, remote: sf.IpAndPort) sf.Socket.Error!void { 55 | const code = sf.c.sfUdpSocket_send(self._ptr, data.ptr, data.len, remote.ip._ip, remote.port); 56 | try sf.Socket._codeToErr(code); 57 | } 58 | /// Sends a packet to a recipient 59 | pub fn sendPacket(self: *UdpSocket, packet: sf.Packet, remote: sf.IpAndPort) sf.Socket.Error!void { 60 | const code = sf.c.sfUdpSocket_sendPacket(self._ptr, packet._ptr, remote.ip._ip, remote.port); 61 | try sf.Socket._codeToErr(code); 62 | } 63 | 64 | /// Represents received data and a remote ip and port 65 | pub const ReceivedRaw = struct { data: []const u8, sender: sf.IpAndPort }; 66 | /// Receives raw data from a recipient 67 | /// Pass in a buffer large enough 68 | /// Returns the slice of the received data and the sender ip and port 69 | pub fn receive(self: *UdpSocket, buf: []u8) sf.Socket.Error!ReceivedRaw { 70 | var size: usize = undefined; 71 | var remote: sf.IpAndPort = undefined; 72 | const code = sf.c.sfUdpSocket_receive(self._ptr, buf.ptr, buf.len, &size, &remote.ip._ip, &remote.port); 73 | try sf.Socket._codeToErr(code); 74 | return ReceivedRaw{ .data = buf[0..size], .sender = remote }; 75 | } 76 | // TODO: consider receiveAlloc ? 77 | // TODO: should this return its own new packet? 78 | /// Receives a packet from a recipient 79 | /// Pass the packet to fill with the data 80 | /// Returns the sender ip and port 81 | pub fn receivePacket(self: *UdpSocket, packet: *sf.Packet) sf.Socket.Error!sf.IpAndPort { 82 | var remote: sf.IpAndPort = undefined; 83 | const code = sf.c.sfUdpSocket_receivePacket(self._ptr, packet._ptr, &remote.ip._ip, &remote.port); 84 | try sf.Socket._codeToErr(code); 85 | return remote; 86 | } 87 | /// Gets the max datagram size you can send 88 | pub fn getMaxDatagramSize() c_uint { 89 | return sf.c.sfUdpSocket_maxDatagramSize(); 90 | } 91 | 92 | /// Pointer to the csfml structure 93 | _ptr: *sf.c.sfUdpSocket, 94 | 95 | test "udp socket: dumb test" { 96 | const tst = @import("std").testing; 97 | 98 | var buf: [1024]u8 = undefined; 99 | var pack = try sf.Packet.create(); 100 | defer pack.destroy(); 101 | 102 | var sock = try UdpSocket.create(); 103 | defer sock.destroy(); 104 | 105 | sock.setBlocking(false); 106 | try tst.expect(!sock.isBlocking()); 107 | 108 | try sock.bind(null, null); 109 | const port = sock.getLocalPort().?; 110 | try tst.expect(port >= 49152); 111 | 112 | try tst.expectError(error.notReady, sock.receive(&buf)); 113 | 114 | sock.unbind(); 115 | try tst.expect(sock.getLocalPort() == null); 116 | try tst.expectError(error.otherError, sock.receivePacket(&pack)); 117 | 118 | const target = sf.IpAndPort{ .port = 1, .ip = sf.IpAddress.none() }; 119 | try tst.expectError(error.otherError, sock.sendPacket(pack, target)); 120 | try tst.expectError(error.otherError, sock.send(buf[0..10], target)); 121 | } 122 | -------------------------------------------------------------------------------- /src/graphics/Shader.zig: -------------------------------------------------------------------------------- 1 | //! Shader class 2 | 3 | const sf = @import("../root.zig"); 4 | const glsl = sf.graphics.glsl; 5 | 6 | const Shader = @This(); 7 | 8 | // Constructor/destructor 9 | 10 | /// Creates a shader object from shader files, you can omit some shader types by passing null 11 | pub fn createFromFile(vertex_shader_path: ?[:0]const u8, geometry_shader_path: ?[:0]const u8, fragment_shader_path: ?[:0]const u8) !Shader { 12 | const shader = sf.c.sfShader_createFromFile(if (vertex_shader_path) |vsp| @as([*c]const u8, @ptrCast(vsp)) else null, if (geometry_shader_path) |gsp| @as([*c]const u8, @ptrCast(gsp)) else null, if (fragment_shader_path) |fsp| @as([*c]const u8, @ptrCast(fsp)) else null); 13 | if (shader) |s| { 14 | return Shader{ ._ptr = s }; 15 | } else return sf.Error.nullptrUnknownReason; 16 | } 17 | /// Create a shader object from glsl code as string, you can omit some shader types by passing null 18 | pub fn createFromMemory(vertex_shader: ?[:0]const u8, geometry_shader: ?[:0]const u8, fragment_shader: ?[:0]const u8) !Shader { 19 | const shader = sf.c.sfShader_createFromMemory(if (vertex_shader) |vs| @as([*c]const u8, @ptrCast(vs)) else null, if (geometry_shader) |gs| @as([*c]const u8, @ptrCast(gs)) else null, if (fragment_shader) |fs| @as([*c]const u8, @ptrCast(fs)) else null); 20 | if (shader) |s| { 21 | return Shader{ ._ptr = s }; 22 | } else return sf.Error.nullptrUnknownReason; 23 | } 24 | /// Destroys this shader object 25 | pub fn destroy(self: *Shader) void { 26 | sf.c.sfShader_destroy(self._ptr); 27 | self._ptr = undefined; 28 | } 29 | 30 | // Availability 31 | 32 | /// Checks whether or not shaders can be used in the system 33 | pub fn isAvailable() bool { 34 | return sf.c.sfShader_isAvailable() != 0; 35 | } 36 | /// Checks whether or not geometry shaders can be used 37 | pub fn isGeometryAvailable() bool { 38 | return sf.c.sfShader_isAvailable() != 0; 39 | } 40 | 41 | const CurrentTextureT = struct {}; 42 | /// Special value to pass to setUniform to have an uniform of the texture used for drawing 43 | /// which cannot be known in advance 44 | pub const CurrentTexture: CurrentTextureT = .{}; 45 | 46 | // Uniform 47 | 48 | /// Sets an uniform for the shader 49 | /// Colors are vectors so if you want to pass a color use .toIVec4() or .toFVec4() 50 | /// Pass CurrentTexture if you want to have the drawing texture as an uniform, which cannot be known in advance 51 | pub fn setUniform(self: *Shader, name: [:0]const u8, value: anytype) void { 52 | const T = @TypeOf(value); 53 | switch (T) { 54 | f32 => sf.c.sfShader_setFloatUniform(self._ptr, name, value), 55 | c_int => sf.c.sfShader_setIntUniform(self._ptr, name, value), 56 | bool => sf.c.sfShader_setBoolUniform(self._ptr, name, if (value) 1 else 0), 57 | glsl.FVec2 => sf.c.sfShader_setVec2Uniform(self._ptr, name, value._toCSFML()), 58 | glsl.FVec3 => sf.c.sfShader_setVec3Uniform(self._ptr, name, value._toCSFML()), 59 | glsl.FVec4 => sf.c.sfShader_setVec4Uniform(self._ptr, name, @as(sf.c.sfGlslVec4, @bitCast(value))), 60 | glsl.IVec2 => sf.c.sfShader_setIvec2Uniform(self._ptr, name, value._toCSFML()), 61 | glsl.IVec3 => sf.c.sfShader_setIvec3Uniform(self._ptr, name, @as(sf.c.sfGlslIvec3, @bitCast(value))), 62 | glsl.IVec4 => sf.c.sfShader_setIvec4Uniform(self._ptr, name, @as(sf.c.sfGlslIvec4, @bitCast(value))), 63 | glsl.BVec2 => sf.c.sfShader_setBvec2Uniform(self._ptr, name, @as(sf.c.sfGlslBvec2, @bitCast(value))), 64 | glsl.BVec3 => sf.c.sfShader_setBvec3Uniform(self._ptr, name, @as(sf.c.sfGlslBvec3, @bitCast(value))), 65 | glsl.BVec4 => sf.c.sfShader_setBvec4Uniform(self._ptr, name, @as(sf.c.sfGlslBvec4, @bitCast(value))), 66 | glsl.Mat3 => sf.c.sfShader_setMat3Uniform(self._ptr, name, @as(*const sf.c.sfGlslMat3, @ptrCast(@alignCast(&value)))), 67 | glsl.Mat4 => sf.c.sfShader_setMat4Uniform(self._ptr, name, @as(*const sf.c.sfGlslMat4, @ptrCast(@alignCast(&value)))), 68 | sf.graphics.Texture => sf.c.sfShader_setTextureUniform(self._ptr, name, value._get()), 69 | CurrentTextureT => sf.c.sfShader_setCurrentTextureUniform(self._ptr, name), 70 | []const f32 => sf.c.sfShader_setFloatUniformArray(self._ptr, name, value.ptr, value.len), 71 | []const glsl.FVec2 => sf.c.sfShader_setVec2UniformArray(self._ptr, name, @as(*sf.c.sfGlslVec2, @ptrCast(value.ptr)), value.len), 72 | []const glsl.FVec3 => sf.c.sfShader_setVec3UniformArray(self._ptr, name, @as(*sf.c.sfGlslVec3, @ptrCast(value.ptr)), value.len), 73 | []const glsl.FVec4 => sf.c.sfShader_setVec4UniformArray(self._ptr, name, @as(*sf.c.sfGlslVec4, @ptrCast(value.ptr)), value.len), 74 | []const glsl.Mat3 => sf.c.sfShader_setMat3UniformArray(self._ptr, name, @as(*sf.c.sfGlslMat3, @ptrCast(value.ptr)), value.len), 75 | []const glsl.Mat4 => sf.c.sfShader_setMat4UniformArray(self._ptr, name, @as(*sf.c.sfGlslVec4, @ptrCast(value.ptr)), value.len), 76 | else => @compileError("Uniform of type \"" ++ @typeName(T) ++ "\" cannot be set inside shader."), 77 | } 78 | } 79 | 80 | /// Pointer to the CSFML structure 81 | _ptr: *sf.c.sfShader 82 | -------------------------------------------------------------------------------- /src/audio/Music.zig: -------------------------------------------------------------------------------- 1 | //! Streamed music played from an audio file. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.audio; 7 | pub usingnamespace sfml.system; 8 | }; 9 | 10 | const Music = @This(); 11 | 12 | // Constructor/destructor 13 | 14 | /// Loads music from a file 15 | pub fn createFromFile(path: [:0]const u8) !Music { 16 | const music = sf.c.sfMusic_createFromFile(path); 17 | if (music) |m| { 18 | return Music{ ._ptr = m }; 19 | } else return sf.Error.resourceLoadingError; 20 | } 21 | /// Loads music from a file in memory 22 | pub fn createFromMemory(data: []const u8) !Music { 23 | const music = sf.c.sfMusic_createFromMemory(@as(?*const anyopaque, @ptrCast(data.ptr)), data.len); 24 | if (music) |m| { 25 | return Music{ ._ptr = m }; 26 | } else return sf.Error.resourceLoadingError; 27 | } 28 | 29 | // TODO 30 | //pub const initFromStream = @compileError("Function is not implemented yet."); 31 | 32 | /// Destroys this music object 33 | pub fn destroy(self: *Music) void { 34 | sf.c.sfMusic_destroy(self._ptr); 35 | self._ptr = undefined; 36 | } 37 | 38 | // Music control functions 39 | 40 | /// Plays the music 41 | pub fn play(self: *Music) void { 42 | sf.c.sfMusic_play(self._ptr); 43 | } 44 | /// Pauses the music 45 | pub fn pause(self: *Music) void { 46 | sf.c.sfMusic_pause(self._ptr); 47 | } 48 | /// Stops the music and resets the player position 49 | pub fn stop(self: *Music) void { 50 | sf.c.sfMusic_stop(self._ptr); 51 | } 52 | 53 | // Getters / Setters 54 | 55 | /// Gets the total duration of the music 56 | pub fn getDuration(self: Music) sf.Time { 57 | return sf.Time._fromCSFML(sf.c.sfMusic_getDuration(self._ptr)); 58 | } 59 | 60 | /// Gets the current stream position of the music 61 | pub fn getPlayingOffset(self: Music) sf.Time { 62 | return sf.Time._fromCSFML(sf.c.sfMusic_getPlayingOffset(self._ptr)); 63 | } 64 | /// Sets the current stream position of the music 65 | pub fn setPlayingOffset(self: *Music, offset: sf.Time) void { 66 | sf.c.sfMusic_setPlayingOffset(self._ptr, offset._toCSFML()); 67 | } 68 | 69 | /// Gets the loop points of the music 70 | pub fn getLoopPoints(self: Music) sf.TimeSpan { 71 | return sf.TimeSpan._fromCSFML(sf.c.sfMusic_getLoopPoints(self._ptr)); 72 | } 73 | /// Gets the loop points of the music 74 | pub fn setLoopPoints(self: *Music, span: sf.TimeSpan) void { 75 | sf.c.sfMusic_setLoopPoints(self._ptr, span._toCSFML()); 76 | } 77 | 78 | /// Tells whether or not this stream is in loop mode 79 | pub fn getLoop(self: Music) bool { 80 | return sf.c.sfMusic_getLoop(self._ptr) != 0; 81 | } 82 | /// Enable or disable auto loop 83 | pub fn setLoop(self: *Music, loop: bool) void { 84 | sf.c.sfMusic_setLoop(self._ptr, @intFromBool(loop)); 85 | } 86 | 87 | /// Sets the pitch of the music 88 | pub fn getPitch(self: Music) f32 { 89 | return sf.c.sfMusic_getPitch(self._ptr); 90 | } 91 | /// Gets the pitch of the music 92 | pub fn setPitch(self: *Music, pitch: f32) void { 93 | sf.c.sfMusic_setPitch(self._ptr, pitch); 94 | } 95 | 96 | /// Sets the volume of the music 97 | pub fn getVolume(self: Music) f32 { 98 | return sf.c.sfMusic_getVolume(self._ptr); 99 | } 100 | /// Gets the volume of the music 101 | pub fn setVolume(self: *Music, volume: f32) void { 102 | sf.c.sfMusic_setVolume(self._ptr, volume); 103 | } 104 | 105 | /// Gets the sample rate of this music 106 | pub fn getSampleRate(self: Music) usize { 107 | return @as(usize, @intCast(sf.c.sfMusic_getSampleRate(self._ptr))); 108 | } 109 | 110 | /// Gets the channel count of the music 111 | pub fn getChannelCount(self: Music) usize { 112 | return @as(usize, @intCast(sf.c.sfMusic_getChannelCount(self._ptr))); 113 | } 114 | 115 | /// Get the current status of a music (stopped, paused, playing) 116 | pub fn getStatus(self: Music) sf.SoundStatus { 117 | return @enumFromInt(sf.c.sfMusic_getStatus(self._ptr)); 118 | } 119 | 120 | /// Tell whether the music's position is relative to the listener or is absolute 121 | pub fn isRelativeToListener(self: Music) bool { 122 | return sf.c.sfMusic_isRelativeToListener(self._ptr) != 0; 123 | } 124 | /// Make the music's position relative to the listener or absolute 125 | pub fn setRelativeToListener(self: *Music, loop: bool) void { 126 | sf.c.sfMusic_setRelativeToListener(self._ptr, @intFromBool(loop)); 127 | } 128 | 129 | /// Set the minimum distance of a music 130 | pub fn setMinDistance(self: *Music, min_distance: f32) void { 131 | sf.c.sfMusic_setMinDistance(self._ptr, min_distance); 132 | } 133 | /// Get the minimum distance of a music 134 | pub fn getMinDistance(self: Music) f32 { 135 | return sf.c.sfMusic_getMinDistance(self._ptr); 136 | } 137 | 138 | /// Set the attenuation factor of a music 139 | pub fn setAttenuation(self: *Music, attenuation: f32) void { 140 | sf.c.sfMusic_setAttenuation(self._ptr, attenuation); 141 | } 142 | /// Get the attenuation factor of a music 143 | pub fn getAttenuation(self: Music) f32 { 144 | return sf.c.sfMusic_getAttenuation(self._ptr); 145 | } 146 | 147 | /// Set the 3D position of a music in the audio scene 148 | pub fn setPosition(self: *Music, position: sf.Vector3f) void { 149 | sf.c.sfMusic_setPosition(self._ptr, position._toCSFML()); 150 | } 151 | /// Get the 3D position of a music in the audio scene 152 | pub fn getPosition(self: Music) sf.Vector3f { 153 | return sf.Vector3f._fromCSFML(sf.c.sfMusic_getPosition(self._ptr)); 154 | } 155 | 156 | /// Pointer to the csfml music 157 | _ptr: *sf.c.sfMusic, 158 | -------------------------------------------------------------------------------- /src/graphics/VertexArray.zig: -------------------------------------------------------------------------------- 1 | //! Define a set of one or more 2D primitives. The vertices are stored in ram. 2 | 3 | const sf = @import("../root.zig"); 4 | const std = @import("std"); 5 | 6 | const VertexArray = @This(); 7 | 8 | // Constructors/destructors 9 | 10 | /// Creates an empty vertex array 11 | pub fn create() !VertexArray { 12 | const va = sf.c.sfVertexArray_create(); 13 | if (va) |vert| { 14 | return VertexArray{ ._ptr = vert }; 15 | } else return sf.Error.nullptrUnknownReason; 16 | } 17 | /// Creates a vertex array from a slice of vertices 18 | pub fn createFromSlice(vertex: []const sf.graphics.Vertex, primitive: sf.graphics.PrimitiveType) !VertexArray { 19 | const va = sf.c.sfVertexArray_create(); 20 | if (va) |vert| { 21 | sf.c.sfVertexArray_setPrimitiveType(vert, @intFromEnum(primitive)); 22 | sf.c.sfVertexArray_resize(vert, vertex.len); 23 | for (vertex, 0..) |v, i| 24 | sf.c.sfVertexArray_getVertex(vert, i).* = @as(sf.c.sfVertex, @bitCast(v)); 25 | return VertexArray{ ._ptr = vert }; 26 | } else return sf.Error.nullptrUnknownReason; 27 | } 28 | 29 | /// Destroys a vertex array 30 | pub fn destroy(self: *VertexArray) void { 31 | sf.c.sfVertexArray_destroy(self._ptr); 32 | self._ptr = undefined; 33 | } 34 | 35 | /// Copies the vertex array 36 | pub fn copy(self: VertexArray) !VertexArray { 37 | const va = sf.c.sfVertexArray_copy(self._ptr); 38 | if (va) |vert| { 39 | return VertexArray{ ._ptr = vert }; 40 | } else return sf.Error.nullptrUnknownReason; 41 | } 42 | 43 | // Wtf github copilot wrote that for me (all of the functions below here) 44 | // Methods and getters/setters 45 | 46 | /// Gets the vertex count of the vertex array 47 | pub fn getVertexCount(self: VertexArray) usize { 48 | return sf.c.sfVertexArray_getVertexCount(self._ptr); 49 | } 50 | 51 | /// Gets a a pointer to a vertex using its index 52 | /// Don't keep the returned pointer, it can be invalidated by other functions 53 | pub fn getVertex(self: VertexArray, index: usize) *sf.graphics.Vertex { 54 | // TODO: Should this use a pointer to the vertexarray? 55 | // Me, later, what did that comment even mean? ^ 56 | const ptr = sf.c.sfVertexArray_getVertex(self._ptr, index); 57 | std.debug.assert(index < self.getVertexCount()); 58 | return @as(*sf.graphics.Vertex, @ptrCast(ptr.?)); 59 | } 60 | 61 | /// Gets a slice to the vertices of the vertex array 62 | /// Don't keep the returned pointer, it can be invalidated by other function 63 | pub fn getSlice(self: VertexArray) []sf.graphics.Vertex { 64 | // TODO: Should this use a pointer to the vertexarray? 65 | var ret: []sf.graphics.Vertex = undefined; 66 | 67 | ret.len = self.getVertexCount(); 68 | ret.ptr = @as([*]sf.graphics.Vertex, @ptrCast(self.getVertex(0))); 69 | 70 | return ret; 71 | } 72 | 73 | /// Clears the vertex array 74 | pub fn clear(self: *VertexArray) void { 75 | sf.c.sfVertexArray_clear(self._ptr); 76 | } 77 | 78 | /// Resizes the vertex array to a given size 79 | pub fn resize(self: *VertexArray, vertexCount: usize) void { 80 | sf.c.sfVertexArray_resize(self._ptr, vertexCount); 81 | } 82 | 83 | /// Appends a vertex to the array 84 | pub fn append(self: *VertexArray, vertex: sf.graphics.Vertex) void { 85 | sf.c.sfVertexArray_append(self._ptr, @as(sf.c.sfVertex, @bitCast(vertex))); 86 | } 87 | 88 | /// Gets the primitives' type of this array 89 | pub fn getPrimitiveType(self: VertexArray) sf.graphics.PrimitiveType { 90 | return @as(sf.graphics.PrimitiveType, @enumFromInt(sf.c.sfVertexArray_getPrimitiveType(self._ptr))); 91 | } 92 | 93 | /// Sets the primitives' type of this array 94 | pub fn setPrimitiveType(self: *VertexArray, primitive: sf.graphics.PrimitiveType) void { 95 | sf.c.sfVertexArray_setPrimitiveType(self._ptr, @intFromEnum(primitive)); 96 | } 97 | 98 | /// Gets the bounding rectangle of the vertex array 99 | pub fn getBounds(self: VertexArray) sf.graphics.FloatRect { 100 | return sf.graphics.FloatRect._fromCSFML(sf.c.sfVertexArray_getBounds(self._ptr)); 101 | } 102 | 103 | pub const draw_suffix = "VertexArray"; 104 | 105 | /// Pointer to the csfml structure 106 | _ptr: *sf.c.sfVertexArray, 107 | 108 | test "VertexArray: sane getters and setters" { 109 | const tst = std.testing; 110 | 111 | const va_slice = [_]sf.graphics.Vertex{ 112 | .{ .position = .{ .x = -1, .y = 0 }, .color = sf.graphics.Color.Red }, 113 | .{ .position = .{ .x = 1, .y = 0 }, .color = sf.graphics.Color.Green }, 114 | .{ .position = .{ .x = -1, .y = 1 }, .color = sf.graphics.Color.Blue }, 115 | }; 116 | var va = try createFromSlice(va_slice[0..], sf.graphics.PrimitiveType.triangles); 117 | defer va.destroy(); 118 | 119 | va.append(.{ .position = .{ .x = 1, .y = 1 }, .color = sf.graphics.Color.Yellow }); 120 | va.setPrimitiveType(sf.graphics.PrimitiveType.quads); 121 | 122 | try tst.expectEqual(@as(usize, 4), va.getVertexCount()); 123 | try tst.expectEqual(sf.graphics.PrimitiveType.quads, va.getPrimitiveType()); 124 | try tst.expectEqual(sf.graphics.FloatRect{ .left = -1, .top = 0, .width = 2, .height = 1 }, va.getBounds()); 125 | 126 | va.resize(3); 127 | va.setPrimitiveType(sf.graphics.PrimitiveType.triangle_fan); 128 | try tst.expectEqual(@as(usize, 3), va.getVertexCount()); 129 | 130 | const vert = va.getVertex(0).*; 131 | try tst.expectEqual(sf.system.Vector2f{ .x = -1, .y = 0 }, vert.position); 132 | try tst.expectEqual(sf.graphics.Color.Red, vert.color); 133 | 134 | va.getVertex(1).* = .{ .position = .{ .x = 1, .y = 1 }, .color = sf.graphics.Color.Yellow }; 135 | 136 | const slice = va.getSlice(); 137 | try tst.expectEqual(sf.graphics.Color.Yellow, slice[1].color); 138 | try tst.expectEqual(@as(usize, 3), slice.len); 139 | 140 | va.clear(); 141 | try tst.expectEqual(@as(usize, 0), va.getVertexCount()); 142 | 143 | var va2 = try create(); 144 | defer va2.destroy(); 145 | 146 | try tst.expectEqual(@as(usize, 0), va2.getVertexCount()); 147 | } 148 | -------------------------------------------------------------------------------- /src/audio/Sound.zig: -------------------------------------------------------------------------------- 1 | //! Regular sound that can be played in the audio environment. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.audio; 7 | pub usingnamespace sfml.system; 8 | }; 9 | 10 | const Sound = @This(); 11 | 12 | // Constructor/destructor 13 | 14 | /// Inits an empty sound 15 | pub fn create() !Sound { 16 | const sound = sf.c.sfSound_create(); 17 | if (sound) |s| { 18 | return Sound{ ._ptr = s }; 19 | } else return sf.Error.nullptrUnknownReason; 20 | } 21 | 22 | /// Inits a sound with a SoundBuffer object 23 | pub fn createFromBuffer(buffer: sf.SoundBuffer) !Sound { 24 | var sound = try Sound.create(); 25 | sound.setBuffer(buffer); 26 | return sound; 27 | } 28 | 29 | /// Destroys this sound object 30 | pub fn destroy(self: *Sound) void { 31 | sf.c.sfSound_destroy(self._ptr); 32 | self._ptr = undefined; 33 | } 34 | 35 | // Sound control functions 36 | 37 | /// Plays the sound 38 | pub fn play(self: *Sound) void { 39 | sf.c.sfSound_play(self._ptr); 40 | } 41 | /// Pauses the sound 42 | pub fn pause(self: *Sound) void { 43 | sf.c.sfSound_pause(self._ptr); 44 | } 45 | /// Stops the sound and resets the player position 46 | pub fn stop(self: *Sound) void { 47 | sf.c.sfSound_stop(self._ptr); 48 | } 49 | 50 | // Getters / Setters 51 | 52 | /// Gets the buffer this sound is attached to 53 | /// Not valid if a buffer was never assigned (but not null?) 54 | pub fn getBuffer(self: Sound) ?sf.SoundBuffer { 55 | const buf = sf.c.sfSound_getBuffer(self._ptr); 56 | if (buf) |buffer| { 57 | return .{ ._const_ptr = buffer }; 58 | } else return null; 59 | } 60 | 61 | /// Sets the buffer this sound will play 62 | pub fn setBuffer(self: *Sound, buffer: sf.SoundBuffer) void { 63 | sf.c.sfSound_setBuffer(self._ptr, buffer._ptr); 64 | } 65 | 66 | /// Gets the current playing offset of the sound 67 | pub fn getPlayingOffset(self: Sound) sf.Time { 68 | return sf.Time._fromCSFML(sf.c.sfSound_getPlayingOffset(self._ptr)); 69 | } 70 | /// Sets the current playing offset of the sound 71 | pub fn setPlayingOffset(self: *Sound, offset: sf.Time) void { 72 | sf.c.sfSound_setPlayingOffset(self._ptr, offset._toCSFML()); 73 | } 74 | 75 | /// Tells whether or not this sound is in loop mode 76 | pub fn getLoop(self: Sound) bool { 77 | return sf.c.sfSound_getLoop(self._ptr) != 0; 78 | } 79 | /// Enable or disable auto loop 80 | pub fn setLoop(self: *Sound, loop: bool) void { 81 | sf.c.sfSound_setLoop(self._ptr, @intFromBool(loop)); 82 | } 83 | 84 | /// Sets the pitch of the sound 85 | pub fn getPitch(self: Sound) f32 { 86 | return sf.c.sfSound_getPitch(self._ptr); 87 | } 88 | /// Gets the pitch of the sound 89 | pub fn setPitch(self: *Sound, pitch: f32) void { 90 | sf.c.sfSound_setPitch(self._ptr, pitch); 91 | } 92 | 93 | /// Sets the volume of the sound 94 | pub fn getVolume(self: Sound) f32 { 95 | return sf.c.sfSound_getVolume(self._ptr); 96 | } 97 | /// Gets the volume of the sound 98 | pub fn setVolume(self: *Sound, volume: f32) void { 99 | sf.c.sfSound_setVolume(self._ptr, volume); 100 | } 101 | 102 | /// Get the current status of a sound (stopped, paused, playing) 103 | pub fn getStatus(self: Sound) sf.SoundStatus { 104 | return @enumFromInt(sf.c.sfSound_getStatus(self._ptr)); 105 | } 106 | 107 | /// Tell whether the sound's position is relative to the listener or is absolute 108 | pub fn isRelativeToListener(self: Sound) bool { 109 | return sf.c.sfSound_isRelativeToListener(self._ptr) != 0; 110 | } 111 | /// Make the sound's position relative to the listener or absolute 112 | pub fn setRelativeToListener(self: *Sound, loop: bool) void { 113 | sf.c.sfSound_setRelativeToListener(self._ptr, @intFromBool(loop)); 114 | } 115 | 116 | /// Set the minimum distance of a sound 117 | pub fn setMinDistance(self: *Sound, min_distance: f32) void { 118 | sf.c.sfSound_setMinDistance(self._ptr, min_distance); 119 | } 120 | /// Get the minimum distance of a sound 121 | pub fn getMinDistance(self: Sound) f32 { 122 | return sf.c.sfSound_getMinDistance(self._ptr); 123 | } 124 | 125 | /// Set the attenuation factor of a sound 126 | pub fn setAttenuation(self: *Sound, attenuation: f32) void { 127 | sf.c.sfSound_setAttenuation(self._ptr, attenuation); 128 | } 129 | /// Get the attenuation factor of a sound 130 | pub fn getAttenuation(self: Sound) f32 { 131 | return sf.c.sfSound_getAttenuation(self._ptr); 132 | } 133 | 134 | /// Set the 3D position of a sound in the audio scene 135 | pub fn setPosition(self: *Sound, position: sf.Vector3f) void { 136 | sf.c.sfSound_setPosition(self._ptr, position._toCSFML()); 137 | } 138 | /// Get the 3D position of a sound in the audio scene 139 | pub fn getPosition(self: Sound) sf.Vector3f { 140 | return sf.Vector3f._fromCSFML(sf.c.sfSound_getPosition(self._ptr)); 141 | } 142 | 143 | /// Pointer to the csfml sound 144 | _ptr: *sf.c.sfSound, 145 | 146 | test "Sound: sane getters and setters" { 147 | const tst = @import("std").testing; 148 | 149 | var sound = try Sound.create(); 150 | defer sound.destroy(); 151 | 152 | sound.setLoop(true); 153 | sound.setAttenuation(0.5); 154 | sound.setMinDistance(10.0); 155 | sound.setPitch(1.2); 156 | sound.setRelativeToListener(true); 157 | sound.setVolume(2.0); 158 | sound.setPosition(sf.vector3f(1.0, 2.0, 3.0)); 159 | 160 | try tst.expectEqual(sf.SoundStatus.stopped, sound.getStatus()); 161 | try tst.expectEqual(sf.Time.seconds(0), sound.getPlayingOffset()); 162 | try tst.expect(sound.getLoop()); 163 | try tst.expectApproxEqAbs(0.5, sound.getAttenuation(), 0.001); 164 | try tst.expectApproxEqAbs(10.0, sound.getMinDistance(), 0.001); 165 | try tst.expectApproxEqAbs(1.2, sound.getPitch(), 0.001); 166 | try tst.expect(sound.isRelativeToListener()); 167 | try tst.expectApproxEqAbs(2.0, sound.getVolume(), 0.001); 168 | 169 | const pos = sound.getPosition(); 170 | try tst.expectApproxEqAbs(1.0, pos.x, 0.001); 171 | try tst.expectApproxEqAbs(2.0, pos.y, 0.001); 172 | try tst.expectApproxEqAbs(3.0, pos.z, 0.001); 173 | } 174 | -------------------------------------------------------------------------------- /src/graphics/Image.zig: -------------------------------------------------------------------------------- 1 | //! Class for loading, manipulating and saving images. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const std = @import("std"); 11 | const assert = std.debug.assert; 12 | 13 | const Image = @This(); 14 | 15 | // Constructor/destructor 16 | 17 | /// Creates a new image 18 | pub fn create(size: sf.Vector2u, color: sf.Color) !Image { 19 | const img = sf.c.sfImage_createFromColor(size.x, size.y, color._toCSFML()); 20 | if (img) |i| { 21 | return Image{ ._ptr = i }; 22 | } else return sf.Error.nullptrUnknownReason; 23 | } 24 | /// Creates an image from a pixel array 25 | pub fn createFromPixels(size: sf.Vector2u, pixels: []const sf.Color) !Image { 26 | // Check if there is enough data 27 | if (pixels.len < size.x * size.y) 28 | return sf.Error.notEnoughData; 29 | 30 | const img = sf.c.sfImage_createFromPixels(size.x, size.y, @as([*]const u8, @ptrCast(pixels.ptr))); 31 | 32 | if (img) |i| { 33 | return Image{ ._ptr = i }; 34 | } else return sf.Error.nullptrUnknownReason; 35 | } 36 | /// Loads an image from a file 37 | pub fn createFromFile(path: [:0]const u8) !Image { 38 | const img = sf.c.sfImage_createFromFile(path); 39 | if (img) |i| { 40 | return Image{ ._ptr = i }; 41 | } else return sf.Error.resourceLoadingError; 42 | } 43 | /// Loads an image from a file in memory 44 | pub fn createFromMemory(data: []const u8) !Image { 45 | const img = sf.c.sfImage_createFromMemory(@as(?*const anyopaque, @ptrCast(data.ptr)), data.len); 46 | if (img) |i| { 47 | return Image{ ._ptr = i }; 48 | } else return sf.Error.resourceLoadingError; 49 | } 50 | 51 | /// Formats for saveToMemory 52 | pub const FileFormat = enum { bmp, png, tga, jpg }; 53 | 54 | /// Save the image to a buffer in memory 55 | pub fn saveToMemory(self: Image, buffer: *sf.system.Buffer, format: FileFormat) !void { 56 | if (sf.c.sfImage_saveToMemory(self._ptr, buffer._ptr, @tagName(format)) == 0) 57 | return sf.Error.savingInFileFailed; 58 | } 59 | 60 | /// Destroys an image 61 | pub fn destroy(self: *Image) void { 62 | sf.c.sfImage_destroy(self._ptr); 63 | self._ptr = undefined; 64 | } 65 | 66 | // Save an image to a file 67 | pub fn saveToFile(self: Image, path: [:0]const u8) !void { 68 | if (sf.c.sfImage_saveToFile(self._ptr, path) != 1) 69 | return sf.Error.savingInFileFailed; 70 | } 71 | 72 | // Getters/setters 73 | 74 | /// Gets a pixel from this image (bounds are only checked in an assertion) 75 | pub fn getPixel(self: Image, pixel_pos: sf.Vector2u) sf.Color { 76 | const size = self.getSize(); 77 | assert(pixel_pos.x < size.x and pixel_pos.y < size.y); 78 | 79 | return sf.Color._fromCSFML(sf.c.sfImage_getPixel(self._ptr, pixel_pos.x, pixel_pos.y)); 80 | } 81 | /// Sets a pixel on this image (bounds are only checked in an assertion) 82 | pub fn setPixel(self: *Image, pixel_pos: sf.Vector2u, color: sf.Color) void { 83 | const size = self.getSize(); 84 | assert(pixel_pos.x < size.x and pixel_pos.y < size.y); 85 | 86 | sf.c.sfImage_setPixel(self._ptr, pixel_pos.x, pixel_pos.y, color._toCSFML()); 87 | } 88 | 89 | /// Gets the size of this image 90 | pub fn getSize(self: Image) sf.Vector2u { 91 | const size = sf.c.sfImage_getSize(self._ptr); 92 | return sf.Vector2u{ .x = size.x, .y = size.y }; 93 | } 94 | 95 | /// Changes the pixels of the image matching color to be transparent 96 | pub fn createMaskFromColor(self: *Image, color: sf.Color, alpha: u8) void { 97 | sf.c.sfImage_createMaskFromColor(self._ptr, color._toCSFML(), alpha); 98 | } 99 | 100 | /// Flip an image horizontally (left <-> right) 101 | pub fn flipHorizontally(self: *Image) void { 102 | sf.c.sfImage_flipHorizontally(self._ptr); 103 | } 104 | /// Flip an image vertically (top <-> bottom) 105 | pub fn flipVertically(self: *Image) void { 106 | sf.c.sfImage_flipVertically(self._ptr); 107 | } 108 | 109 | /// Get a read-only pointer to the array of pixels of the image 110 | pub fn getPixelsSlice(self: Image) []const sf.Color { 111 | const ptr = sf.c.sfImage_getPixelsPtr(self._ptr); 112 | 113 | const size = self.getSize(); 114 | const len = size.x * size.y; 115 | 116 | var ret: []const sf.Color = undefined; 117 | ret.len = len; 118 | ret.ptr = @as([*]const sf.Color, @ptrCast(@alignCast(ptr))); 119 | 120 | return ret; 121 | } 122 | 123 | /// Pointer to the csfml texture 124 | _ptr: *sf.c.sfImage, 125 | 126 | test "image: sane getters and setters" { 127 | const tst = std.testing; 128 | const allocator = std.heap.page_allocator; 129 | 130 | const pixel_data = try allocator.alloc(sf.Color, 30); 131 | defer allocator.free(pixel_data); 132 | 133 | for (pixel_data, 0..) |*c, i| { 134 | c.* = sf.Color.fromHSVA(@as(f32, @floatFromInt(i)) / 30 * 360, 100, 100, 1); 135 | } 136 | 137 | var img = try Image.createFromPixels(.{ .x = 5, .y = 6 }, pixel_data); 138 | defer img.destroy(); 139 | 140 | try tst.expectEqual(sf.Vector2u{ .x = 5, .y = 6 }, img.getSize()); 141 | 142 | img.setPixel(.{ .x = 1, .y = 2 }, sf.Color.Cyan); 143 | try tst.expectEqual(sf.Color.Cyan, img.getPixel(.{ .x = 1, .y = 2 })); 144 | 145 | var tex = try sf.Texture.createFromImage(img, null); 146 | defer tex.destroy(); 147 | 148 | img.setPixel(.{ .x = 1, .y = 2 }, sf.Color.Red); 149 | const slice = img.getPixelsSlice(); 150 | try tst.expectEqual(sf.Color.Red, slice[0]); 151 | } 152 | 153 | test "image: save to memory" { 154 | const tst = std.testing; 155 | 156 | var img = try Image.create(.{ .x = 50, .y = 50 }, sf.Color.Cyan); 157 | defer img.destroy(); 158 | 159 | inline for (std.meta.fields(FileFormat)) |format| { 160 | var buf = try sf.Buffer.create(); 161 | defer buf.destroy(); 162 | 163 | try img.saveToMemory(&buf, @enumFromInt(format.value)); 164 | 165 | try tst.expect(buf.getSize() != 0); 166 | const slice = buf.getData(); 167 | 168 | try tst.expect(slice.len != 0); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/system/vector.zig: -------------------------------------------------------------------------------- 1 | //! Utility struct for manipulating 2-dimensional vectors. 2 | 3 | const std = @import("std"); 4 | const sf = @import("../root.zig"); 5 | 6 | fn isNum(comptime T: type) bool { 7 | return switch (@typeInfo(T)) { 8 | .int, .float, .comptime_float, .comptime_int => true, 9 | else => false, 10 | }; 11 | } 12 | 13 | /// Template for a 2 dimensional vector 14 | pub fn Vector2(comptime T: type) type { 15 | return extern struct { 16 | const Self = @This(); 17 | 18 | /// The CSFML vector type equivalent 19 | const CsfmlEquivalent = switch (T) { 20 | c_uint => sf.c.sfVector2u, 21 | c_int => sf.c.sfVector2i, 22 | f32 => sf.c.sfVector2f, 23 | else => void, 24 | }; 25 | 26 | pub fn new(x: T, y: T) Self { 27 | return .{ .x = x, .y = y }; 28 | } 29 | 30 | pub usingnamespace if (CsfmlEquivalent != void) struct { 31 | /// Makes a CSFML vector with this vector (only if the corresponding type exists) 32 | /// This is mainly for the inner workings of this wrapper 33 | pub fn _toCSFML(self: Self) CsfmlEquivalent { 34 | if (CsfmlEquivalent == void) @compileError("This vector type doesn't have a CSFML equivalent."); 35 | return @as(CsfmlEquivalent, @bitCast(self)); 36 | } 37 | 38 | /// Creates a vector from a CSFML one (only if the corresponding type exists) 39 | /// This is mainly for the inner workings of this wrapper 40 | pub fn _fromCSFML(vec: CsfmlEquivalent) Self { 41 | if (CsfmlEquivalent == void) @compileError("This vector type doesn't have a CSFML equivalent."); 42 | return @as(Self, @bitCast(vec)); 43 | } 44 | } else struct {}; 45 | 46 | pub usingnamespace if (isNum(T)) struct { 47 | /// Adds two vectors 48 | pub fn add(self: Self, other: Self) Self { 49 | return Self{ .x = self.x + other.x, .y = self.y + other.y }; 50 | } 51 | 52 | /// Substracts two vectors 53 | pub fn substract(self: Self, other: Self) Self { 54 | return Self{ .x = self.x - other.x, .y = self.y - other.y }; 55 | } 56 | 57 | /// Scales a vector 58 | pub fn scale(self: Self, scalar: T) Self { 59 | return Self{ .x = self.x * scalar, .y = self.y * scalar }; 60 | } 61 | } else struct {}; 62 | 63 | /// x component of the vector 64 | x: T, 65 | /// y component of the vector 66 | y: T, 67 | }; 68 | } 69 | 70 | /// Template for a 3 dimensional vector 71 | pub fn Vector3(comptime T: type) type { 72 | return packed struct { 73 | const Self = @This(); 74 | 75 | pub fn new(x: T, y: T, z: T) Self { 76 | return .{ .x = x, .y = y, .z = z }; 77 | } 78 | 79 | pub usingnamespace if (T == f32) struct { 80 | /// Makes a CSFML vector with this vector (only if the corresponding type exists) 81 | /// This is mainly for the inner workings of this wrapper 82 | pub fn _toCSFML(self: Self) sf.c.sfVector3f { 83 | if (T != f32) @compileError("This vector type doesn't have a CSFML equivalent."); 84 | return @as(sf.c.sfVector3f, @bitCast(self)); 85 | } 86 | 87 | /// Creates a vector from a CSFML one (only if the corresponding type exists) 88 | /// This is mainly for the inner workings of this wrapper 89 | pub fn _fromCSFML(vec: sf.c.sfVector3f) Self { 90 | if (T != f32) @compileError("This vector type doesn't have a CSFML equivalent."); 91 | return @as(Self, @bitCast(vec)); 92 | } 93 | } else struct {}; 94 | 95 | pub usingnamespace if (isNum(T)) struct { 96 | /// Adds two vectors 97 | pub fn add(self: Self, other: Self) Self { 98 | return Self{ .x = self.x + other.x, .y = self.y + other.y, .z = self.z + other.z }; 99 | } 100 | 101 | /// Substracts two vectors 102 | pub fn substract(self: Self, other: Self) Self { 103 | return Self{ .x = self.x - other.x, .y = self.y - other.y, .z = self.z - other.z }; 104 | } 105 | 106 | /// Scales a vector 107 | pub fn scale(self: Self, scalar: T) Self { 108 | return Self{ .x = self.x * scalar, .y = self.y * scalar, .z = self.z * scalar }; 109 | } 110 | } else struct {}; 111 | 112 | /// x component of the vector 113 | x: T, 114 | /// y component of the vector 115 | y: T, 116 | /// z component of the vector 117 | z: T, 118 | }; 119 | } 120 | 121 | /// Template for a 4 dimensional vector 122 | /// SFML doesn't really have this but as shaders use such vectors it can be here anyways 123 | pub fn Vector4(comptime T: type) type { 124 | return packed struct { 125 | const Self = @This(); 126 | 127 | pub fn new(x: T, y: T, z: T, w: T) Self { 128 | return .{ .x = x, .y = y, .z = z, .w = w }; 129 | } 130 | 131 | /// x component of the vector 132 | x: T, 133 | /// y component of the vector 134 | y: T, 135 | /// z component of the vector 136 | z: T, 137 | /// w component of the vector 138 | w: T, 139 | }; 140 | } 141 | 142 | test "vector: sane from/to CSFML vectors" { 143 | const tst = @import("std").testing; 144 | 145 | inline for ([_]type{ c_int, c_uint, f32 }) |T| { 146 | const VecT = Vector2(T); 147 | const vec = VecT{ .x = 1, .y = 3 }; 148 | const cvec = vec._toCSFML(); 149 | 150 | try tst.expectEqual(vec.x, cvec.x); 151 | try tst.expectEqual(vec.y, cvec.y); 152 | 153 | const vec2 = VecT._fromCSFML(cvec); 154 | 155 | try tst.expectEqual(vec, vec2); 156 | } 157 | 158 | { 159 | const vec = Vector3(f32){ .x = 1, .y = 3.5, .z = -12 }; 160 | const cvec = vec._toCSFML(); 161 | 162 | try tst.expectEqual(vec.x, cvec.x); 163 | try tst.expectEqual(vec.y, cvec.y); 164 | try tst.expectEqual(vec.z, cvec.z); 165 | 166 | const vec2 = Vector3(f32)._fromCSFML(cvec); 167 | 168 | try tst.expectEqual(vec, vec2); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/graphics/Sprite.zig: -------------------------------------------------------------------------------- 1 | //! Drawable representation of a texture, with its own transformations, color, etc. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const Sprite = @This(); 11 | 12 | // Constructor/destructor 13 | 14 | /// Inits a sprite with no texture 15 | pub fn create() !Sprite { 16 | const sprite = sf.c.sfSprite_create(); 17 | if (sprite == null) 18 | return sf.Error.nullptrUnknownReason; 19 | 20 | return Sprite{ ._ptr = sprite.? }; 21 | } 22 | 23 | /// Inits a sprite with a texture 24 | pub fn createFromTexture(texture: sf.Texture) !Sprite { 25 | const sprite = sf.c.sfSprite_create(); 26 | if (sprite == null) 27 | return sf.Error.nullptrUnknownReason; 28 | 29 | sf.c.sfSprite_setTexture(sprite, texture._get(), 1); 30 | 31 | return Sprite{ ._ptr = sprite.? }; 32 | } 33 | 34 | /// Destroys this sprite 35 | pub fn destroy(self: *Sprite) void { 36 | sf.c.sfSprite_destroy(self._ptr); 37 | self._ptr = undefined; 38 | } 39 | 40 | // Draw function 41 | /// The draw function of this sprite 42 | /// Meant to be called by your_target.draw(your_sprite, .{}); 43 | pub fn sfDraw(self: Sprite, target: anytype, states: ?*sf.c.sfRenderStates) void { 44 | switch (@TypeOf(target)) { 45 | sf.RenderWindow => sf.c.sfRenderWindow_drawSprite(target._ptr, self._ptr, states), 46 | sf.RenderTexture => sf.c.sfRenderTexture_drawSprite(target._ptr, self._ptr, states), 47 | else => @compileError("target must be a render target"), 48 | } 49 | } 50 | 51 | // Getters/setters 52 | 53 | /// Gets the position of this sprite 54 | pub fn getPosition(self: Sprite) sf.Vector2f { 55 | return sf.Vector2f._fromCSFML(sf.c.sfSprite_getPosition(self._ptr)); 56 | } 57 | /// Sets the position of this sprite 58 | pub fn setPosition(self: *Sprite, pos: sf.Vector2f) void { 59 | sf.c.sfSprite_setPosition(self._ptr, pos._toCSFML()); 60 | } 61 | /// Adds the offset to this shape's position 62 | pub fn move(self: *Sprite, offset: sf.Vector2f) void { 63 | sf.c.sfSprite_move(self._ptr, offset._toCSFML()); 64 | } 65 | 66 | /// Gets the scale of this sprite 67 | pub fn getScale(self: Sprite) sf.Vector2f { 68 | return sf.Vector2f._fromCSFML(sf.c.sfSprite_getScale(self._ptr)); 69 | } 70 | /// Sets the scale of this sprite 71 | pub fn setScale(self: *Sprite, factor: sf.Vector2f) void { 72 | sf.c.sfSprite_setScale(self._ptr, factor._toCSFML()); 73 | } 74 | /// Scales this sprite 75 | pub fn scale(self: *Sprite, factor: sf.Vector2f) void { 76 | sf.c.sfSprite_scale(self._ptr, factor._toCSFML()); 77 | } 78 | 79 | /// Gets the origin of this sprite 80 | pub fn getOrigin(self: Sprite) sf.Vector2f { 81 | return sf.Vector2f._fromCSFML(sf.c.sfSprite_getOrigin(self._ptr)); 82 | } 83 | /// Sets the origin of this sprite 84 | pub fn setOrigin(self: *Sprite, origin: sf.Vector2f) void { 85 | sf.c.sfSprite_setOrigin(self._ptr, origin._toCSFML()); 86 | } 87 | 88 | /// Gets the rotation of this sprite 89 | pub fn getRotation(self: Sprite) f32 { 90 | return sf.c.sfSprite_getRotation(self._ptr); 91 | } 92 | /// Sets the rotation of this sprite 93 | pub fn setRotation(self: *Sprite, angle: f32) void { 94 | sf.c.sfSprite_setRotation(self._ptr, angle); 95 | } 96 | /// Rotates this shape by a given amount 97 | pub fn rotate(self: *Sprite, angle: f32) void { 98 | sf.c.sfSprite_rotate(self._ptr, angle); 99 | } 100 | 101 | /// Gets the color of this sprite 102 | pub fn getColor(self: Sprite) sf.Color { 103 | return sf.Color._fromCSFML(sf.c.sfSprite_getColor(self._ptr)); 104 | } 105 | /// Sets the color of this sprite 106 | pub fn setColor(self: *Sprite, color: sf.Color) void { 107 | sf.c.sfSprite_setColor(self._ptr, color._toCSFML()); 108 | } 109 | 110 | /// Gets the texture of this shape 111 | pub fn getTexture(self: Sprite) ?sf.Texture { 112 | const t = sf.c.sfSprite_getTexture(self._ptr); 113 | if (t) |tex| { 114 | return sf.Texture{ ._const_ptr = tex }; 115 | } else return null; 116 | } 117 | /// Sets this sprite's texture (the sprite will take the texture's dimensions) 118 | pub fn setTexture(self: *Sprite, texture: ?sf.Texture) void { 119 | const tex = if (texture) |t| t._get() else null; 120 | sf.c.sfSprite_setTexture(self._ptr, tex, 1); 121 | } 122 | /// Gets the sub-rectangle of the texture that the sprite will display 123 | pub fn getTextureRect(self: Sprite) sf.IntRect { 124 | return sf.IntRect._fromCSFML(sf.c.sfSprite_getTextureRect(self._ptr)); 125 | } 126 | /// Sets the sub-rectangle of the texture that the sprite will display 127 | pub fn setTextureRect(self: *Sprite, rect: sf.IntRect) void { 128 | sf.c.sfSprite_setTextureRect(self._ptr, rect._toCSFML()); 129 | } 130 | 131 | /// Gets the bounds in the local coordinates system 132 | pub fn getLocalBounds(self: Sprite) sf.FloatRect { 133 | return sf.FloatRect._fromCSFML(sf.c.sfSprite_getLocalBounds(self._ptr)); 134 | } 135 | 136 | /// Gets the bounds in the global coordinates 137 | pub fn getGlobalBounds(self: Sprite) sf.FloatRect { 138 | return sf.FloatRect._fromCSFML(sf.c.sfSprite_getGlobalBounds(self._ptr)); 139 | } 140 | 141 | pub const draw_suffix = "Sprite"; 142 | 143 | /// Pointer to the csfml structure 144 | _ptr: *sf.c.sfSprite, 145 | 146 | test "sprite: sane getters and setters" { 147 | const tst = @import("std").testing; 148 | 149 | var spr = try Sprite.create(); 150 | defer spr.destroy(); 151 | 152 | spr.setColor(sf.Color.Yellow); 153 | spr.setRotation(15); 154 | spr.setPosition(.{ .x = 1, .y = 2 }); 155 | spr.setOrigin(.{ .x = 20, .y = 25 }); 156 | spr.setScale(.{ .x = 2, .y = 2 }); 157 | spr.setTexture(null); 158 | 159 | try tst.expectEqual(sf.Color.Yellow, spr.getColor()); 160 | try tst.expectEqual(sf.Vector2f{ .x = 1, .y = 2 }, spr.getPosition()); 161 | try tst.expectEqual(sf.Vector2f{ .x = 20, .y = 25 }, spr.getOrigin()); 162 | try tst.expectEqual(@as(?sf.Texture, null), spr.getTexture()); 163 | try tst.expectEqual(sf.Vector2f{ .x = 2, .y = 2 }, spr.getScale()); 164 | 165 | spr.rotate(5); 166 | spr.move(.{ .x = -5, .y = 5 }); 167 | spr.scale(.{ .x = 5, .y = 5 }); 168 | 169 | try tst.expectEqual(@as(f32, 20), spr.getRotation()); 170 | try tst.expectEqual(sf.Vector2f{ .x = -4, .y = 7 }, spr.getPosition()); 171 | try tst.expectEqual(sf.Vector2f{ .x = 10, .y = 10 }, spr.getScale()); 172 | } 173 | -------------------------------------------------------------------------------- /src/graphics/color.zig: -------------------------------------------------------------------------------- 1 | //! Utility class for manipulating RGBA colors. 2 | 3 | const sf = @import("../root.zig"); 4 | const std = @import("std"); 5 | const math = std.math; 6 | 7 | pub const Color = packed struct { 8 | /// Converts a color from a csfml object 9 | /// For inner workings 10 | pub fn _fromCSFML(col: sf.c.sfColor) Color { 11 | return @as(Color, @bitCast(col)); 12 | } 13 | 14 | /// Converts this color to a csfml one 15 | /// For inner workings 16 | pub fn _toCSFML(self: Color) sf.c.sfColor { 17 | return @as(sf.c.sfColor, @bitCast(self)); 18 | } 19 | 20 | /// Inits a color with rgb components 21 | pub fn fromRGB(red: u8, green: u8, blue: u8) Color { 22 | return Color{ 23 | .r = red, 24 | .g = green, 25 | .b = blue, 26 | .a = 0xff, 27 | }; 28 | } 29 | 30 | /// Inits a color with rgba components 31 | pub fn fromRGBA(red: u8, green: u8, blue: u8, alpha: u8) Color { 32 | return Color{ 33 | .r = red, 34 | .g = green, 35 | .b = blue, 36 | .a = alpha, 37 | }; 38 | } 39 | 40 | /// Inits a color from a 32bits value (RGBA in that order) 41 | pub fn fromInteger(int: u32) Color { 42 | return Color{ 43 | .r = @as(u8, @truncate((int & 0xff000000) >> 24)), 44 | .g = @as(u8, @truncate((int & 0x00ff0000) >> 16)), 45 | .b = @as(u8, @truncate((int & 0x0000ff00) >> 8)), 46 | .a = @as(u8, @truncate((int & 0x000000ff) >> 0)), 47 | }; 48 | } 49 | 50 | /// Gets a 32 bit integer representing the color 51 | pub fn toInteger(self: Color) u32 { 52 | return (@as(u32, @intCast(self.r)) << 24) | 53 | (@as(u32, @intCast(self.g)) << 16) | 54 | (@as(u32, @intCast(self.b)) << 8) | 55 | (@as(u32, @intCast(self.a)) << 0); 56 | } 57 | 58 | /// Creates a color with rgba floats from 0 to 1 59 | fn fromFloats(red: f32, green: f32, blue: f32, alpha: f32) Color { 60 | return Color{ 61 | .r = @as(u8, @intFromFloat(math.clamp(red, 0.0, 1.0) * 255.0)), 62 | .g = @as(u8, @intFromFloat(math.clamp(green, 0.0, 1.0) * 255.0)), 63 | .b = @as(u8, @intFromFloat(math.clamp(blue, 0.0, 1.0) * 255.0)), 64 | .a = @as(u8, @intFromFloat(math.clamp(alpha, 0.0, 1.0) * 255.0)), 65 | }; 66 | } 67 | 68 | /// Comptime helper function to create a color from a hexadecimal string 69 | pub fn fromHex(comptime hex: []const u8) Color { 70 | if (hex.len != 7 or hex[0] != '#') 71 | @compileError("Invalid hexadecimal color"); 72 | 73 | const int = comptime try std.fmt.parseInt(u32, hex[1..7], 16); 74 | 75 | return comptime fromInteger((int << 8) | 0xff); 76 | } 77 | 78 | /// Creates a color from HSV and transparency components (this is not part of the SFML) 79 | /// hue is in degrees, saturation and value are in percents 80 | pub fn fromHSVA(hue: f32, saturation: f32, value: f32, alpha: f32) Color { 81 | const h = hue; 82 | const s = saturation / 100; 83 | const v = value / 100; 84 | const a = alpha; 85 | 86 | var hh: f32 = h; 87 | 88 | if (v <= 0.0) 89 | return fromFloats(0, 0, 0, a); 90 | 91 | if (hh >= 360.0) 92 | hh = 0; 93 | hh /= 60.0; 94 | 95 | const ff: f32 = hh - math.floor(hh); 96 | 97 | const p: f32 = v * (1.0 - s); 98 | const q: f32 = v * (1.0 - (s * ff)); 99 | const t: f32 = v * (1.0 - (s * (1.0 - ff))); 100 | 101 | return switch (@as(usize, @intFromFloat(hh))) { 102 | 0 => fromFloats(v, t, p, a), 103 | 1 => fromFloats(q, v, p, a), 104 | 2 => fromFloats(p, v, t, a), 105 | 3 => fromFloats(p, q, v, a), 106 | 4 => fromFloats(t, p, v, a), 107 | else => fromFloats(v, p, q, a), 108 | }; 109 | } 110 | 111 | /// Get a GLSL float vector for this color (for shaders) 112 | pub fn toFVec4(self: Color) sf.graphics.glsl.FVec4 { 113 | return .{ .x = @as(f32, @floatFromInt(self.r)) / 255.0, .y = @as(f32, @floatFromInt(self.g)) / 255.0, .z = @as(f32, @floatFromInt(self.b)) / 255.0, .w = @as(f32, @floatFromInt(self.a)) / 255.0 }; 114 | } 115 | /// Get a GLSL int vector for this color (for shaders) 116 | pub fn toIVec4(self: Color) sf.graphics.glsl.IVec4 { 117 | return .{ .x = self.r, .y = self.g, .z = self.b, .w = self.a }; 118 | } 119 | 120 | // Colors 121 | /// Black color 122 | pub const Black = Color.fromRGB(0, 0, 0); 123 | /// White color 124 | pub const White = Color.fromRGB(255, 255, 255); 125 | /// Red color 126 | pub const Red = Color.fromRGB(255, 0, 0); 127 | /// Green color 128 | pub const Green = Color.fromRGB(0, 255, 0); 129 | /// Blue color 130 | pub const Blue = Color.fromRGB(0, 0, 255); 131 | /// Yellow color 132 | pub const Yellow = Color.fromRGB(255, 255, 0); 133 | /// Magenta color 134 | pub const Magenta = Color.fromRGB(255, 0, 255); 135 | /// Cyan color 136 | pub const Cyan = Color.fromRGB(0, 255, 255); 137 | /// Transparent color 138 | pub const Transparent = Color.fromRGBA(0, 0, 0, 0); 139 | 140 | /// Red component 141 | r: u8, 142 | /// Green component 143 | g: u8, 144 | /// Blue component 145 | b: u8, 146 | /// Alpha (opacity) component 147 | a: u8, 148 | }; 149 | 150 | test "color: conversions" { 151 | const tst = @import("std").testing; 152 | 153 | const code: u32 = 0x4BDA9CFF; 154 | var col = Color.fromInteger(code); 155 | 156 | try tst.expectEqual(Color.fromHex("#4BDA9C"), col); 157 | try tst.expectEqual(Color.fromRGB(75, 218, 156), col); 158 | try tst.expectEqual(code, col.toInteger()); 159 | 160 | const csfml_col = sf.c.sfColor_fromInteger(@as(c_uint, code)); 161 | 162 | try tst.expectEqual(Color._fromCSFML(csfml_col), col); 163 | } 164 | 165 | test "color: hsv to rgb" { 166 | const tst = @import("std").testing; 167 | 168 | const col = Color.fromHSVA(10, 20, 100, 255); 169 | 170 | try tst.expectEqual(Color.fromRGB(255, 212, 204), col); 171 | } 172 | 173 | test "color: sane from/to CSFML color" { 174 | const tst = @import("std").testing; 175 | 176 | const col = Color.fromRGBA(5, 12, 28, 127); 177 | const ccol = col._toCSFML(); 178 | 179 | try tst.expectEqual(col.r, ccol.r); 180 | try tst.expectEqual(col.g, ccol.g); 181 | try tst.expectEqual(col.b, ccol.b); 182 | try tst.expectEqual(col.a, ccol.a); 183 | 184 | const col2 = Color._fromCSFML(ccol); 185 | 186 | try tst.expectEqual(col, col2); 187 | } 188 | -------------------------------------------------------------------------------- /src/graphics/RenderTexture.zig: -------------------------------------------------------------------------------- 1 | //! Target for off-screen 2D rendering into a texture. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const RenderTexture = @This(); 11 | 12 | // Constructor/destructor 13 | 14 | /// Inits a render texture with a size (use createWithDepthBuffer if you want a depth buffer) 15 | pub fn create(size: sf.Vector2u) !RenderTexture { 16 | const rtex = sf.c.sfRenderTexture_create(size.x, size.y, 0); //0 means no depth buffer 17 | 18 | if (rtex) |t| { 19 | return RenderTexture{ ._ptr = t }; 20 | } else return sf.Error.nullptrUnknownReason; 21 | } 22 | /// Inits a render texture with a size, it will have a depth buffer 23 | pub fn createWithDepthBuffer(size: sf.Vector2u) !RenderTexture { 24 | const rtex = sf.c.sfRenderTexture_create(size.x, size.y, 1); 25 | 26 | if (rtex) |t| { 27 | return .{ ._ptr = t }; 28 | } else return sf.Error.nullptrUnknownReason; 29 | } 30 | 31 | /// Destroys this render texture 32 | pub fn destroy(self: *RenderTexture) void { 33 | sf.c.sfRenderTexture_destroy(self._ptr); 34 | self._ptr = undefined; 35 | } 36 | 37 | // Drawing functions 38 | 39 | /// Clears the drawing target with a color 40 | pub fn clear(self: *RenderTexture, color: sf.Color) void { 41 | sf.c.sfRenderTexture_clear(self._ptr, color._toCSFML()); 42 | } 43 | 44 | /// Updates the texture with what has been drawn on the render area 45 | pub fn display(self: *RenderTexture) void { 46 | sf.c.sfRenderTexture_display(self._ptr); 47 | } 48 | 49 | /// Draw something on the texture (won't be visible until display is called) 50 | /// You can pass a render state or null for default 51 | pub fn draw(self: *RenderTexture, to_draw: anytype, states: ?sf.RenderStates) void { 52 | const draw_fn = @field(sf.c, "sfRenderTexture_draw" ++ @TypeOf(to_draw).draw_suffix); 53 | if (states) |s| { 54 | var cstates = s._toCSFML(); 55 | draw_fn(self._ptr, to_draw._ptr, &cstates); 56 | } else draw_fn(self._ptr, to_draw._ptr, null); 57 | } 58 | 59 | /// Gets a const reference to the target texture (the reference doesn't change) 60 | pub fn getTexture(self: RenderTexture) sf.Texture { 61 | const tex = sf.c.sfRenderTexture_getTexture(self._ptr); 62 | return sf.Texture{ ._const_ptr = tex.? }; 63 | } 64 | 65 | // Texture related stuff 66 | 67 | /// Generates a mipmap for the current texture data, returns true if the operation succeeded 68 | pub fn generateMipmap(self: *RenderTexture) bool { 69 | return sf.c.sfRenderTexture_generateMipmap(self._ptr) != 0; 70 | } 71 | 72 | /// Tells whether or not the texture is to be smoothed 73 | pub fn isSmooth(self: RenderTexture) bool { 74 | return sf.c.sfRenderTexture_isSmooth(self._ptr) != 0; 75 | } 76 | /// Enables or disables texture smoothing 77 | pub fn setSmooth(self: *RenderTexture, smooth: bool) void { 78 | sf.c.sfRenderTexture_setSmooth(self._ptr, @intFromBool(smooth)); 79 | } 80 | 81 | /// Tells whether or not the texture should repeat when rendering outside its bounds 82 | pub fn isRepeated(self: RenderTexture) bool { 83 | return sf.c.sfRenderTexture_isRepeated(self._ptr) != 0; 84 | } 85 | /// Enables or disables texture repeating 86 | pub fn setRepeated(self: *RenderTexture, repeated: bool) void { 87 | sf.c.sfRenderTexture_setRepeated(self._ptr, @intFromBool(repeated)); 88 | } 89 | 90 | /// Gets the size of this window 91 | pub fn getSize(self: RenderTexture) sf.Vector2u { 92 | return sf.Vector2u._fromCSFML(sf.c.sfRenderTexture_getSize(self._ptr)); 93 | } 94 | 95 | // Target related stuff 96 | 97 | /// Gets the current view of the target 98 | /// Unlike in SFML, you don't get a const pointer but a copy 99 | pub fn getView(self: RenderTexture) sf.View { 100 | return sf.View._fromCSFML(sf.c.sfRenderTexture_getView(self._ptr).?); 101 | } 102 | /// Gets the default view of this target 103 | /// Unlike in SFML, you don't get a const pointer but a copy 104 | pub fn getDefaultView(self: RenderTexture) sf.View { 105 | return sf.View._fromCSFML(sf.c.sfRenderTexture_getDefaultView(self._ptr).?); 106 | } 107 | /// Sets the view of this target 108 | pub fn setView(self: *RenderTexture, view: sf.View) void { 109 | const cview = view._toCSFML(); 110 | defer sf.c.sfView_destroy(cview); 111 | sf.c.sfRenderTexture_setView(self._ptr, cview); 112 | } 113 | /// Gets the viewport of this target 114 | pub fn getViewport(self: RenderTexture, view: sf.View) sf.IntRect { 115 | return sf.IntRect._fromCSFML(sf.c.sfRenderTexture_getViewport(self._ptr, view._toCSFML())); 116 | } 117 | 118 | /// Convert a point from target coordinates to world coordinates, using the current view (or the specified view) 119 | pub fn mapPixelToCoords(self: RenderTexture, pixel: sf.Vector2i, view: ?sf.View) sf.Vector2f { 120 | if (view) |v| { 121 | const cview = v._toCSFML(); 122 | defer sf.c.sfView_destroy(cview); 123 | return sf.Vector2f._fromCSFML(sf.c.sfRenderTexture_mapPixelToCoords(self._ptr, pixel._toCSFML(), cview)); 124 | } else return sf.Vector2f._fromCSFML(sf.c.sfRenderTexture_mapPixelToCoords(self._ptr, pixel._toCSFML(), null)); 125 | } 126 | /// Convert a point from world coordinates to target coordinates, using the current view (or the specified view) 127 | pub fn mapCoordsToPixel(self: RenderTexture, coords: sf.Vector2f, view: ?sf.View) sf.Vector2i { 128 | if (view) |v| { 129 | const cview = v._toCSFML(); 130 | defer sf.c.sfView_destroy(cview); 131 | return sf.Vector2i._fromCSFML(sf.c.sfRenderTexture_mapCoordsToPixel(self._ptr, coords._toCSFML(), cview)); 132 | } else return sf.Vector2i._fromCSFML(sf.c.sfRenderTexture_mapCoordsToPixel(self._ptr, coords._toCSFML(), null)); 133 | } 134 | 135 | /// Pointer to the csfml structure 136 | _ptr: *sf.c.sfRenderTexture, 137 | 138 | test "rendertexture tests" { 139 | const tst = @import("std").testing; 140 | 141 | var rentex = try RenderTexture.create(.{ .x = 10, .y = 10 }); 142 | defer rentex.destroy(); 143 | 144 | rentex.setRepeated(true); 145 | rentex.setSmooth(true); 146 | 147 | rentex.clear(sf.Color.Red); 148 | { 149 | var rect = try sf.RectangleShape.create(.{ .x = 5, .y = 5 }); 150 | defer rect.destroy(); 151 | 152 | rect.setFillColor(sf.Color.Blue); 153 | 154 | rentex.draw(rect, null); 155 | } 156 | rentex.display(); 157 | 158 | _ = rentex.generateMipmap(); 159 | 160 | try tst.expect(rentex.isRepeated()); 161 | try tst.expect(rentex.isSmooth()); 162 | 163 | const tex = rentex.getTexture(); 164 | 165 | try tst.expectEqual(sf.Vector2u{ .x = 10, .y = 10 }, tex.getSize()); 166 | try tst.expectEqual(sf.Vector2u{ .x = 10, .y = 10 }, rentex.getSize()); 167 | 168 | var img = tex.copyToImage(); 169 | defer img.destroy(); 170 | 171 | try tst.expectEqual(sf.Color.Blue, img.getPixel(.{ .x = 1, .y = 1 })); 172 | try tst.expectEqual(sf.Color.Red, img.getPixel(.{ .x = 6, .y = 3 })); 173 | } 174 | -------------------------------------------------------------------------------- /src/window/event.zig: -------------------------------------------------------------------------------- 1 | //! Defines a system event and its parameters. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | }; 8 | 9 | const std = @import("std"); 10 | 11 | pub const Event = union(enum) { 12 | const Self = @This(); 13 | 14 | // Big oof 15 | /// Creates this event from a csfml one 16 | pub fn _fromCSFML(event: sf.c.sfEvent) Self { 17 | return switch (event.type) { 18 | sf.c.sfEvtClosed => .closed, 19 | sf.c.sfEvtResized => .{ .resized = .{ .size = .{ .x = event.size.width, .y = event.size.height } } }, 20 | sf.c.sfEvtLostFocus => .lost_focus, 21 | sf.c.sfEvtGainedFocus => .gained_focus, 22 | sf.c.sfEvtTextEntered => .{ .text_entered = .{ .unicode = event.text.unicode } }, 23 | sf.c.sfEvtKeyPressed => .{ .key_pressed = .{ .code = @as(sf.window.keyboard.KeyCode, @enumFromInt(event.key.code)), .alt = (event.key.alt != 0), .control = (event.key.control != 0), .shift = (event.key.shift != 0), .system = (event.key.system != 0) } }, 24 | sf.c.sfEvtKeyReleased => .{ .key_released = .{ .code = @as(sf.window.keyboard.KeyCode, @enumFromInt(event.key.code)), .alt = (event.key.alt != 0), .control = (event.key.control != 0), .shift = (event.key.shift != 0), .system = (event.key.system != 0) } }, 25 | sf.c.sfEvtMouseWheelMoved => .{ .mouse_wheel_moved = .{ .delta = event.mouseWheel.delta, .x = event.mouseWheel.x, .y = event.mouseWheel.y } }, 26 | sf.c.sfEvtMouseWheelScrolled => .{ .mouse_wheel_scrolled = .{ .wheel = @as(sf.window.mouse.Wheel, @enumFromInt(event.mouseWheelScroll.wheel)), .delta = event.mouseWheelScroll.delta, .pos = .{ .x = event.mouseWheelScroll.x, .y = event.mouseWheelScroll.y } } }, 27 | sf.c.sfEvtMouseButtonPressed => .{ .mouse_button_pressed = .{ .button = @as(sf.window.mouse.Button, @enumFromInt(event.mouseButton.button)), .pos = .{ .x = event.mouseButton.x, .y = event.mouseButton.y } } }, 28 | sf.c.sfEvtMouseButtonReleased => .{ .mouse_button_released = .{ .button = @as(sf.window.mouse.Button, @enumFromInt(event.mouseButton.button)), .pos = .{ .x = event.mouseButton.x, .y = event.mouseButton.y } } }, 29 | sf.c.sfEvtMouseMoved => .{ .mouse_moved = .{ .pos = .{ .x = event.mouseMove.x, .y = event.mouseMove.y } } }, 30 | sf.c.sfEvtMouseEntered => .mouse_entered, 31 | sf.c.sfEvtMouseLeft => .mouse_left, 32 | sf.c.sfEvtJoystickButtonPressed => .{ .joystick_button_pressed = .{ .joystickId = event.joystickButton.joystickId, .button = event.joystickButton.button } }, 33 | sf.c.sfEvtJoystickButtonReleased => .{ .joystick_button_released = .{ .joystickId = event.joystickButton.joystickId, .button = event.joystickButton.button } }, 34 | sf.c.sfEvtJoystickMoved => .{ .joystick_moved = .{ .joystickId = event.joystickMove.joystickId, .axis = event.joystickMove.axis, .position = event.joystickMove.position } }, 35 | sf.c.sfEvtJoystickConnected => .{ .joystick_connected = .{ .joystickId = event.joystickConnect.joystickId } }, 36 | sf.c.sfEvtJoystickDisconnected => .{ .joystick_disconnected = .{ .joystickId = event.joystickConnect.joystickId } }, 37 | sf.c.sfEvtTouchBegan => .{ .touch_began = .{ .finger = event.touch.finger, .pos = .{ .x = event.touch.x, .y = event.touch.y } } }, 38 | sf.c.sfEvtTouchMoved => .{ .touch_moved = .{ .finger = event.touch.finger, .pos = .{ .x = event.touch.x, .y = event.touch.y } } }, 39 | sf.c.sfEvtTouchEnded => .{ .touch_ended = .{ .finger = event.touch.finger, .pos = .{ .x = event.touch.x, .y = event.touch.y } } }, 40 | sf.c.sfEvtSensorChanged => .{ .sensor_changed = .{ .sensorType = event.sensor.sensorType, .vector = .{ .x = event.sensor.x, .y = event.sensor.y, .z = event.sensor.z } } }, 41 | else => blk: { 42 | std.debug.print("Event code {} is unimplemented! Please open an issue.\n", .{event.type}); 43 | break :blk .unimplemented; 44 | } 45 | }; 46 | } 47 | 48 | /// Gets how many types of event exist 49 | pub fn getEventCount() usize { 50 | return @intCast(sf.c.sfEvtCount); 51 | } 52 | 53 | /// Size events parameters 54 | pub const SizeEvent = struct { 55 | size: sf.Vector2u, 56 | }; 57 | 58 | /// Keyboard event parameters 59 | pub const KeyEvent = struct { 60 | code: sf.window.keyboard.KeyCode, 61 | alt: bool, 62 | control: bool, 63 | shift: bool, 64 | system: bool, 65 | }; 66 | 67 | /// Text event parameters 68 | pub const TextEvent = struct { 69 | unicode: u32, 70 | }; 71 | 72 | /// Mouse move event parameters 73 | pub const MouseMoveEvent = struct { 74 | pos: sf.Vector2i, 75 | }; 76 | 77 | /// Mouse buttons events parameters 78 | pub const MouseButtonEvent = struct { 79 | button: sf.window.mouse.Button, 80 | pos: sf.Vector2i, 81 | }; 82 | 83 | /// Mouse wheel events parameters (deprecated) 84 | pub const MouseWheelEvent = struct { 85 | delta: c_int, 86 | x: c_int, 87 | y: c_int, 88 | }; 89 | 90 | /// Mouse wheel events parameters 91 | pub const MouseWheelScrollEvent = struct { 92 | wheel: sf.window.mouse.Wheel, 93 | delta: f32, 94 | pos: sf.Vector2i, 95 | }; 96 | 97 | /// Joystick axis move event parameters 98 | pub const JoystickMoveEvent = struct { 99 | joystickId: c_uint, 100 | axis: sf.c.sfJoystickAxis, 101 | position: f32, 102 | }; 103 | 104 | /// Joystick buttons events parameters 105 | pub const JoystickButtonEvent = struct { 106 | joystickId: c_uint, 107 | button: c_uint, 108 | }; 109 | 110 | /// Joystick connection/disconnection event parameters 111 | pub const JoystickConnectEvent = struct { 112 | joystickId: c_uint, 113 | }; 114 | 115 | /// Touch events parameters 116 | pub const TouchEvent = struct { 117 | finger: c_uint, 118 | pos: sf.Vector2i, 119 | }; 120 | 121 | /// Sensor event parameters 122 | pub const SensorEvent = struct { 123 | sensorType: sf.c.sfSensorType, 124 | vector: sf.Vector3f, 125 | }; 126 | 127 | // An event is one of those 128 | closed, 129 | resized: SizeEvent, 130 | lost_focus, 131 | gained_focus, 132 | text_entered: TextEvent, 133 | key_pressed: KeyEvent, 134 | key_released: KeyEvent, 135 | /// Deprecated 136 | mouse_wheel_moved: MouseWheelEvent, 137 | mouse_wheel_scrolled: MouseWheelScrollEvent, 138 | mouse_button_pressed: MouseButtonEvent, 139 | mouse_button_released: MouseButtonEvent, 140 | mouse_moved: MouseMoveEvent, 141 | mouse_entered, 142 | mouse_left, 143 | joystick_button_pressed: JoystickButtonEvent, 144 | joystick_button_released: JoystickButtonEvent, 145 | joystick_moved: JoystickMoveEvent, 146 | joystick_connected: JoystickConnectEvent, 147 | joystick_disconnected: JoystickConnectEvent, 148 | touch_began: TouchEvent, 149 | touch_moved: TouchEvent, 150 | touch_ended: TouchEvent, 151 | sensor_changed: SensorEvent, 152 | unimplemented, // If you encounter this, it means that event code needs to be added 153 | }; 154 | -------------------------------------------------------------------------------- /src/graphics/rect.zig: -------------------------------------------------------------------------------- 1 | //! Utility class for manipulating 2D axis aligned rectangles. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | }; 8 | 9 | pub fn Rect(comptime T: type) type { 10 | return packed struct { 11 | const Self = @This(); 12 | 13 | /// The CSFML vector type equivalent 14 | const CsfmlEquivalent = switch (T) { 15 | c_int => sf.c.sfIntRect, 16 | f32 => sf.c.sfFloatRect, 17 | else => void, 18 | }; 19 | 20 | /// Creates a rect (just for convinience) 21 | pub fn init(left: T, top: T, width: T, height: T) Self { 22 | return Self{ 23 | .left = left, 24 | .top = top, 25 | .width = width, 26 | .height = height, 27 | }; 28 | } 29 | 30 | /// Makes a CSFML rect with this rect (only if the corresponding type exists) 31 | /// This is mainly for the inner workings of this wrapper 32 | pub fn _toCSFML(self: Self) CsfmlEquivalent { 33 | if (CsfmlEquivalent == void) @compileError("This rectangle type doesn't have a CSFML equivalent."); 34 | return @as(CsfmlEquivalent, @bitCast(self)); 35 | } 36 | 37 | /// Creates a rect from a CSFML one (only if the corresponding type exists) 38 | /// This is mainly for the inner workings of this wrapper 39 | pub fn _fromCSFML(rect: CsfmlEquivalent) Self { 40 | if (CsfmlEquivalent == void) @compileError("This rectangle type doesn't have a CSFML equivalent."); 41 | return @as(Self, @bitCast(rect)); 42 | } 43 | 44 | /// Checks if a point is inside this recangle 45 | pub fn contains(self: Self, vec: sf.Vector2(T)) bool { 46 | // Shamelessly stolen 47 | const min_x: T = @min(self.left, self.left + self.width); 48 | const max_x: T = @max(self.left, self.left + self.width); 49 | const min_y: T = @min(self.top, self.top + self.height); 50 | const max_y: T = @max(self.top, self.top + self.height); 51 | 52 | return (vec.x >= min_x and 53 | vec.x < max_x and 54 | vec.y >= min_y and 55 | vec.y < max_y); 56 | } 57 | 58 | /// Checks if two rectangles have a common intersection, if yes returns that zone, if not returns null 59 | pub fn intersects(self: Self, other: Self) ?Self { 60 | // Shamelessly stolen too 61 | const r1_min_x: T = @min(self.left, self.left + self.width); 62 | const r1_max_x: T = @max(self.left, self.left + self.width); 63 | const r1_min_y: T = @min(self.top, self.top + self.height); 64 | const r1_max_y: T = @max(self.top, self.top + self.height); 65 | 66 | const r2_min_x: T = @min(other.left, other.left + other.width); 67 | const r2_max_x: T = @max(other.left, other.left + other.width); 68 | const r2_min_y: T = @min(other.top, other.top + other.height); 69 | const r2_max_y: T = @max(other.top, other.top + other.height); 70 | 71 | const inter_left: T = @max(r1_min_x, r2_min_x); 72 | const inter_top: T = @max(r1_min_y, r2_min_y); 73 | const inter_right: T = @min(r1_max_x, r2_max_x); 74 | const inter_bottom: T = @min(r1_max_y, r2_max_y); 75 | 76 | if (inter_left < inter_right and inter_top < inter_bottom) { 77 | return Self.init(inter_left, inter_top, inter_right - inter_left, inter_bottom - inter_top); 78 | } else { 79 | return null; 80 | } 81 | } 82 | 83 | /// Checks if two rectangles are the same 84 | pub fn equals(self: Self, other: Self) bool { 85 | return (self.left == other.left and 86 | self.top == other.top and 87 | self.width == other.width and 88 | self.height == other.height); 89 | } 90 | 91 | /// Gets a vector with left and top components inside 92 | pub fn getCorner(self: Self) sf.Vector2(T) { 93 | return sf.Vector2(T){ .x = self.left, .y = self.top }; 94 | } 95 | pub const getPosition = getCorner; 96 | /// Gets a vector with the bottom right corner coordinates 97 | pub fn getOtherCorner(self: Self) sf.Vector2(T) { 98 | return self.getCorner().add(self.getSize()); 99 | } 100 | /// Gets a vector with width and height components inside 101 | pub fn getSize(self: Self) sf.Vector2(T) { 102 | return sf.Vector2(T){ .x = self.width, .y = self.height }; 103 | } 104 | 105 | /// x component of the top left corner 106 | left: T, 107 | /// x component of the top left corner 108 | top: T, 109 | /// width of the rectangle 110 | width: T, 111 | /// height of the rectangle 112 | height: T, 113 | }; 114 | } 115 | 116 | test "rect: intersect" { 117 | const tst = @import("std").testing; 118 | 119 | var r1 = Rect(c_int).init(0, 0, 10, 10); 120 | var r2 = Rect(c_int).init(6, 6, 20, 20); 121 | var r3 = Rect(c_int).init(-5, -5, 10, 10); 122 | 123 | try tst.expectEqual(@as(?Rect(c_int), null), r2.intersects(r3)); 124 | 125 | var inter1: sf.c.sfIntRect = undefined; 126 | var inter2: sf.c.sfIntRect = undefined; 127 | 128 | try tst.expectEqual(sf.c.sfIntRect_intersects(&r1._toCSFML(), &r2._toCSFML(), &inter1), 1); 129 | try tst.expectEqual(sf.c.sfIntRect_intersects(&r1._toCSFML(), &r3._toCSFML(), &inter2), 1); 130 | 131 | try tst.expectEqual(Rect(c_int)._fromCSFML(inter1), r1.intersects(r2).?); 132 | try tst.expectEqual(Rect(c_int)._fromCSFML(inter2), r1.intersects(r3).?); 133 | } 134 | 135 | test "rect: contains" { 136 | const tst = @import("std").testing; 137 | 138 | const r1 = Rect(f32).init(0, 0, 10, 10); 139 | 140 | try tst.expect(r1.contains(.{ .x = 0, .y = 0 })); 141 | try tst.expect(r1.contains(.{ .x = 9, .y = 9 })); 142 | try tst.expect(!r1.contains(.{ .x = 5, .y = -1 })); 143 | try tst.expect(!r1.contains(.{ .x = 10, .y = 5 })); 144 | } 145 | 146 | test "rect: getPosition" { 147 | const tst = @import("std").testing; 148 | 149 | const r1 = Rect(f32).init(1, 3, 10, 10); 150 | const pos = r1.getPosition(); 151 | 152 | try tst.expectApproxEqAbs(1.0, pos.x, 0.0001); 153 | try tst.expectApproxEqAbs(3.0, pos.y, 0.0001); 154 | } 155 | 156 | test "rect: getSize" { 157 | const tst = @import("std").testing; 158 | 159 | const r1 = Rect(f32).init(1, 3, 10, 12); 160 | const size = r1.getSize(); 161 | 162 | try tst.expectApproxEqAbs(10, size.x, 0.0001); 163 | try tst.expectApproxEqAbs(12, size.y, 0.0001); 164 | } 165 | 166 | test "rect: sane from/to CSFML rect" { 167 | const tst = @import("std").testing; 168 | 169 | inline for ([_]type{ c_int, f32 }) |T| { 170 | const rect = Rect(T).init(1, 3, 5, 10); 171 | const crect = rect._toCSFML(); 172 | 173 | try tst.expectEqual(rect.left, crect.left); 174 | try tst.expectEqual(rect.top, crect.top); 175 | try tst.expectEqual(rect.width, crect.width); 176 | try tst.expectEqual(rect.height, crect.height); 177 | 178 | const rect2 = Rect(T)._fromCSFML(crect); 179 | 180 | try tst.expectEqual(rect, rect2); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/network/Packet.zig: -------------------------------------------------------------------------------- 1 | //! Utility class to build blocks of data to transfer over the network. 2 | 3 | const std = @import("std"); 4 | const sf = @import("../root.zig"); 5 | 6 | const Packet = @This(); 7 | 8 | // Constructor/destructor 9 | 10 | /// Inits an empty packet 11 | pub fn create() !Packet { 12 | const pack = sf.c.sfPacket_create(); 13 | if (pack) |p| { 14 | return Packet{ ._ptr = p }; 15 | } else return sf.Error.nullptrUnknownReason; 16 | } 17 | /// Destroys a packet 18 | pub fn destroy(self: *Packet) void { 19 | sf.c.sfPacket_destroy(self._ptr); 20 | } 21 | /// Copies a packet 22 | pub fn copy(self: Packet) !Packet { 23 | const pack = sf.c.sfPacket_copy(self._ptr); 24 | if (pack) |p| { 25 | return Packet{ ._ptr = p }; 26 | } else return sf.Error.nullptrUnknownReason; 27 | } 28 | 29 | // Getters/Setters 30 | 31 | /// Empties this packet of its data 32 | pub fn clear(self: *Packet) void { 33 | sf.c.sfPacket_clear(self._ptr); 34 | } 35 | 36 | /// Gets a const slice of this packet's content 37 | /// Do not store, pointer may be changed when editing data 38 | /// Be careful when using that in conjunction with read or reader 39 | /// This gets all the data and does not move the reader 40 | pub fn getData(self: Packet) []const u8 { 41 | const data = sf.c.sfPacket_getData(self._ptr); 42 | const size = sf.c.sfPacket_getDataSize(self._ptr); 43 | 44 | var slice: []const u8 = undefined; 45 | slice.ptr = @as([*]const u8, @ptrCast(data)); 46 | slice.len = size; 47 | return slice; 48 | } 49 | /// Gets the data size in bytes inside this packet 50 | pub fn getDataSize(self: Packet) usize { 51 | return sf.c.sfPacket_getDataSize(self._ptr); 52 | } 53 | 54 | /// Appends bytes to the packet. You can also use the writer() 55 | pub fn append(self: *Packet, data: []const u8) !void { 56 | const size_a = self.getDataSize(); 57 | sf.c.sfPacket_append(self._ptr, data.ptr, data.len); 58 | const size_b = self.getDataSize(); 59 | const size = size_b - size_a; 60 | if (size != data.len) 61 | return sf.Error.cannotWriteToPacket; 62 | } 63 | 64 | /// Returns false if the packet is ready for reading, true if there is no more data 65 | pub fn isAtEnd(self: Packet) bool { 66 | return sf.c.sfPacket_endOfPacket(self._ptr) != 0; 67 | } 68 | /// Returns an error if last read operation failed 69 | fn checkLastRead(self: Packet) !void { 70 | if (sf.c.sfPacket_canRead(self._ptr) == 0) 71 | return sf.Error.couldntRead; 72 | } 73 | 74 | /// Reads a type from the packet 75 | /// Slightly faster than using a reader for bigger types 76 | pub fn read(self: *Packet, comptime T: type) !T { 77 | const res: T = switch (T) { 78 | bool => (sf.c.sfPacket_readBool(self._ptr) != 0), 79 | i8 => sf.c.sfPacket_readInt8(self._ptr), 80 | u8 => sf.c.sfPacket_readUint8(self._ptr), 81 | i16 => sf.c.sfPacket_readInt16(self._ptr), 82 | u16 => sf.c.sfPacket_readUint16(self._ptr), 83 | i32 => sf.c.sfPacket_readInt32(self._ptr), 84 | u32 => sf.c.sfPacket_readUint32(self._ptr), 85 | f32 => sf.c.sfPacket_readFloat(self._ptr), 86 | f64 => sf.c.sfPacket_readDouble(self._ptr), 87 | [*:0]const u8 => sf.c.sfPacket_readString(self._ptr), 88 | [*:0]const u16 => sf.c.sfPacket_readWideString(self._ptr), 89 | else => @compileError("Can't read type " ++ @typeName(T) ++ " from packet"), 90 | }; 91 | try self.checkLastRead(); 92 | return res; 93 | } 94 | 95 | /// Writes a value to the packet 96 | /// Slightly faster than using a writer for bigger types 97 | pub fn write(self: *Packet, comptime T: type, value: T) !void { 98 | // TODO: find how to make this safer 99 | switch (T) { 100 | bool => sf.c.sfPacket_writeBool(self._ptr, @intFromBool(value)), 101 | i8 => sf.c.sfPacket_writeInt8(self._ptr, value), 102 | u8 => sf.c.sfPacket_writeUint8(self._ptr, value), 103 | i16 => sf.c.sfPacket_writeInt16(self._ptr, value), 104 | u16 => sf.c.sfPacket_writeUint16(self._ptr, value), 105 | i32 => sf.c.sfPacket_writeInt32(self._ptr, value), 106 | u32 => sf.c.sfPacket_writeUint32(self._ptr, value), 107 | f32 => sf.c.sfPacket_writeFloat(self._ptr, value), 108 | f64 => sf.c.sfPacket_writeDouble(self._ptr, value), 109 | [*:0]const u8 => sf.c.sfPacket_writeString(self._ptr, value), 110 | [*:0]const u16 => sf.c.sfPacket_writeWideString(self._ptr, value), 111 | else => @compileError("Can't write type " ++ @typeName(T) ++ " to packet"), 112 | } 113 | } 114 | 115 | /// Writer type for a packet 116 | pub const Writer = std.io.Writer(*Packet, sf.Error, writeFn); 117 | /// Initializes a Writer which will write to the packet 118 | /// Slightly slower than write for bigger types but more convinient for some things 119 | pub fn writer(self: *Packet) Writer { 120 | return .{ .context = self }; 121 | } 122 | /// Write function for the writer 123 | fn writeFn(self: *Packet, m: []const u8) sf.Error!usize { 124 | // TODO: find out if I can have better safety here 125 | const size_a = self.getDataSize(); 126 | sf.c.sfPacket_append(self._ptr, m.ptr, m.len); 127 | const size_b = self.getDataSize(); 128 | const size = size_b - size_a; 129 | if (m.len != 0 and size == 0) 130 | return sf.Error.cannotWriteToPacket; 131 | return size; 132 | } 133 | 134 | /// Reader type for a packet 135 | pub const Reader = std.io.Reader(*Packet, sf.Error, readFn); 136 | /// Initializes a Reader which will read the packet's bytes 137 | /// Slightly slower than read for bigger types but more convinient for some things 138 | pub fn reader(self: *Packet) Reader { 139 | return .{ .context = self }; 140 | } 141 | /// Read function for the reader 142 | fn readFn(self: *Packet, b: []u8) sf.Error!usize { 143 | for (b, 0..) |*byte, i| { 144 | if (sf.c.sfPacket_endOfPacket(self._ptr) != 0) 145 | return i; 146 | const val = sf.c.sfPacket_readUint8(self._ptr); 147 | try self.checkLastRead(); 148 | byte.* = val; 149 | } 150 | return b.len; 151 | } 152 | 153 | /// Pointer to the csfml structure 154 | _ptr: *sf.c.sfPacket, 155 | 156 | test "packet: reading and writing" { 157 | const tst = std.testing; 158 | 159 | var pack1 = try Packet.create(); 160 | defer pack1.destroy(); 161 | 162 | // writing to the packet 163 | // using its methods 164 | try pack1.write(u16, 1999); 165 | try pack1.write(bool, true); 166 | // using a writer 167 | { 168 | var w = pack1.writer(); 169 | try w.writeIntNative(u64, 12345678); 170 | try w.writeAll("oh:"); 171 | } 172 | // using append 173 | const str = "abc"; 174 | try pack1.append(str); 175 | try tst.expectEqual(@as(usize, 17), pack1.getDataSize()); 176 | 177 | var pack2 = try pack1.copy(); 178 | defer pack2.destroy(); 179 | pack1.clear(); 180 | try tst.expectEqual(@as(usize, 0), pack1.getDataSize()); 181 | try tst.expect(pack1.isAtEnd()); 182 | 183 | // reading tests 184 | // read method 185 | try tst.expectEqual(@as(u16, 1999), try pack2.read(u16)); 186 | try tst.expect(try pack2.read(bool)); 187 | // reader 188 | { 189 | var buf: [16]u8 = undefined; 190 | var r = pack2.reader(); 191 | try tst.expectEqual(@as(u64, 12345678), try r.readIntNative(u64)); 192 | const count = try r.readAll(&buf); 193 | try tst.expectEqual(@as(usize, 6), count); 194 | try tst.expectEqualStrings("oh:abc", buf[0..count]); 195 | } 196 | // getdata 197 | const dat = pack2.getData(); 198 | try tst.expectEqual(@as(usize, 17), dat.len); 199 | try tst.expectEqualStrings("h:a", dat[12..15]); 200 | try tst.expect(pack2.isAtEnd()); 201 | } 202 | -------------------------------------------------------------------------------- /src/graphics/RectangleShape.zig: -------------------------------------------------------------------------------- 1 | //! Specialized shape representing a rectangle. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const RectangleShape = @This(); 11 | 12 | // Constructor/destructor 13 | 14 | /// Creates a rectangle shape with a size. The rectangle will be white 15 | pub fn create(size: sf.Vector2f) !RectangleShape { 16 | const rect = sf.c.sfRectangleShape_create(); 17 | if (rect == null) 18 | return sf.Error.nullptrUnknownReason; 19 | 20 | sf.c.sfRectangleShape_setFillColor(rect, sf.c.sfWhite); 21 | sf.c.sfRectangleShape_setSize(rect, size._toCSFML()); 22 | 23 | return RectangleShape{ ._ptr = rect.? }; 24 | } 25 | 26 | /// Destroys a rectangle shape 27 | pub fn destroy(self: *RectangleShape) void { 28 | sf.c.sfRectangleShape_destroy(self._ptr); 29 | self._ptr = undefined; 30 | } 31 | 32 | // Getters/setters 33 | 34 | /// Gets the fill color of this rectangle shape 35 | pub fn getFillColor(self: RectangleShape) sf.Color { 36 | return sf.Color._fromCSFML(sf.c.sfRectangleShape_getFillColor(self._ptr)); 37 | } 38 | /// Sets the fill color of this rectangle shape 39 | pub fn setFillColor(self: *RectangleShape, color: sf.Color) void { 40 | sf.c.sfRectangleShape_setFillColor(self._ptr, color._toCSFML()); 41 | } 42 | 43 | /// Gets the outline color of this rectangle shape 44 | pub fn getOutlineColor(self: RectangleShape) sf.Color { 45 | return sf.Color._fromCSFML(sf.c.sfRectangleShape_getOutlineColor(self._ptr)); 46 | } 47 | /// Sets the outline color of this rectangle shape 48 | pub fn setOutlineColor(self: *RectangleShape, color: sf.Color) void { 49 | sf.c.sfRectangleShape_setOutlineColor(self._ptr, color._toCSFML()); 50 | } 51 | 52 | /// Gets the outline thickness of this rectangle shape 53 | pub fn getOutlineThickness(self: RectangleShape) f32 { 54 | return sf.c.sfRectangleShape_getOutlineThickness(self._ptr); 55 | } 56 | /// Sets the outline thickness of this rectangle shape 57 | pub fn setOutlineThickness(self: *RectangleShape, thickness: f32) void { 58 | sf.c.sfRectangleShape_setOutlineThickness(self._ptr, thickness); 59 | } 60 | 61 | /// Gets the size of this rectangle shape 62 | pub fn getSize(self: RectangleShape) sf.Vector2f { 63 | return sf.Vector2f._fromCSFML(sf.c.sfRectangleShape_getSize(self._ptr)); 64 | } 65 | /// Sets the size of this rectangle shape 66 | pub fn setSize(self: *RectangleShape, size: sf.Vector2f) void { 67 | sf.c.sfRectangleShape_setSize(self._ptr, size._toCSFML()); 68 | } 69 | 70 | /// Gets the position of this rectangle shape 71 | pub fn getPosition(self: RectangleShape) sf.Vector2f { 72 | return sf.Vector2f._fromCSFML(sf.c.sfRectangleShape_getPosition(self._ptr)); 73 | } 74 | /// Sets the position of this rectangle shape 75 | pub fn setPosition(self: *RectangleShape, pos: sf.Vector2f) void { 76 | sf.c.sfRectangleShape_setPosition(self._ptr, pos._toCSFML()); 77 | } 78 | /// Adds the offset to this shape's position 79 | pub fn move(self: *RectangleShape, offset: sf.Vector2f) void { 80 | sf.c.sfRectangleShape_move(self._ptr, offset._toCSFML()); 81 | } 82 | 83 | /// Gets the origin of this rectangle shape 84 | pub fn getOrigin(self: RectangleShape) sf.Vector2f { 85 | return sf.Vector2f._fromCSFML(sf.c.sfRectangleShape_getOrigin(self._ptr)); 86 | } 87 | /// Sets the origin of this rectangle shape 88 | pub fn setOrigin(self: *RectangleShape, origin: sf.Vector2f) void { 89 | sf.c.sfRectangleShape_setOrigin(self._ptr, origin._toCSFML()); 90 | } 91 | 92 | /// Gets the rotation of this rectangle shape 93 | pub fn getRotation(self: RectangleShape) f32 { 94 | return sf.c.sfRectangleShape_getRotation(self._ptr); 95 | } 96 | /// Sets the rotation of this rectangle shape 97 | pub fn setRotation(self: *RectangleShape, angle: f32) void { 98 | sf.c.sfRectangleShape_setRotation(self._ptr, angle); 99 | } 100 | /// Rotates this shape by a given amount 101 | pub fn rotate(self: *RectangleShape, angle: f32) void { 102 | sf.c.sfRectangleShape_rotate(self._ptr, angle); 103 | } 104 | 105 | /// Gets the texture of this shape 106 | pub fn getTexture(self: RectangleShape) ?sf.Texture { 107 | const t = sf.c.sfRectangleShape_getTexture(self._ptr); 108 | if (t) |tex| { 109 | return sf.Texture{ ._const_ptr = tex }; 110 | } else return null; 111 | } 112 | /// Sets the texture of this shape 113 | pub fn setTexture(self: *RectangleShape, texture: ?sf.Texture) void { 114 | const tex = if (texture) |t| t._get() else null; 115 | sf.c.sfRectangleShape_setTexture(self._ptr, tex, 0); 116 | } 117 | /// Gets the sub-rectangle of the texture that the shape will display 118 | pub fn getTextureRect(self: RectangleShape) sf.IntRect { 119 | return sf.IntRect._fromCSFML(sf.c.sfRectangleShape_getTextureRect(self._ptr)); 120 | } 121 | /// Sets the sub-rectangle of the texture that the shape will display 122 | pub fn setTextureRect(self: *RectangleShape, rect: sf.IntRect) void { 123 | sf.c.sfRectangleShape_setTextureRect(self._ptr, rect._toCSFML()); 124 | } 125 | 126 | /// Gets the bounds in the local coordinates system 127 | pub fn getLocalBounds(self: RectangleShape) sf.FloatRect { 128 | return sf.FloatRect._fromCSFML(sf.c.sfRectangleShape_getLocalBounds(self._ptr)); 129 | } 130 | 131 | /// Gets the bounds in the global coordinates 132 | pub fn getGlobalBounds(self: RectangleShape) sf.FloatRect { 133 | return sf.FloatRect._fromCSFML(sf.c.sfRectangleShape_getGlobalBounds(self._ptr)); 134 | } 135 | 136 | /// Gets the point count of this shape 137 | /// A rectangle has 4 points 138 | pub fn getPointCount(_: RectangleShape) usize { 139 | return 4; 140 | } 141 | 142 | /// Gets a point of the shape using its index 143 | /// This function takes a u2 because a rectangle only has 4 points 144 | pub fn getPoint(self: RectangleShape, index: u2) sf.Vector2f { 145 | return sf.Vector2f._fromCSFML(sf.c.sfRectangleShape_getPoint(self._ptr, index)); 146 | } 147 | 148 | pub const draw_suffix = "RectangleShape"; 149 | 150 | /// Pointer to the csfml structure 151 | _ptr: *sf.c.sfRectangleShape, 152 | 153 | test "rectangle shape: sane getters and setters" { 154 | const tst = @import("std").testing; 155 | 156 | var rect = try RectangleShape.create(sf.Vector2f{ .x = 30, .y = 50 }); 157 | defer rect.destroy(); 158 | 159 | try tst.expectEqual(sf.Vector2f{ .x = 30, .y = 50 }, rect.getSize()); 160 | 161 | rect.setFillColor(sf.Color.Yellow); 162 | rect.setOutlineColor(sf.Color.Red); 163 | rect.setOutlineThickness(3); 164 | rect.setSize(.{ .x = 15, .y = 510 }); 165 | rect.setRotation(15); 166 | rect.setPosition(.{ .x = 1, .y = 2 }); 167 | rect.setOrigin(.{ .x = 20, .y = 25 }); 168 | rect.setTexture(null); //Weirdly, getTexture if texture wasn't set gives a wrong pointer 169 | 170 | try tst.expectEqual(sf.Color.Yellow, rect.getFillColor()); 171 | try tst.expectEqual(sf.Color.Red, rect.getOutlineColor()); 172 | try tst.expectEqual(@as(f32, 3), rect.getOutlineThickness()); 173 | try tst.expectEqual(sf.Vector2f{ .x = 15, .y = 510 }, rect.getSize()); 174 | try tst.expectEqual(@as(f32, 15), rect.getRotation()); 175 | try tst.expectEqual(sf.Vector2f{ .x = 1, .y = 2 }, rect.getPosition()); 176 | try tst.expectEqual(sf.Vector2f{ .x = 20, .y = 25 }, rect.getOrigin()); 177 | try tst.expectEqual(@as(?sf.Texture, null), rect.getTexture()); 178 | try tst.expectEqual(@as(usize, 4), rect.getPointCount()); 179 | 180 | rect.rotate(5); 181 | rect.move(.{ .x = -5, .y = 5 }); 182 | 183 | try tst.expectEqual(@as(f32, 20), rect.getRotation()); 184 | try tst.expectEqual(sf.Vector2f{ .x = -4, .y = 7 }, rect.getPosition()); 185 | 186 | _ = rect.getGlobalBounds(); 187 | _ = rect.getLocalBounds(); 188 | _ = rect.getTextureRect(); 189 | _ = rect.getPoint(2); 190 | } 191 | -------------------------------------------------------------------------------- /src/graphics/ConvexShape.zig: -------------------------------------------------------------------------------- 1 | //! Specialized shape representing a convex polygon 2 | 3 | const assert = @import("std").debug.assert; 4 | const sf = struct { 5 | const sfml = @import("../root.zig"); 6 | pub usingnamespace sfml; 7 | pub usingnamespace sfml.system; 8 | pub usingnamespace sfml.graphics; 9 | }; 10 | 11 | const ConvexShape = @This(); 12 | 13 | // Constructor/destructor 14 | 15 | /// Creates a new empty convex shape 16 | pub fn create() !ConvexShape { 17 | const shape = sf.c.sfConvexShape_create(); 18 | if (shape == null) 19 | return sf.Error.nullptrUnknownReason; 20 | 21 | return ConvexShape{ ._ptr = shape.? }; 22 | } 23 | 24 | /// Destroys a convex shape 25 | pub fn destroy(self: *ConvexShape) void { 26 | sf.c.sfConvexShape_destroy(self._ptr); 27 | self._ptr = undefined; 28 | } 29 | 30 | // Getters/setters 31 | 32 | /// Gets how many points this convex polygon shape has 33 | pub fn getPointCount(self: ConvexShape) usize { 34 | return sf.c.sfConvexShape_getPointCount(self._ptr); 35 | } 36 | /// Sets how many points this convex polygon shape has 37 | /// Must be greater than 2 to get a valid shape 38 | pub fn setPointCount(self: *ConvexShape, count: usize) void { 39 | sf.c.sfConvexShape_setPointCount(self._ptr, count); 40 | } 41 | /// Gets a point using its index (asserts the index is within range) 42 | pub fn getPoint(self: ConvexShape, index: usize) sf.Vector2f { 43 | assert(index < self.getPointCount()); 44 | return sf.Vector2f._fromCSFML(sf.c.sfConvexShape_getPoint(self._ptr, index)); 45 | } 46 | /// Sets a point using its index (asserts the index is within range) 47 | /// setPointCount must be called first 48 | /// Shape must remain convex and the points have to be ordered! 49 | pub fn setPoint(self: *ConvexShape, index: usize, point: sf.Vector2f) void { 50 | assert(index < self.getPointCount()); 51 | sf.c.sfConvexShape_setPoint(self._ptr, index, point._toCSFML()); 52 | } 53 | 54 | /// Gets the fill color of this convex shape 55 | pub fn getFillColor(self: ConvexShape) sf.Color { 56 | return sf.Color._fromCSFML(sf.c.sfConvexShape_getFillColor(self._ptr)); 57 | } 58 | /// Sets the fill color of this convex shape 59 | pub fn setFillColor(self: *ConvexShape, color: sf.Color) void { 60 | sf.c.sfConvexShape_setFillColor(self._ptr, color._toCSFML()); 61 | } 62 | 63 | /// Gets the outline color of this convex shape 64 | pub fn getOutlineColor(self: ConvexShape) sf.Color { 65 | return sf.Color._fromCSFML(sf.c.sfConvexShape_getOutlineColor(self._ptr)); 66 | } 67 | /// Sets the outline color of this convex shape 68 | pub fn setOutlineColor(self: *ConvexShape, color: sf.Color) void { 69 | sf.c.sfConvexShape_setOutlineColor(self._ptr, color._toCSFML()); 70 | } 71 | 72 | /// Gets the outline thickness of this convex shape 73 | pub fn getOutlineThickness(self: ConvexShape) f32 { 74 | return sf.c.sfConvexShape_getOutlineThickness(self._ptr); 75 | } 76 | /// Sets the outline thickness of this convex shape 77 | pub fn setOutlineThickness(self: *ConvexShape, thickness: f32) void { 78 | sf.c.sfConvexShape_setOutlineThickness(self._ptr, thickness); 79 | } 80 | 81 | /// Gets the position of this convex shape 82 | pub fn getPosition(self: ConvexShape) sf.Vector2f { 83 | return sf.Vector2f._fromCSFML(sf.c.sfConvexShape_getPosition(self._ptr)); 84 | } 85 | /// Sets the position of this convex shape 86 | pub fn setPosition(self: *ConvexShape, pos: sf.Vector2f) void { 87 | sf.c.sfConvexShape_setPosition(self._ptr, pos._toCSFML()); 88 | } 89 | /// Adds the offset to this shape's position 90 | pub fn move(self: *ConvexShape, offset: sf.Vector2f) void { 91 | sf.c.sfConvexShape_move(self._ptr, offset._toCSFML()); 92 | } 93 | 94 | /// Gets the origin of this convex shape 95 | pub fn getOrigin(self: ConvexShape) sf.Vector2f { 96 | return sf.Vector2f._fromCSFML(sf.c.sfConvexShape_getOrigin(self._ptr)); 97 | } 98 | /// Sets the origin of this convex shape 99 | pub fn setOrigin(self: *ConvexShape, origin: sf.Vector2f) void { 100 | sf.c.sfConvexShape_setOrigin(self._ptr, origin._toCSFML()); 101 | } 102 | 103 | /// Gets the rotation of this convex shape 104 | pub fn getRotation(self: ConvexShape) f32 { 105 | return sf.c.sfConvexShape_getRotation(self._ptr); 106 | } 107 | /// Sets the rotation of this convex shape 108 | pub fn setRotation(self: *ConvexShape, angle: f32) void { 109 | sf.c.sfConvexShape_setRotation(self._ptr, angle); 110 | } 111 | /// Rotates this shape by a given amount 112 | pub fn rotate(self: *ConvexShape, angle: f32) void { 113 | sf.c.sfConvexShape_rotate(self._ptr, angle); 114 | } 115 | 116 | /// Gets the texture of this shape 117 | pub fn getTexture(self: ConvexShape) ?sf.Texture { 118 | const t = sf.c.sfConvexShape_getTexture(self._ptr); 119 | if (t) |tex| { 120 | return sf.Texture{ ._const_ptr = tex }; 121 | } else return null; 122 | } 123 | /// Sets the texture of this shape 124 | pub fn setTexture(self: *ConvexShape, texture: ?sf.Texture) void { 125 | const tex = if (texture) |t| t._get() else null; 126 | sf.c.sfConvexShape_setTexture(self._ptr, tex, 0); 127 | } 128 | /// Gets the sub-shapeangle of the texture that the shape will display 129 | pub fn getTextureRect(self: ConvexShape) sf.IntRect { 130 | return sf.IntRect._fromCSFML(sf.c.sfConvexShape_getTextureRect(self._ptr)); 131 | } 132 | /// Sets the sub-shapeangle of the texture that the shape will display 133 | pub fn setTextureRect(self: *ConvexShape, shape: sf.IntRect) void { 134 | sf.c.sfConvexShape_setTextureRect(self._ptr, shape._toCSFML()); 135 | } 136 | 137 | /// Gets the bounds in the local coordinates system 138 | pub fn getLocalBounds(self: ConvexShape) sf.FloatRect { 139 | return sf.FloatRect._fromCSFML(sf.c.sfConvexShape_getLocalBounds(self._ptr)); 140 | } 141 | 142 | /// Gets the bounds in the global coordinates 143 | pub fn getGlobalBounds(self: ConvexShape) sf.FloatRect { 144 | return sf.FloatRect._fromCSFML(sf.c.sfConvexShape_getGlobalBounds(self._ptr)); 145 | } 146 | 147 | pub const draw_suffix = "ConvexShape"; 148 | 149 | /// Pointer to the csfml structure 150 | _ptr: *sf.c.sfConvexShape, 151 | 152 | test "convex shape: sane getters and setters" { 153 | const tst = @import("std").testing; 154 | 155 | var shape = try ConvexShape.create(); 156 | defer shape.destroy(); 157 | 158 | shape.setPointCount(3); 159 | shape.setPoint(0, .{ .x = 0, .y = 0 }); 160 | shape.setPoint(1, .{ .x = 1, .y = 1 }); 161 | shape.setPoint(2, .{ .x = -1, .y = 1 }); 162 | 163 | try tst.expectEqual(@as(usize, 3), shape.getPointCount()); 164 | // TODO: find out why that doesn't work 165 | //try tst.expectEqual(sf.Vector2f{ .x = 1, .y = 1 }, shape.getPoint(1)); 166 | _ = shape.getPoint(0); 167 | 168 | shape.setFillColor(sf.Color.Yellow); 169 | shape.setOutlineColor(sf.Color.Red); 170 | shape.setOutlineThickness(3); 171 | shape.setRotation(15); 172 | shape.setPosition(.{ .x = 1, .y = 2 }); 173 | shape.setOrigin(.{ .x = 20, .y = 25 }); 174 | shape.setTexture(null); //Weirdly, getTexture if texture wasn't set gives a wrong pointer 175 | 176 | try tst.expectEqual(sf.Color.Yellow, shape.getFillColor()); 177 | try tst.expectEqual(sf.Color.Red, shape.getOutlineColor()); 178 | try tst.expectEqual(@as(f32, 3), shape.getOutlineThickness()); 179 | try tst.expectEqual(@as(f32, 15), shape.getRotation()); 180 | try tst.expectEqual(sf.Vector2f{ .x = 1, .y = 2 }, shape.getPosition()); 181 | try tst.expectEqual(sf.Vector2f{ .x = 20, .y = 25 }, shape.getOrigin()); 182 | try tst.expectEqual(@as(?sf.Texture, null), shape.getTexture()); 183 | 184 | shape.rotate(5); 185 | shape.move(.{ .x = -5, .y = 5 }); 186 | 187 | try tst.expectEqual(@as(f32, 20), shape.getRotation()); 188 | try tst.expectEqual(sf.Vector2f{ .x = -4, .y = 7 }, shape.getPosition()); 189 | 190 | _ = shape.getTextureRect(); 191 | _ = shape.getGlobalBounds(); 192 | _ = shape.getLocalBounds(); 193 | } 194 | -------------------------------------------------------------------------------- /src/graphics/CircleShape.zig: -------------------------------------------------------------------------------- 1 | //! Specialized shape representing a circle. 2 | 3 | const std = @import("std"); 4 | const sf = struct { 5 | const sfml = @import("../root.zig"); 6 | pub usingnamespace sfml; 7 | pub usingnamespace sfml.system; 8 | pub usingnamespace sfml.graphics; 9 | }; 10 | 11 | const CircleShape = @This(); 12 | 13 | // Constructor/destructor 14 | 15 | /// Inits a circle shape with a radius. The circle will be white and have 30 points 16 | pub fn create(radius: f32) !CircleShape { 17 | const circle = sf.c.sfCircleShape_create(); 18 | if (circle == null) 19 | return sf.Error.nullptrUnknownReason; 20 | 21 | sf.c.sfCircleShape_setFillColor(circle, sf.c.sfWhite); 22 | sf.c.sfCircleShape_setRadius(circle, radius); 23 | 24 | return CircleShape{ ._ptr = circle.? }; 25 | } 26 | 27 | /// Destroys a circle shape 28 | pub fn destroy(self: *CircleShape) void { 29 | sf.c.sfCircleShape_destroy(self._ptr); 30 | self._ptr = undefined; 31 | } 32 | 33 | // Getters/setters 34 | 35 | /// Gets the fill color of this circle shape 36 | pub fn getFillColor(self: CircleShape) sf.Color { 37 | return sf.Color._fromCSFML(sf.c.sfCircleShape_getFillColor(self._ptr)); 38 | } 39 | /// Sets the fill color of this circle shape 40 | pub fn setFillColor(self: *CircleShape, color: sf.Color) void { 41 | sf.c.sfCircleShape_setFillColor(self._ptr, color._toCSFML()); 42 | } 43 | 44 | /// Gets the outline color of this circle shape 45 | pub fn getOutlineColor(self: CircleShape) sf.Color { 46 | return sf.Color._fromCSFML(sf.c.sfCircleShape_getOutlineColor(self._ptr)); 47 | } 48 | /// Sets the outline color of this circle shape 49 | pub fn setOutlineColor(self: *CircleShape, color: sf.Color) void { 50 | sf.c.sfCircleShape_setOutlineColor(self._ptr, color._toCSFML()); 51 | } 52 | 53 | /// Gets the outline thickness of this circle shape 54 | pub fn getOutlineThickness(self: CircleShape) f32 { 55 | return sf.c.sfCircleShape_getOutlineThickness(self._ptr); 56 | } 57 | /// Sets the outline thickness of this circle shape 58 | pub fn setOutlineThickness(self: *CircleShape, thickness: f32) void { 59 | sf.c.sfCircleShape_setOutlineThickness(self._ptr, thickness); 60 | } 61 | 62 | /// Gets the radius of this circle shape 63 | pub fn getRadius(self: CircleShape) f32 { 64 | return sf.c.sfCircleShape_getRadius(self._ptr); 65 | } 66 | /// Sets the radius of this circle shape 67 | pub fn setRadius(self: *CircleShape, radius: f32) void { 68 | sf.c.sfCircleShape_setRadius(self._ptr, radius); 69 | } 70 | 71 | /// Gets the position of this circle shape 72 | pub fn getPosition(self: CircleShape) sf.Vector2f { 73 | return sf.Vector2f._fromCSFML(sf.c.sfCircleShape_getPosition(self._ptr)); 74 | } 75 | /// Sets the position of this circle shape 76 | pub fn setPosition(self: *CircleShape, pos: sf.Vector2f) void { 77 | sf.c.sfCircleShape_setPosition(self._ptr, pos._toCSFML()); 78 | } 79 | /// Adds the offset to this shape's position 80 | pub fn move(self: *CircleShape, offset: sf.Vector2f) void { 81 | sf.c.sfCircleShape_move(self._ptr, offset._toCSFML()); 82 | } 83 | 84 | /// Gets the origin of this circle shape 85 | pub fn getOrigin(self: CircleShape) sf.Vector2f { 86 | return sf.Vector2f._fromCSFML(sf.c.sfCircleShape_getOrigin(self._ptr)); 87 | } 88 | /// Sets the origin of this circle shape 89 | pub fn setOrigin(self: *CircleShape, origin: sf.Vector2f) void { 90 | sf.c.sfCircleShape_setOrigin(self._ptr, origin._toCSFML()); 91 | } 92 | 93 | /// Gets the rotation of this circle shape 94 | pub fn getRotation(self: CircleShape) f32 { 95 | return sf.c.sfCircleShape_getRotation(self._ptr); 96 | } 97 | /// Sets the rotation of this circle shape 98 | pub fn setRotation(self: *CircleShape, angle: f32) void { 99 | sf.c.sfCircleShape_setRotation(self._ptr, angle); 100 | } 101 | /// Rotates this shape by a given amount 102 | pub fn rotate(self: *CircleShape, angle: f32) void { 103 | sf.c.sfCircleShape_rotate(self._ptr, angle); 104 | } 105 | 106 | /// Gets the texture of this shape 107 | pub fn getTexture(self: CircleShape) ?sf.Texture { 108 | const t = sf.c.sfCircleShape_getTexture(self._ptr); 109 | if (t) |tex| { 110 | return sf.Texture{ ._const_ptr = tex }; 111 | } else return null; 112 | } 113 | /// Sets the texture of this shape 114 | pub fn setTexture(self: *CircleShape, texture: ?sf.Texture) void { 115 | const tex = if (texture) |t| t._get() else null; 116 | sf.c.sfCircleShape_setTexture(self._ptr, tex, 0); 117 | } 118 | /// Gets the sub-rectangle of the texture that the shape will display 119 | pub fn getTextureRect(self: CircleShape) sf.IntRect { 120 | return sf.IntRect._fromCSFML(sf.c.sfCircleShape_getTextureRect(self._ptr)); 121 | } 122 | /// Sets the sub-rectangle of the texture that the shape will display 123 | pub fn setTextureRect(self: *CircleShape, rect: sf.IntRect) void { 124 | sf.c.sfCircleShape_setTextureRect(self._ptr, rect._toCSFML()); 125 | } 126 | 127 | /// Gets the bounds of this shape in the local coordinates system 128 | pub fn getLocalBounds(self: CircleShape) sf.FloatRect { 129 | return sf.FloatRect._fromCSFML(sf.c.sfCircleShape_getLocalBounds(self._ptr)); 130 | } 131 | 132 | /// Gets the bounds of this shape in the global coordinates 133 | pub fn getGlobalBounds(self: CircleShape) sf.FloatRect { 134 | return sf.FloatRect._fromCSFML(sf.c.sfCircleShape_getGlobalBounds(self._ptr)); 135 | } 136 | 137 | /// Gets the point count of this shape 138 | /// For a circle shape, this value can also be set 139 | pub fn getPointCount(self: CircleShape) usize { 140 | return sf.c.sfCircleShape_getPointCount(self._ptr); 141 | } 142 | 143 | /// Gets a point of the shape using its index 144 | /// In debug mode, this index is asserted to be inbounds 145 | pub fn getPoint(self: CircleShape, index: usize) sf.Vector2f { 146 | std.debug.assert(index < self.getPointCount()); 147 | return sf.Vector2f._fromCSFML(sf.c.sfCircleShape_getPoint(self._ptr, index)); 148 | } 149 | 150 | /// Sets the point count of this shape 151 | /// The default value is 30 152 | pub fn setPointCount(self: *CircleShape, point_count: usize) void { 153 | sf.c.sfCircleShape_setPointCount(self._ptr, point_count); 154 | } 155 | 156 | pub const draw_suffix = "CircleShape"; 157 | 158 | /// Pointer to the csfml structure 159 | _ptr: *sf.c.sfCircleShape, 160 | 161 | test "circle shape: sane getters and setters" { 162 | const tst = std.testing; 163 | 164 | var circle = try CircleShape.create(30); 165 | defer circle.destroy(); 166 | 167 | try tst.expectEqual(@as(usize, 30), circle.getPointCount()); 168 | 169 | circle.setFillColor(sf.Color.Yellow); 170 | circle.setOutlineColor(sf.Color.Red); 171 | circle.setOutlineThickness(3); 172 | circle.setRadius(50); 173 | circle.setRotation(15); 174 | circle.setPosition(.{ .x = 1, .y = 2 }); 175 | circle.setOrigin(.{ .x = 20, .y = 25 }); 176 | circle.setTexture(null); 177 | circle.setPointCount(40); 178 | 179 | try tst.expectEqual(sf.Color.Yellow, circle.getFillColor()); 180 | try tst.expectEqual(sf.Color.Red, circle.getOutlineColor()); 181 | try tst.expectEqual(@as(f32, 3), circle.getOutlineThickness()); 182 | try tst.expectEqual(@as(f32, 50), circle.getRadius()); 183 | try tst.expectEqual(@as(f32, 15), circle.getRotation()); 184 | try tst.expectEqual(sf.Vector2f{ .x = 1, .y = 2 }, circle.getPosition()); 185 | try tst.expectEqual(sf.Vector2f{ .x = 20, .y = 25 }, circle.getOrigin()); 186 | try tst.expectEqual(@as(?sf.Texture, null), circle.getTexture()); 187 | try tst.expectEqual(@as(usize, 40), circle.getPointCount()); 188 | 189 | circle.rotate(5); 190 | circle.move(.{ .x = -5, .y = 5 }); 191 | 192 | try tst.expectEqual(@as(f32, 20), circle.getRotation()); 193 | try tst.expectEqual(sf.Vector2f{ .x = -4, .y = 7 }, circle.getPosition()); 194 | 195 | _ = circle.getGlobalBounds(); 196 | _ = circle.getLocalBounds(); 197 | _ = circle.getTextureRect(); 198 | _ = circle.getPoint(39); 199 | } 200 | -------------------------------------------------------------------------------- /src/graphics/RenderWindow.zig: -------------------------------------------------------------------------------- 1 | //! Window that can serve as a target for 2D drawing. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | pub usingnamespace sfml.window; 9 | }; 10 | 11 | const RenderWindow = @This(); 12 | 13 | // Constructor/destructor 14 | 15 | /// Inits a render window with a size, a bits per pixel (most put 32) a title, a style and context settings 16 | pub fn create(size: sf.Vector2u, bpp: usize, title: [:0]const u8, style: u32, settings: ?sf.ContextSettings) !RenderWindow { 17 | var ret: RenderWindow = undefined; 18 | 19 | const mode: sf.c.sfVideoMode = .{ 20 | .width = @as(c_uint, @intCast(size.x)), 21 | .height = @as(c_uint, @intCast(size.y)), 22 | .bitsPerPixel = @as(c_uint, @intCast(bpp)), 23 | }; 24 | 25 | const c_settings = if (settings) |s| s._toCSFML() else null; 26 | const window = sf.c.sfRenderWindow_create(mode, @as([*c]const u8, @ptrCast(title)), style, if (c_settings) |s| &s else null); 27 | 28 | if (window) |w| { 29 | ret._ptr = w; 30 | } else return sf.Error.windowCreationFailed; 31 | 32 | return ret; 33 | } 34 | /// Inits a render window with a size and a title 35 | /// The window will have the default style 36 | pub fn createDefault(size: sf.Vector2u, title: [:0]const u8) !RenderWindow { 37 | var ret: RenderWindow = undefined; 38 | 39 | const mode: sf.c.sfVideoMode = .{ 40 | .width = @as(c_uint, @intCast(size.x)), 41 | .height = @as(c_uint, @intCast(size.y)), 42 | .bitsPerPixel = 32, 43 | }; 44 | 45 | const window = sf.c.sfRenderWindow_create(mode, @as([*c]const u8, @ptrCast(title)), sf.window.Style.defaultStyle, null); 46 | 47 | if (window) |w| { 48 | ret._ptr = w; 49 | } else return sf.Error.windowCreationFailed; 50 | 51 | return ret; 52 | } 53 | /// Inits a rendering plane from a window handle. The handle can actually be to any drawing surface. 54 | pub fn createFromHandle(handle: sf.c.sfWindowHandle, settings: ?sf.ContextSettings) !RenderWindow { 55 | const c_settings = if (settings) |s| s._toCSFML() else null; 56 | const window = sf.c.sfRenderWindow_createFromHandle(handle, if (c_settings) |s| &s else null); 57 | 58 | if (window) |w| { 59 | return .{ ._ptr = w }; 60 | } else return sf.Error.windowCreationFailed; 61 | } 62 | 63 | /// Destroys this window object 64 | pub fn destroy(self: *RenderWindow) void { 65 | sf.c.sfRenderWindow_destroy(self._ptr); 66 | self._ptr = undefined; 67 | } 68 | 69 | // Event related 70 | 71 | /// Returns true if this window is still open 72 | pub fn isOpen(self: RenderWindow) bool { 73 | return sf.c.sfRenderWindow_isOpen(self._ptr) != 0; 74 | } 75 | 76 | /// Closes this window 77 | pub fn close(self: *RenderWindow) void { 78 | sf.c.sfRenderWindow_close(self._ptr); 79 | } 80 | 81 | /// Gets an event from the queue, returns null is theres none 82 | /// Use while on this to get all the events in your game loop 83 | pub fn pollEvent(self: *RenderWindow) ?sf.window.Event { 84 | var event: sf.c.sfEvent = undefined; 85 | if (sf.c.sfRenderWindow_pollEvent(self._ptr, &event) == 0) 86 | return null; 87 | 88 | // Skip sfEvtMouseWheelMoved to avoid sending mouseWheelScrolled twice 89 | if (event.type == sf.c.sfEvtMouseWheelMoved) { 90 | return self.pollEvent(); 91 | } 92 | return sf.window.Event._fromCSFML(event); 93 | } 94 | 95 | /// block until an event occurs 96 | /// can be used like: 97 | /// if (window.waitEvent()) |event| {} 98 | /// or: 99 | /// while (window.waitEvent()) |event| {} 100 | pub fn waitEvent(self: *RenderWindow) ?sf.window.Event { 101 | var event: sf.c.sfEvent = undefined; 102 | if (sf.c.sfRenderWindow_waitEvent(self._ptr, &event) != 0) { 103 | return sf.window.Event._fromCSFML(event); 104 | } else return null; 105 | } 106 | 107 | // Drawing functions 108 | 109 | /// Clears the drawing screen with a color 110 | pub fn clear(self: *RenderWindow, color: sf.Color) void { 111 | sf.c.sfRenderWindow_clear(self._ptr, color._toCSFML()); 112 | } 113 | 114 | /// Displays what has been drawn on the render area 115 | pub fn display(self: *RenderWindow) void { 116 | sf.c.sfRenderWindow_display(self._ptr); 117 | } 118 | 119 | /// Draw something on the screen (won't be visible until display is called) 120 | /// You can pass a render state or null for default 121 | pub fn draw(self: *RenderWindow, to_draw: anytype, states: ?sf.RenderStates) void { 122 | const draw_fn = @field(sf.c, "sfRenderWindow_draw" ++ @TypeOf(to_draw).draw_suffix); 123 | if (states) |s| { 124 | var cstates = s._toCSFML(); 125 | draw_fn(self._ptr, to_draw._ptr, &cstates); 126 | } else draw_fn(self._ptr, to_draw._ptr, null); 127 | } 128 | 129 | // Getters/setters 130 | 131 | /// Gets the current view of the window 132 | /// Unlike in SFML, you don't get a const pointer but a copy 133 | pub fn getView(self: RenderWindow) sf.View { 134 | return sf.View._fromCSFML(sf.c.sfRenderWindow_getView(self._ptr).?); 135 | } 136 | /// Gets the default view of this window 137 | /// Unlike in SFML, you don't get a const pointer but a copy 138 | pub fn getDefaultView(self: RenderWindow) sf.View { 139 | return sf.View._fromCSFML(sf.c.sfRenderWindow_getDefaultView(self._ptr).?); 140 | } 141 | /// Sets the view of this window 142 | pub fn setView(self: *RenderWindow, view: sf.View) void { 143 | const cview = view._toCSFML(); 144 | defer sf.c.sfView_destroy(cview); 145 | sf.c.sfRenderWindow_setView(self._ptr, cview); 146 | } 147 | /// Gets the viewport of this render target 148 | pub fn getViewport(self: RenderWindow, view: sf.View) sf.IntRect { 149 | return sf.IntRect._fromCSFML(sf.c.sfRenderWindow_getViewport(self._ptr, view._toCSFML())); 150 | } 151 | 152 | /// Set mouse cursor grabbing 153 | pub fn setMouseCursorGrabbed(self: *RenderWindow, grab: bool) void { 154 | sf.c.sfRenderWindow_setMouseCursorGrabbed(self._ptr, @intFromBool(grab)); 155 | } 156 | 157 | /// Set mouse cursor visibility 158 | pub fn setMouseCursorVisible(self: *RenderWindow, visible: bool) void { 159 | sf.c.sfRenderWindow_setMouseCursorVisible(self._ptr, @intFromBool(visible)); 160 | } 161 | 162 | /// Set the displayed cursor to a native system cursor 163 | pub fn setMouseCursor(self: *RenderWindow, cursor: sf.Cursor) void { 164 | sf.c.sfRenderWindow_setMouseCursor(self._ptr, cursor._ptr); 165 | } 166 | 167 | /// Set automatic key-repeat 168 | pub fn setKeyRepeatEnabled(self: *RenderWindow, enabled: bool) void { 169 | sf.c.sfRenderWindow_setKeyRepeatEnabled(self._ptr, @intFromBool(enabled)); 170 | } 171 | 172 | /// Gets the size of this window 173 | pub fn getSize(self: RenderWindow) sf.Vector2u { 174 | return sf.Vector2u._fromCSFML(sf.c.sfRenderWindow_getSize(self._ptr)); 175 | } 176 | /// Sets the size of this window 177 | pub fn setSize(self: *RenderWindow, size: sf.Vector2u) void { 178 | sf.c.sfRenderWindow_setSize(self._ptr, size._toCSFML()); 179 | } 180 | 181 | /// Gets the position of this window 182 | pub fn getPosition(self: RenderWindow) sf.Vector2i { 183 | return sf.Vector2i._fromCSFML(sf.c.sfRenderWindow_getPosition(self._ptr)); 184 | } 185 | /// Sets the position of this window 186 | pub fn setPosition(self: *RenderWindow, pos: sf.Vector2i) void { 187 | sf.c.sfRenderWindow_setPosition(self._ptr, pos._toCSFML()); 188 | } 189 | 190 | /// Sets the title of this window 191 | pub fn setTitle(self: *RenderWindow, title: [:0]const u8) void { 192 | sf.c.sfRenderWindow_setTitle(self._ptr, title); 193 | } 194 | /// Sets the title of this window, in unicode 195 | pub fn setTitleUnicode(self: *RenderWindow, title_unicode: [:0]const u32) void { 196 | sf.c.sfRenderWindow_setUnicodeTitle(self._ptr, title_unicode.ptr); 197 | } 198 | 199 | /// Sets the windows's framerate limit 200 | pub fn setFramerateLimit(self: *RenderWindow, fps: c_uint) void { 201 | sf.c.sfRenderWindow_setFramerateLimit(self._ptr, fps); 202 | } 203 | /// Enables or disables vertical sync 204 | pub fn setVerticalSyncEnabled(self: *RenderWindow, enabled: bool) void { 205 | sf.c.sfRenderWindow_setVerticalSyncEnabled(self._ptr, @intFromBool(enabled)); 206 | } 207 | 208 | /// Convert a point from target coordinates to world coordinates, using the current view (or the specified view) 209 | pub fn mapPixelToCoords(self: RenderWindow, pixel: sf.Vector2i, view: ?sf.View) sf.Vector2f { 210 | if (view) |v| { 211 | const cview = v._toCSFML(); 212 | defer sf.c.sfView_destroy(cview); 213 | return sf.Vector2f._fromCSFML(sf.c.sfRenderWindow_mapPixelToCoords(self._ptr, pixel._toCSFML(), cview)); 214 | } else return sf.Vector2f._fromCSFML(sf.c.sfRenderWindow_mapPixelToCoords(self._ptr, pixel._toCSFML(), null)); 215 | } 216 | /// Convert a point from world coordinates to target coordinates, using the current view (or the specified view) 217 | pub fn mapCoordsToPixel(self: RenderWindow, coords: sf.Vector2f, view: ?sf.View) sf.Vector2i { 218 | if (view) |v| { 219 | const cview = v._toCSFML(); 220 | defer sf.c.sfView_destroy(cview); 221 | return sf.Vector2i._fromCSFML(sf.c.sfRenderWindow_mapCoordsToPixel(self._ptr, coords._toCSFML(), cview)); 222 | } else return sf.Vector2i._fromCSFML(sf.c.sfRenderWindow_mapCoordsToPixel(self._ptr, coords._toCSFML(), null)); 223 | } 224 | 225 | /// Pointer to the csfml structure 226 | _ptr: *sf.c.sfRenderWindow 227 | -------------------------------------------------------------------------------- /src/graphics/Text.zig: -------------------------------------------------------------------------------- 1 | //! Graphical text that can be drawn to a render target. 2 | 3 | const std = @import("std"); 4 | const sf = struct { 5 | const sfml = @import("../root.zig"); 6 | pub usingnamespace sfml; 7 | pub usingnamespace sfml.system; 8 | pub usingnamespace sfml.graphics; 9 | }; 10 | 11 | const Text = @This(); 12 | 13 | pub const TextStyle = enum(c_uint) { 14 | regular = 0, 15 | bold = 1 << 0, 16 | italic = 1 << 1, 17 | underlined = 1 << 2, 18 | strike_through = 1 << 3, 19 | }; 20 | 21 | // Constructor/destructor 22 | 23 | /// Inits an empty text 24 | pub fn create() !Text { 25 | const text = sf.c.sfText_create(); 26 | if (text == null) 27 | return sf.Error.nullptrUnknownReason; 28 | return Text{ ._ptr = text.? }; 29 | } 30 | /// Inits a text with content 31 | pub fn createWithText(string: [:0]const u8, font: sf.Font, character_size: usize) !Text { 32 | const text = sf.c.sfText_create(); 33 | if (text == null) 34 | return sf.Error.nullptrUnknownReason; 35 | sf.c.sfText_setFont(text, font._ptr); 36 | sf.c.sfText_setCharacterSize(text, @as(c_uint, @intCast(character_size))); 37 | sf.c.sfText_setString(text, string); 38 | return Text{ ._ptr = text.? }; 39 | } 40 | /// Inits a text with unicode content 41 | pub fn createWithTextUnicode(unicode: [:0]const u32, font: sf.Font, character_size: usize) !Text { 42 | const text = sf.c.sfText_create(); 43 | if (text == null) 44 | return sf.Error.nullptrUnknownReason; 45 | sf.c.sfText_setFont(text, font._ptr); 46 | sf.c.sfText_setCharacterSize(text, @as(c_uint, @intCast(character_size))); 47 | sf.c.sfText_setUnicodeString(text, unicode); 48 | return Text{ ._ptr = text.? }; 49 | } 50 | /// Destroys a text 51 | pub fn destroy(self: *Text) void { 52 | sf.c.sfText_destroy(self._ptr); 53 | self._ptr = undefined; 54 | } 55 | 56 | // Getters/setters 57 | 58 | /// Sets the content of this text 59 | pub fn setString(self: *Text, string: [:0]const u8) void { 60 | sf.c.sfText_setString(self._ptr, string); 61 | } 62 | /// Sets the content of this text, but using fmt 63 | pub fn setStringFmt(self: *Text, comptime format: []const u8, args: anytype) std.fmt.AllocPrintError!void { 64 | const alloc = std.heap.c_allocator; 65 | 66 | const buf = try std.fmt.allocPrintZ(alloc, format, args); 67 | defer alloc.free(buf); 68 | 69 | sf.c.sfText_setString(self._ptr, buf); 70 | } 71 | /// Sets the string of this text, in unicode 72 | pub fn setStringUnicode(self: *Text, string_unicode: [:0]const u32) void { 73 | sf.c.sfText_setUnicodeString(self._ptr, string_unicode.ptr); 74 | } 75 | 76 | /// Sets the font of this text 77 | pub fn setFont(self: *Text, font: sf.Font) void { 78 | sf.c.sfText_setFont(self._ptr, font._ptr); 79 | } 80 | 81 | /// Gets the character size of this text 82 | pub fn getCharacterSize(self: Text) usize { 83 | return @as(usize, @intCast(sf.c.sfText_getCharacterSize(self._ptr))); 84 | } 85 | /// Sets the character size of this text 86 | pub fn setCharacterSize(self: *Text, character_size: usize) void { 87 | sf.c.sfText_setCharacterSize(self._ptr, @as(c_uint, @intCast(character_size))); 88 | } 89 | 90 | /// Gets the fill color of this text 91 | pub fn getFillColor(self: Text) sf.Color { 92 | return sf.Color._fromCSFML(sf.c.sfText_getFillColor(self._ptr)); 93 | } 94 | /// Sets the fill color of this text 95 | pub fn setFillColor(self: *Text, color: sf.Color) void { 96 | sf.c.sfText_setFillColor(self._ptr, color._toCSFML()); 97 | } 98 | 99 | /// Gets the outline color of this text 100 | pub fn getOutlineColor(self: Text) sf.Color { 101 | return sf.Color._fromCSFML(sf.c.sfText_getOutlineColor(self._ptr)); 102 | } 103 | /// Sets the outline color of this text 104 | pub fn setOutlineColor(self: *Text, color: sf.Color) void { 105 | sf.c.sfText_setOutlineColor(self._ptr, color._toCSFML()); 106 | } 107 | 108 | /// Gets the outline thickness of this text 109 | pub fn getOutlineThickness(self: Text) f32 { 110 | return sf.c.sfText_getOutlineThickness(self._ptr); 111 | } 112 | /// Sets the outline thickness of this text 113 | pub fn setOutlineThickness(self: *Text, thickness: f32) void { 114 | sf.c.sfText_setOutlineThickness(self._ptr, thickness); 115 | } 116 | 117 | /// Gets the position of this text 118 | pub fn getPosition(self: Text) sf.Vector2f { 119 | return sf.Vector2f._fromCSFML(sf.c.sfText_getPosition(self._ptr)); 120 | } 121 | /// Sets the position of this text 122 | pub fn setPosition(self: *Text, pos: sf.Vector2f) void { 123 | sf.c.sfText_setPosition(self._ptr, pos._toCSFML()); 124 | } 125 | /// Adds the offset to this text 126 | pub fn move(self: *Text, offset: sf.Vector2f) void { 127 | sf.c.sfText_move(self._ptr, offset._toCSFML()); 128 | } 129 | 130 | /// Gets the origin of this text 131 | pub fn getOrigin(self: Text) sf.Vector2f { 132 | return sf.Vector2f._fromCSFML(sf.c.sfText_getOrigin(self._ptr)); 133 | } 134 | /// Sets the origin of this text 135 | pub fn setOrigin(self: *Text, origin: sf.Vector2f) void { 136 | sf.c.sfText_setOrigin(self._ptr, origin._toCSFML()); 137 | } 138 | 139 | /// Gets the rotation of this text 140 | pub fn getRotation(self: Text) f32 { 141 | return sf.c.sfText_getRotation(self._ptr); 142 | } 143 | /// Sets the rotation of this text 144 | pub fn setRotation(self: *Text, angle: f32) void { 145 | sf.c.sfText_setRotation(self._ptr, angle); 146 | } 147 | /// Rotates this text by a given amount 148 | pub fn rotate(self: *Text, angle: f32) void { 149 | sf.c.sfText_rotate(self._ptr, angle); 150 | } 151 | 152 | /// Gets the scale of this text 153 | pub fn getScale(self: Text) sf.Vector2f { 154 | return sf.Vector2f._fromCSFML(sf.c.sfText_getScale(self._ptr)); 155 | } 156 | /// Sets the scale of this text 157 | pub fn setScale(self: *Text, factor: sf.Vector2f) void { 158 | sf.c.sfText_setScale(self._ptr, factor._toCSFML()); 159 | } 160 | /// Scales this text 161 | pub fn scale(self: *Text, factor: sf.Vector2f) void { 162 | sf.c.sfText_scale(self._ptr, factor._toCSFML()); 163 | } 164 | 165 | /// return the position of the index-th character 166 | pub fn findCharacterPos(self: Text, index: usize) sf.Vector2f { 167 | return sf.Vector2f._fromCSFML(sf.c.sfText_findCharacterPos(self._ptr, index)); 168 | } 169 | 170 | /// Gets the letter spacing factor 171 | pub fn getLetterSpacing(self: Text) f32 { 172 | return sf.c.sfText_getLetterSpacing(self._ptr); 173 | } 174 | /// Sets the letter spacing factor 175 | pub fn setLetterSpacing(self: *Text, spacing_factor: f32) void { 176 | sf.c.sfText_setLetterSpacing(self._ptr, spacing_factor); 177 | } 178 | 179 | /// Gets the line spacing factor 180 | pub fn getLineSpacing(self: Text) f32 { 181 | return sf.c.sfText_getLineSpacing(self._ptr); 182 | } 183 | /// Sets the line spacing factor 184 | pub fn setLineSpacing(self: *Text, spacing_factor: f32) void { 185 | sf.c.sfText_setLineSpacing(self._ptr, spacing_factor); 186 | } 187 | 188 | /// Sets the text's style 189 | pub fn setStyle(self: *Text, style: TextStyle) void { 190 | sf.c.sfText_setStyle(self._ptr, @intFromEnum(style)); 191 | } 192 | /// Gets the text's style 193 | pub fn getStyle(self: Text) TextStyle { 194 | return @as(TextStyle, @enumFromInt(sf.c.sfText_getStyle(self._ptr))); 195 | } 196 | 197 | /// Gets the local bounding rectangle of the text 198 | pub fn getLocalBounds(self: Text) sf.FloatRect { 199 | return sf.FloatRect._fromCSFML(sf.c.sfText_getLocalBounds(self._ptr)); 200 | } 201 | /// Gets the global bounding rectangle of the text 202 | pub fn getGlobalBounds(self: Text) sf.FloatRect { 203 | return sf.FloatRect._fromCSFML(sf.c.sfText_getGlobalBounds(self._ptr)); 204 | } 205 | 206 | /// Puts the origin at the center of the bounding box 207 | pub fn centerOrigin(self: *Text) void { 208 | const bounds = self.getLocalBounds(); 209 | self.setOrigin(sf.Vector2f{ .x = bounds.width / 2, .y = bounds.height / 2 }); 210 | } 211 | 212 | // TODO 213 | //pub const getTransform = @compileError("Function is not implemented yet."); 214 | //pub const getInverseTransform = @compileError("Function is not implemented yet."); 215 | 216 | pub const draw_suffix = "Text"; 217 | 218 | /// Pointer to the csfml font 219 | _ptr: *sf.c.sfText, 220 | 221 | test "text: sane getters and setters" { 222 | const tst = std.testing; 223 | 224 | var text = try Text.create(); 225 | defer text.destroy(); 226 | 227 | text.setString("hello"); 228 | try text.setStringFmt("An int: {}", .{42}); 229 | text.setFillColor(sf.Color.Yellow); 230 | text.setOutlineColor(sf.Color.Red); 231 | text.setOutlineThickness(2); 232 | text.setCharacterSize(10); 233 | text.setRotation(15); 234 | text.setPosition(.{ .x = 1, .y = 2 }); 235 | text.setOrigin(.{ .x = 20, .y = 25 }); 236 | text.setScale(.{ .x = 2, .y = 2 }); 237 | 238 | text.rotate(5); 239 | text.move(.{ .x = -5, .y = 5 }); 240 | text.scale(.{ .x = 2, .y = 3 }); 241 | 242 | try tst.expectEqual(sf.Color.Yellow, text.getFillColor()); 243 | try tst.expectEqual(sf.Color.Red, text.getOutlineColor()); 244 | try tst.expectEqual(@as(f32, 2), text.getOutlineThickness()); 245 | try tst.expectEqual(@as(usize, 10), text.getCharacterSize()); 246 | try tst.expectEqual(@as(f32, 20), text.getRotation()); 247 | try tst.expectEqual(sf.Vector2f{ .x = -4, .y = 7 }, text.getPosition()); 248 | try tst.expectEqual(sf.Vector2f{ .x = 20, .y = 25 }, text.getOrigin()); 249 | try tst.expectEqual(sf.Vector2f{ .x = 4, .y = 6 }, text.getScale()); 250 | 251 | _ = text.getLocalBounds(); 252 | _ = text.getGlobalBounds(); 253 | } 254 | -------------------------------------------------------------------------------- /src/graphics/texture.zig: -------------------------------------------------------------------------------- 1 | //! Image living on the graphics card that can be used for drawing. 2 | 3 | const sf = struct { 4 | const sfml = @import("../root.zig"); 5 | pub usingnamespace sfml; 6 | pub usingnamespace sfml.system; 7 | pub usingnamespace sfml.graphics; 8 | }; 9 | 10 | const std = @import("std"); 11 | 12 | pub const Texture = union(enum) { 13 | // Constructor/destructor 14 | 15 | /// Creates a texture from nothing 16 | pub fn create(size: sf.Vector2u) !Texture { 17 | const tex = sf.c.sfTexture_create(@as(c_uint, @intCast(size.x)), @as(c_uint, @intCast(size.y))); 18 | if (tex) |t| { 19 | return Texture{ ._ptr = t }; 20 | } else return sf.Error.resourceLoadingError; 21 | } 22 | /// Loads a texture from a file 23 | pub fn createFromFile(path: [:0]const u8) !Texture { 24 | const tex = sf.c.sfTexture_createFromFile(path, null); 25 | if (tex) |t| { 26 | return Texture{ ._ptr = t }; 27 | } else return sf.Error.resourceLoadingError; 28 | } 29 | /// Loads a texture from a file in memory 30 | pub fn createFromMemory(data: []const u8, area: sf.IntRect) !Texture { 31 | const tex = sf.c.sfTexture_createFromMemory(@as(?*const anyopaque, @ptrCast(data.ptr)), data.len, &area._toCSFML()); 32 | if (tex) |t| { 33 | return Texture{ ._ptr = t }; 34 | } else return sf.Error.resourceLoadingError; 35 | } 36 | /// Creates an texture from an image 37 | pub fn createFromImage(image: sf.Image, area: ?sf.IntRect) !Texture { 38 | const tex = if (area) |a| 39 | sf.c.sfTexture_createFromImage(image._ptr, &a._toCSFML()) 40 | else 41 | sf.c.sfTexture_createFromImage(image._ptr, null); 42 | 43 | if (tex) |t| { 44 | return Texture{ ._ptr = t }; 45 | } else return sf.Error.nullptrUnknownReason; 46 | } 47 | 48 | /// Loads a texture from a file 49 | pub fn createSrgbFromFile(path: [:0]const u8) !Texture { 50 | const tex = sf.c.sfTexture_createSrgbFromFile(path, null); 51 | if (tex) |t| { 52 | return Texture{ ._ptr = t }; 53 | } else return sf.Error.resourceLoadingError; 54 | } 55 | /// Loads a texture from a file in memory 56 | pub fn createSrgbFromMemory(data: []const u8, area: sf.IntRect) !Texture { 57 | const tex = sf.c.sfTexture_createSrgbFromMemory(@as(?*const anyopaque, @ptrCast(data.ptr)), data.len, &area._toCSFML()); 58 | if (tex) |t| { 59 | return Texture{ ._ptr = t }; 60 | } else return sf.Error.resourceLoadingError; 61 | } 62 | /// Creates an texture from an image 63 | pub fn createSrgbFromImage(image: sf.Image, area: ?sf.IntRect) !Texture { 64 | const tex = sf.c.sfTexture_createSrgbFromImage(image._ptr, if (area) |a| &a._toCSFML() else null); 65 | 66 | if (tex) |t| { 67 | return Texture{ ._ptr = t }; 68 | } else return sf.Error.nullptrUnknownReason; 69 | } 70 | 71 | /// Destroys a texture 72 | /// You can only destroy non const textures 73 | pub fn destroy(self: *Texture) void { 74 | std.debug.assert(self.* == ._ptr); 75 | sf.c.sfTexture_destroy(self._ptr); 76 | self._ptr = undefined; 77 | } 78 | 79 | // Getters/Setters 80 | 81 | /// Gets a const pointer to this texture 82 | /// For inner workings 83 | pub fn _get(self: Texture) *const sf.c.sfTexture { 84 | return switch (self) { 85 | ._ptr => self._ptr, 86 | ._const_ptr => self._const_ptr, 87 | }; 88 | } 89 | 90 | /// Clones this texture (the clone won't be const) 91 | pub fn copy(self: Texture) !Texture { 92 | const cpy = sf.c.sfTexture_copy(self._get()); 93 | if (cpy == null) 94 | return sf.Error.nullptrUnknownReason; 95 | return Texture{ ._ptr = cpy.? }; 96 | } 97 | /// Copy this texture to an image in ram 98 | pub fn copyToImage(self: Texture) sf.Image { 99 | return .{ ._ptr = sf.c.sfTexture_copyToImage(self._get()).? }; 100 | } 101 | 102 | /// Makes this texture constant (I don't know why you would do that) 103 | pub fn makeConst(self: *Texture) void { 104 | self.* = Texture{ ._const_ptr = self._get() }; 105 | } 106 | 107 | /// Gets a const reference to this texture 108 | pub fn getConst(self: Texture) Texture { 109 | var cpy = self; 110 | cpy.makeConst(); 111 | return cpy; 112 | } 113 | 114 | /// Gets the size of this image 115 | pub fn getSize(self: Texture) sf.Vector2u { 116 | const size = sf.c.sfTexture_getSize(self._get()); 117 | return sf.Vector2u{ .x = size.x, .y = size.y }; 118 | } 119 | /// Gets the pixel count of this image 120 | pub fn getPixelCount(self: Texture) usize { 121 | const dim = self.getSize(); 122 | return dim.x * dim.y; 123 | } 124 | 125 | /// Updates the pixels of the image from an array of pixels (colors) 126 | pub fn updateFromPixels(self: *Texture, pixels: []const sf.Color, zone: ?sf.Rect(c_uint)) !void { 127 | if (self.* == ._const_ptr) 128 | @panic("Can't set pixels on a const texture"); 129 | if (self.isSrgb()) 130 | @panic("Updating an srgb from a pixel array isn't implemented"); 131 | 132 | var real_zone: sf.Rect(c_uint) = undefined; 133 | const size = self.getSize(); 134 | 135 | if (zone) |z| { 136 | // Check if the given zone is fully inside the image 137 | const intersection = z.intersects(sf.Rect(c_uint).init(0, 0, size.x, size.y)); 138 | 139 | if (intersection) |i| { 140 | if (!i.equals(z)) 141 | return sf.Error.areaDoesNotFit; 142 | } else return sf.Error.areaDoesNotFit; 143 | 144 | real_zone = z; 145 | } else { 146 | real_zone.left = 0; 147 | real_zone.top = 0; 148 | real_zone.width = size.x; 149 | real_zone.height = size.y; 150 | } 151 | // Check if there is enough data 152 | if (pixels.len < real_zone.width * real_zone.height) 153 | return sf.Error.notEnoughData; 154 | 155 | sf.c.sfTexture_updateFromPixels(self._ptr, @as([*]const u8, @ptrCast(pixels.ptr)), real_zone.width, real_zone.height, real_zone.left, real_zone.top); 156 | } 157 | /// Updates the pixels of the image from an other texture 158 | pub fn updateFromTexture(self: *Texture, other: Texture, copy_pos: ?sf.Vector2u) void { 159 | if (self.* == ._const_ptr) 160 | @panic("Can't set pixels on a const texture"); 161 | 162 | const pos = if (copy_pos) |a| a else sf.Vector2u{ .x = 0, .y = 0 }; 163 | const max = other.getSize().add(pos); 164 | const size = self.getSize(); 165 | 166 | std.debug.assert(max.x <= size.x and max.y <= size.y); 167 | 168 | sf.c.sfTexture_updateFromTexture(self._ptr, other._get(), pos.x, pos.y); 169 | } 170 | /// Updates the pixels of the image from an image 171 | pub fn updateFromImage(self: *Texture, image: sf.Image, copy_pos: ?sf.Vector2u) void { 172 | if (self.* == ._const_ptr) 173 | @panic("Can't set pixels on a const texture"); 174 | 175 | const pos = if (copy_pos) |a| a else sf.Vector2u{ .x = 0, .y = 0 }; 176 | const max = image.getSize().add(pos); 177 | const size = self.getSize(); 178 | 179 | std.debug.assert(max.x <= size.x and max.y <= size.y); 180 | 181 | sf.c.sfTexture_updateFromImage(self._ptr, image._ptr, pos.x, pos.y); 182 | } 183 | 184 | /// Tells whether or not this texture is to be smoothed 185 | pub fn isSmooth(self: Texture) bool { 186 | return sf.c.sfTexture_isSmooth(self._ptr) != 0; 187 | } 188 | /// Enables or disables texture smoothing 189 | pub fn setSmooth(self: *Texture, smooth: bool) void { 190 | if (self.* == ._const_ptr) 191 | @panic("Can't set properties on a const texture"); 192 | 193 | sf.c.sfTexture_setSmooth(self._ptr, @intFromBool(smooth)); 194 | } 195 | 196 | /// Tells whether or not this texture should repeat when rendering outside its bounds 197 | pub fn isRepeated(self: Texture) bool { 198 | return sf.c.sfTexture_isRepeated(self._ptr) != 0; 199 | } 200 | /// Enables or disables texture repeating 201 | pub fn setRepeated(self: *Texture, repeated: bool) void { 202 | if (self.* == ._const_ptr) 203 | @panic("Can't set properties on a const texture"); 204 | 205 | sf.c.sfTexture_setRepeated(self._ptr, @intFromBool(repeated)); 206 | } 207 | 208 | /// Tells whether or not this texture has colors in the SRGB format 209 | /// SRGB functions arent implemented yet 210 | pub fn isSrgb(self: Texture) bool { 211 | return sf.c.sfTexture_isSrgb(self._ptr) != 0; 212 | } 213 | 214 | /// Swaps this texture's contents with an other texture 215 | pub fn swap(self: *Texture, other: *Texture) void { 216 | if (self.* == ._const_ptr or other.* == ._const_ptr) 217 | @panic("Texture swapping must be done between two non const textures"); 218 | 219 | sf.c.sfTexture_swap(self._ptr, other._ptr); 220 | } 221 | 222 | // Others 223 | 224 | /// Generates a mipmap for the current texture data, returns true if the operation succeeded 225 | pub fn generateMipmap(self: *Texture) bool { 226 | if (self.* == ._const_ptr) 227 | @panic("Can't act on a const texture"); 228 | 229 | return sf.c.sfTexture_generateMipmap(self._ptr) != 0; 230 | } 231 | 232 | /// Pointer to the csfml texture 233 | _ptr: *sf.c.sfTexture, 234 | /// Const pointer to the csfml texture 235 | _const_ptr: *const sf.c.sfTexture, 236 | }; 237 | 238 | // TODO: texture bind function 239 | 240 | test "texture: sane getters and setters" { 241 | const tst = std.testing; 242 | const allocator = std.heap.page_allocator; 243 | 244 | var tex = try Texture.create(.{ .x = 12, .y = 10 }); 245 | defer tex.destroy(); 246 | 247 | const size = tex.getSize(); 248 | 249 | tex.setSmooth(true); 250 | tex.setRepeated(true); 251 | 252 | try tst.expectEqual(@as(u32, 12), size.x); 253 | try tst.expectEqual(@as(u32, 10), size.y); 254 | try tst.expectEqual(@as(usize, 120), tex.getPixelCount()); 255 | 256 | var pixel_data = try allocator.alloc(sf.Color, 120); 257 | defer allocator.free(pixel_data); 258 | 259 | for (pixel_data, 0..) |*c, i| { 260 | c.* = sf.graphics.Color.fromHSVA(@as(f32, @floatFromInt(i)) / 144 * 360, 100, 100, 1); 261 | } 262 | 263 | pixel_data[0] = sf.Color.Green; 264 | 265 | try tex.updateFromPixels(pixel_data, null); 266 | 267 | try tst.expect(!tex.isSrgb()); 268 | try tst.expect(tex.isSmooth()); 269 | try tst.expect(tex.isRepeated()); 270 | 271 | var img = tex.copyToImage(); 272 | defer img.destroy(); 273 | 274 | try tst.expectEqual(sf.Color.Green, img.getPixel(.{ .x = 0, .y = 0 })); 275 | 276 | tex.updateFromImage(img, null); 277 | 278 | var t = tex; 279 | t.makeConst(); 280 | 281 | var copy = try t.copy(); 282 | 283 | try tst.expectEqual(@as(usize, 120), copy.getPixelCount()); 284 | 285 | var tex2 = try Texture.create(.{ .x = 100, .y = 100 }); 286 | defer tex2.destroy(); 287 | 288 | copy.swap(&tex2); 289 | 290 | try tst.expectEqual(@as(usize, 100 * 100), copy.getPixelCount()); 291 | try tst.expectEqual(@as(usize, 120), tex2.getPixelCount()); 292 | } 293 | --------------------------------------------------------------------------------