├── .gitattributes
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── build.zig
├── libs
├── Camera.zig
├── Shader.zig
├── common.zig
├── gl.zig
├── mach-glfw
├── zmath
│ ├── README.md
│ ├── build.zig
│ └── src
│ │ ├── benchmark.zig
│ │ ├── main.zig
│ │ ├── util.zig
│ │ └── zmath.zig
└── zstbi
│ ├── README.md
│ ├── build.zig
│ ├── libs
│ └── stbi
│ │ ├── stb_image.c
│ │ ├── stb_image.h
│ │ ├── stb_image_resize.h
│ │ └── stb_image_write.h
│ └── src
│ └── zstbi.zig
└── src
└── getting_started
├── basic_lighting
├── build.zig
├── content
│ ├── awesomeface.png
│ ├── container.jpg
│ ├── light_shader.fs
│ ├── light_shader.vs
│ ├── shader.fs
│ └── shader.vs
├── image.png
└── main.zig
├── camera_rotate
├── build.zig
├── content
│ ├── awesomeface.png
│ ├── container.jpg
│ ├── shader.fs
│ └── shader.vs
└── main.zig
├── coordinate_systems
├── build.zig
├── content
│ ├── awesomeface.png
│ ├── container.jpg
│ ├── shader.fs
│ └── shader.vs
├── image.png
└── main.zig
├── hello_rectangle
├── README.md
├── build.zig
├── image.png
└── main.zig
├── hello_triangle
├── README.md
├── build.zig
├── excercise_1.png
├── exercise_2.png
├── exercise_3.png
├── image.png
└── main.zig
├── shaders
├── build.zig
├── image.png
├── main.zig
└── shaders
│ ├── shader.fs
│ ├── shader.vs
│ ├── shader_ex3.fs
│ └── shader_ex3.vs
├── simple_camera
├── build.zig
├── content
│ ├── awesomeface.png
│ ├── container.jpg
│ ├── shader.fs
│ └── shader.vs
└── main.zig
├── textures
├── build.zig
├── content
│ ├── awesomeface.png
│ ├── container.jpg
│ ├── shader.fs
│ ├── shader.vs
│ └── wall.jpg
├── image.png
└── main.zig
└── transformations
├── build.zig
├── content
├── awesomeface.png
├── container.jpg
├── shader.fs
├── shader.vs
└── wall.jpg
├── image.png
└── main.zig
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file is for zig-specific build artifacts.
2 | # If you have OS-specific or editor-specific files to ignore,
3 | # such as *.swp or .DS_Store, put those in your global
4 | # ~/.gitignore and put this in your ~/.gitconfig:
5 | #
6 | # [core]
7 | # excludesfile = ~/.gitignore
8 | #
9 | # Cheers!
10 | # -andrewrk
11 |
12 | zig-cache/
13 | zig-out/
14 | /release/
15 | /debug/
16 | /build/
17 | /build-*/
18 | /docgen_tmp/
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "libs/mach-glfw"]
2 | path = libs/mach-glfw
3 | url = https://github.com/craftlinks/mach-glfw.git
4 | branch = main
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Files in a directory with a separate LICENSE file may contain files under different license terms,
2 | described within that LICENSE file.
3 |
4 | All other files are licensed under the Apache License, Version 2.0 (see LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
5 | or the MIT license (see LICENSE-MIT or http://opensource.org/licenses/MIT), at your option.
6 |
7 | All files in the project without exclusions may not be copied, modified, or distributed except
8 | according to the terms above.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [**Learn OpenGL**](https://learnopengl.com/) using [Zig](https://ziglang.org/).
2 |
3 | Windowing library: [mach/glfw - Ziggified GLFW bindings](https://github.com/hexops/mach-glfw) by slimsag
4 | (Stephen Gutekanst).
5 |
6 | OpenGL bindings for Zig were generated by the [zig-opengl](https://github.com/MasterQ32/zig-opengl) project by MasterQ32 (Felix Queißner).
7 |
8 | [zstbi](https://github.com/michal-z/zig-gamedev/tree/main/libs/zstbi) - stb image bindings, [zmath](https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath) - SIMD math library, provided by [Michal Ziulek](https://github.com/michal-z), part of the [zig-gamedev](https://github.com/michal-z/zig-gamedev) project.
9 |
10 | Sample programs can be used together with the reference book: [Learn OpenGL - Graphics Programming](https://learnopengl.com/) by [Joey de Vries](http://joeydevries.com/#home).
11 |
12 | ---
13 | Zig Language installation [How-to instructions](https://ziglang.org/learn/getting-started/).
14 |
15 | ---
16 | ## **I. Getting Started**
17 | ### Hello Triangle
18 |
19 | - [**hello_triangle**](src/getting_started/hello_triangle/): Minimal setup for drawing a trianlge on screen.
`zig build hello_triangle-run`
20 |
21 | - [**hello_rectangle**](src/getting_started/hello_rectangle/): Draw a rectangle efficiently with indexed rendering using the **'Element Buffer Object'**.
`zig build hello_rectangle-run`
22 |
23 |
24 | ### Shaders
25 | - [**shaders**](src/getting_started/shaders/): Little programs that rest on the GPU
26 | `zig build shaders-run`
27 |
28 |
29 | [Shader](src/common/shader.zig) struct mirrors the C++ Shader Class in the book.
30 |
31 | ### Textures
32 | - [**textures**](src/getting_started/textures/): Decorate objects with textures
33 | `zig build textures-run`
34 |
35 |
36 | ### Transformations
37 | - [**Transformations**](src/getting_started/transformations/): Apply a transformation matrix to vertex data
38 | `zig build transformations-run`
39 |
40 |
41 | ### Coordinate Systems
42 | - [**Coordinate systems**](src/getting_started/coordinate_systems/): Model, View, Projection matrices
43 | `zig build coordinate_systems-run`
44 |
45 |
46 | ### Camera
47 | - [**Camera rotation**](src/getting_started/camera_rotate/): Camera rotation around world origin
48 | `zig build camera_rotate-run`
49 |
50 | - [**Simple camera**](src/getting_started/simple_camera/): Fly-like camera
51 | `zig build simple_camera-run`
52 |
53 | ## **II. Lighting**
54 |
55 | ### Basic Lighting
56 | - [**Basic Lighting**](src/getting_started/basic_lighting/): Phong lighting model
57 | `zig build basic_lighting-run`
58 |
--------------------------------------------------------------------------------
/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const glfw = @import("libs/mach-glfw/build.zig");
3 | const zstbi = @import("libs/zstbi/build.zig");
4 | const zmath = @import("libs/zmath/build.zig");
5 |
6 | fn installExe(b: *std.build.Builder, exe: *std.build.LibExeObjStep, comptime name: []const u8, dependencies: []const ExeDependency) !void {
7 | exe.want_lto = false;
8 | if (exe.optimize == .ReleaseFast)
9 | exe.strip = true;
10 |
11 | for (dependencies) |dep| {
12 | exe.addModule(dep.name, dep.module);
13 | }
14 |
15 | try glfw.link(b, exe, .{});
16 | zstbi.link(exe);
17 |
18 | const install = b.step(name, "Build '" ++ name);
19 | install.dependOn(&b.addInstallArtifact(exe).step);
20 |
21 | const run_step = b.step(name ++ "-run", "Run " ++ name);
22 | const run_cmd = exe.run();
23 | run_cmd.step.dependOn(install);
24 | run_step.dependOn(&run_cmd.step);
25 |
26 | b.getInstallStep().dependOn(install);
27 | }
28 |
29 | pub fn build(b: *std.Build) !void {
30 | const options = Options{
31 | .build_mode = b.standardOptimizeOption(.{}),
32 | .target = b.standardTargetOptions(.{}),
33 | };
34 |
35 | // Modules
36 | const zmath_module = zmath.package(b, .{}).module;
37 | const zstbi_module = zstbi.package(b, .{}).module;
38 | const glfw_module = glfw.module(b);
39 | const gl_module = b.createModule(.{ .source_file = .{ .path = "libs/gl.zig" }, .dependencies = &.{} });
40 | const shader_module = b.createModule(.{ .source_file = .{ .path = "libs/Shader.zig" }, .dependencies = &.{.{ .name = "gl", .module = gl_module }} });
41 | const common_module = b.createModule(.{ .source_file = .{ .path = "libs/common.zig" }, .dependencies = &.{} });
42 | const camera_module = b.createModule(.{ .source_file = .{ .path = "libs/Camera.zig" }, .dependencies = &.{ .{ .name = "gl", .module = gl_module }, .{ .name = "Shader", .module = shader_module }, .{ .name = "zmath", .module = zmath_module }, .{ .name = "common", .module = common_module } } });
43 |
44 | // Dependencies
45 | const exe_dependencies: []const ExeDependency = &.{ .{ .name = "zmath", .module = zmath_module }, .{ .name = "zstbi", .module = zstbi_module }, .{ .name = "gl", .module = gl_module }, .{ .name = "glfw", .module = glfw_module }, .{ .name = "Shader", .module = shader_module }, .{ .name = "common", .module = common_module }, .{ .name = "Camera", .module = camera_module } };
46 |
47 | const hello_triangle = @import("src/getting_started/hello_triangle/build.zig");
48 | const hello_rectangle = @import("src/getting_started/hello_rectangle/build.zig");
49 | const shaders = @import("src/getting_started/shaders/build.zig");
50 | const textures = @import("src/getting_started/textures/build.zig");
51 | const transformations = @import("src/getting_started/transformations/build.zig");
52 | const coordinate_systems = @import("src/getting_started/coordinate_systems/build.zig");
53 | const camera_rotate = @import("src/getting_started/camera_rotate/build.zig");
54 | const simple_camera = @import("src/getting_started/simple_camera/build.zig");
55 | const basic_lighting = @import("src/getting_started/basic_lighting/build.zig");
56 |
57 | try installExe(b, hello_triangle.build(b, options), "hello_triangle", exe_dependencies);
58 | try installExe(b, hello_rectangle.build(b, options), "hello_rectangle", exe_dependencies);
59 | try installExe(b, shaders.build(b, options), "shaders", exe_dependencies);
60 | try installExe(b, textures.build(b, options), "textures", exe_dependencies);
61 | try installExe(b, transformations.build(b, options), "transformations", exe_dependencies);
62 | try installExe(b, coordinate_systems.build(b, options), "coordinate_systems", exe_dependencies);
63 | try installExe(b, camera_rotate.build(b, options), "camera_rotate", exe_dependencies);
64 | try installExe(b, simple_camera.build(b, options), "simple_camera", exe_dependencies);
65 | try installExe(b, basic_lighting.build(b, options), "basic_lighting", exe_dependencies);
66 | }
67 |
68 | pub const Options = struct {
69 | build_mode: std.builtin.Mode,
70 | target: std.zig.CrossTarget,
71 | };
72 |
73 | pub const ExeDependency = struct {
74 | name: []const u8,
75 | module: *std.Build.Module,
76 | };
77 |
--------------------------------------------------------------------------------
/libs/Camera.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const gl = @import("gl");
3 | const zm = @import("zmath");
4 | const common = @import("common");
5 | const Camera = @This();
6 |
7 | pub const CameraMovement = enum {
8 | FORWARD,
9 | BACKWARD,
10 | LEFT,
11 | RIGHT,
12 | };
13 |
14 | const WORLD_UP = zm.loadArr3(.{0.0, 1.0, 0.0});
15 | const MOVEMENT_SPEED: f32 = 2.5;
16 | const MOUSE_SENSITIVITY: f32 = 0.1;
17 |
18 | // Camera attributes
19 | position: zm.F32x4 = zm.loadArr3(.{0.0, 0.0, 0.0}),
20 | front: zm.F32x4 = zm.loadArr3(.{0.0, 0.0, -1.0}),
21 | up: zm.F32x4 = undefined,
22 | right: zm.F32x4 = undefined,
23 |
24 | // euler Angles
25 | yaw: f32 = -90,
26 | pitch: f32 = 0.0,
27 |
28 | // camera options
29 | zoom: f32 = 45.0,
30 |
31 |
32 | pub fn camera(position: ?zm.F32x4) Camera {
33 | const _position = p: {
34 | if(position) |value| {
35 | break :p value;
36 | }
37 | else {
38 | break :p zm.loadArr3(.{0.0, 0.0, 0.0});
39 | }
40 | };
41 |
42 | const _front = zm.loadArr3(.{0.0, 0.0, -1.0});
43 | const _world_up = zm.loadArr3(.{0.0, 1.0, 0.0});
44 | const _right = zm.normalize3(zm.cross3(_front, _world_up));
45 | const _up = zm.normalize3(zm.cross3(_right, _front));
46 |
47 | return Camera{
48 | .position = _position,
49 | .right = _right, // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
50 | .up = _up,
51 | };
52 | }
53 |
54 | // returns the view matrix calculated using Euler Angles and the LookAt Matrix
55 | pub fn getViewMatrix(self: *Camera) zm.Mat {
56 | return zm.lookAtRh(self.position, self.position + self.front, self.up);
57 | }
58 |
59 | // processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
60 | pub fn processKeyboard(self: *Camera, direction: Camera.CameraMovement, delta_time: f32) void {
61 | const velocity = zm.f32x4s(MOVEMENT_SPEED * delta_time);
62 | switch (direction) {
63 | .FORWARD => self.position += self.front * velocity,
64 | .BACKWARD => self.position -= self.front * velocity,
65 | .LEFT => self.position -= self.right * velocity,
66 | .RIGHT => self.position += self.right * velocity,
67 | }
68 | }
69 |
70 | // processes input received from a mouse input system. Expects the offset value in both the x and y direction.
71 | pub fn processMouseMovement(self: *Camera, xoffset: f64, yoffset: f64, constrain_pitch: bool) void {
72 | const _xoffset = @floatCast(f32,xoffset) * MOUSE_SENSITIVITY;
73 | const _yoffset = @floatCast(f32, yoffset) * MOUSE_SENSITIVITY;
74 |
75 | self.yaw += _xoffset;
76 | self.pitch += _yoffset;
77 |
78 | // make sure that when pitch is out of bounds, screen doesn't get flipped
79 | if (constrain_pitch) {
80 | if (self.pitch > 89.0)
81 | self.pitch = 89.0;
82 | if (self.pitch < -89.0)
83 | self.pitch = -89.0;
84 | }
85 |
86 | // update Front, Right and Up Vectors using the updated Euler angles
87 | self.updateCameraVectors();
88 | }
89 |
90 | // processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
91 | pub fn processMouseScroll(self: *Camera, yoffset: f64) void {
92 | self.zoom -= @floatCast(f32,yoffset);
93 | if (self.zoom < 1.0)
94 | self.zoom = 1.0;
95 | if (self.zoom > 45.0)
96 | self.zoom = 45.0;
97 | }
98 |
99 | // calculates the front vector from the Camera's (updated) Euler Angles
100 | fn updateCameraVectors(self: *Camera) void {
101 | // calculate the new Front vector
102 | var front: zm.F32x4 = undefined;
103 | front[0] = @cos(self.yaw * common.RAD_CONVERSION) * @cos(self.pitch * common.RAD_CONVERSION);
104 | front[1] = @sin(self.pitch * common.RAD_CONVERSION);
105 | front[2] = @sin(self.yaw * common.RAD_CONVERSION) * @cos(self.pitch * common.RAD_CONVERSION);
106 | self.front = front;
107 | // also re-calculate the Right and Up vector
108 | self.right = zm.normalize3(zm.cross3(self.front, WORLD_UP)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
109 | self.up = zm.normalize3(zm.cross3(self.right, self.front));
110 | }
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/libs/Shader.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const gl = @import("gl");
3 | const Shader = @This();
4 |
5 | // The program ID
6 | ID: c_uint,
7 |
8 | pub fn create(arena: std.mem.Allocator, vs_path:[]const u8, fs_path:[]const u8) Shader {
9 |
10 | // Create vertex shader
11 | var vertexShader: c_uint = undefined;
12 | vertexShader = gl.createShader(gl.VERTEX_SHADER);
13 | defer gl.deleteShader(vertexShader);
14 | const full_vs_path = std.fs.path.join(arena, &.{
15 | std.fs.selfExeDirPathAlloc(arena) catch unreachable,
16 | vs_path,
17 | }) catch unreachable;
18 |
19 | const full_fs_path = std.fs.path.join(arena, &.{
20 | std.fs.selfExeDirPathAlloc(arena) catch unreachable,
21 | fs_path,
22 | }) catch unreachable;
23 |
24 | const vs_file = std.fs.openFileAbsolute(full_vs_path, .{}) catch unreachable;
25 | const vs_code = vs_file.readToEndAllocOptions(arena, (10 * 1024), null, @alignOf(u8), 0) catch unreachable;
26 |
27 | const fs_file = std.fs.openFileAbsolute(full_fs_path, .{}) catch unreachable;
28 | const fs_code = fs_file.readToEndAllocOptions(arena, (10 * 1024), null, @alignOf(u8), 0) catch unreachable;
29 |
30 | // Attach the shader source to the vertex shader object and compile it
31 | gl.shaderSource(vertexShader, 1, @ptrCast([*c]const [*c]const u8, &vs_code), 0);
32 | gl.compileShader(vertexShader);
33 |
34 | // Check if vertex shader was compiled successfully
35 | var success: c_int = undefined;
36 | var infoLog: [512]u8 = [_]u8{0} ** 512;
37 |
38 | gl.getShaderiv(vertexShader, gl.COMPILE_STATUS, &success);
39 |
40 | if (success == 0) {
41 | gl.getShaderInfoLog(vertexShader, 512, 0, &infoLog);
42 | std.log.err("{s}", .{infoLog});
43 | }
44 |
45 | // Fragment shader
46 | var fragmentShader: c_uint = undefined;
47 | fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
48 | defer gl.deleteShader(fragmentShader);
49 |
50 | gl.shaderSource(fragmentShader, 1, @ptrCast([*c]const [*c]const u8, &fs_code), 0);
51 | gl.compileShader(fragmentShader);
52 |
53 | gl.getShaderiv(fragmentShader, gl.COMPILE_STATUS, &success);
54 |
55 | if (success == 0) {
56 | gl.getShaderInfoLog(fragmentShader, 512, 0, &infoLog);
57 | std.log.err("{s}", .{infoLog});
58 | }
59 |
60 | // create a program object
61 | const shaderProgram = gl.createProgram();
62 |
63 | // attach compiled shader objects to the program object and link
64 | gl.attachShader(shaderProgram, vertexShader);
65 | gl.attachShader(shaderProgram, fragmentShader);
66 | gl.linkProgram(shaderProgram);
67 |
68 | // check if shader linking was successfull
69 | gl.getProgramiv(shaderProgram, gl.LINK_STATUS, &success);
70 | if (success == 0) {
71 | gl.getProgramInfoLog(shaderProgram, 512, 0, &infoLog);
72 | std.log.err("{s}", .{infoLog});
73 | }
74 | return Shader{.ID = shaderProgram};
75 | }
76 |
77 | pub fn use(self: Shader) void {
78 | gl.useProgram(self.ID);
79 | }
80 |
81 | pub fn setBool(self: Shader, name: [*c]const u8, value: bool) void {
82 | gl.uniform1i(gl.getUniformLocation(self.ID, name), @boolToInt(value));
83 | }
84 |
85 | pub fn setInt(self: Shader, name: [*c]const u8, value: u32) void {
86 | gl.uniform1i(gl.getUniformLocation(self.ID, name), @intCast(c_int,value));
87 | }
88 |
89 | pub fn setFloat(self: Shader, name: [*c]const u8, value: f32) void {
90 | gl.uniform1f(gl.getUniformLocation(self.ID, name), value);
91 | }
92 |
93 | pub fn setVec3f(self: Shader, name: [*c]const u8, value: [3]f32) void {
94 | gl.uniform3f(gl.getUniformLocation(self.ID, name), value[0], value[1], value[2]);
95 | }
96 |
97 | pub fn setMat4f(self: Shader, name: [*c]const u8, value: [16]f32) void {
98 | const matLoc = gl.getUniformLocation(self.ID, name);
99 | gl.uniformMatrix4fv(matLoc, 1, gl.FALSE, &value);
100 | }
--------------------------------------------------------------------------------
/libs/common.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = std.math;
3 |
4 | pub fn pathToContent(arena: std.mem.Allocator, resource_relative_path: [:0]const u8) ![4096:0] u8 {
5 | const exe_path = std.fs.selfExeDirPathAlloc(arena) catch unreachable;
6 | const content_path = std.fs.path.join(arena, &.{exe_path, resource_relative_path}) catch unreachable;
7 | var content_path_zero : [4096:0]u8 = undefined;
8 | if (content_path.len >= 4096) return error.NameTooLong;
9 | std.mem.copy(u8, &content_path_zero, content_path);
10 | content_path_zero[content_path.len] = 0;
11 | return content_path_zero;
12 | }
13 |
14 | // Create the transformation matrices:
15 | // Degree to radians conversion factor
16 | pub const RAD_CONVERSION = math.pi / 180.0;
--------------------------------------------------------------------------------
/libs/mach-glfw:
--------------------------------------------------------------------------------
1 | /home/hugh/GitRepos/Zig/Snow/mach-glfw
--------------------------------------------------------------------------------
/libs/zmath/README.md:
--------------------------------------------------------------------------------
1 | # zmath v0.9.5 - SIMD math library for game developers
2 |
3 | Tested on x86_64 and AArch64.
4 |
5 | Provides ~140 optimized routines and ~70 extensive tests.
6 |
7 | Can be used with any graphics API.
8 |
9 | Documentation can be found [here](https://github.com/michal-z/zig-gamedev/blob/main/libs/zmath/src/zmath.zig).
10 |
11 | Benchamrks can be found [here](https://github.com/michal-z/zig-gamedev/blob/main/libs/zmath/src/benchmark.zig).
12 |
13 | An intro article can be found [here](https://zig.news/michalz/fast-multi-platform-simd-math-library-in-zig-2adn).
14 |
15 | ## Getting started
16 |
17 | Copy `zmath` folder to a `libs` subdirectory of the root of your project.
18 |
19 | Then in your `build.zig` add:
20 |
21 | ```zig
22 | const std = @import("std");
23 | const zmath = @import("libs/zmath/build.zig");
24 |
25 | pub fn build(b: *std.Build) void {
26 | ...
27 | const zmath_pkg = zmath.package(b, .{});
28 |
29 | exe.addModule("zmath", zmath_pkg.module);
30 | }
31 | ```
32 |
33 | Now in your code you may import and use zmath:
34 |
35 | ```zig
36 | const zm = @import("zmath");
37 |
38 | pub fn main() !void {
39 | //
40 | // OpenGL/Vulkan example
41 | //
42 | const object_to_world = zm.rotationY(..);
43 | const world_to_view = zm.lookAtRh(
44 | zm.f32x4(3.0, 3.0, 3.0, 1.0), // eye position
45 | zm.f32x4(0.0, 0.0, 0.0, 1.0), // focus point
46 | zm.f32x4(0.0, 1.0, 0.0, 0.0), // up direction ('w' coord is zero because this is a vector not a point)
47 | );
48 | // `perspectiveFovRhGl` produces Z values in [-1.0, 1.0] range (Vulkan app should use `perspectiveFovRh`)
49 | const view_to_clip = zm.perspectiveFovRhGl(0.25 * math.pi, aspect_ratio, 0.1, 20.0);
50 |
51 | const object_to_view = zm.mul(object_to_world, world_to_view);
52 | const object_to_clip = zm.mul(object_to_view, view_to_clip);
53 |
54 | // Transposition is needed because GLSL uses column-major matrices by default
55 | gl.uniformMatrix4fv(0, 1, gl.TRUE, zm.arrNPtr(&object_to_clip));
56 |
57 | // In GLSL: gl_Position = vec4(in_position, 1.0) * object_to_clip;
58 |
59 | //
60 | // DirectX example
61 | //
62 | const object_to_world = zm.rotationY(..);
63 | const world_to_view = zm.lookAtLh(
64 | zm.f32x4(3.0, 3.0, -3.0, 1.0), // eye position
65 | zm.f32x4(0.0, 0.0, 0.0, 1.0), // focus point
66 | zm.f32x4(0.0, 1.0, 0.0, 0.0), // up direction ('w' coord is zero because this is a vector not a point)
67 | );
68 | const view_to_clip = zm.perspectiveFovLh(0.25 * math.pi, aspect_ratio, 0.1, 20.0);
69 |
70 | const object_to_view = zm.mul(object_to_world, world_to_view);
71 | const object_to_clip = zm.mul(object_to_view, view_to_clip);
72 |
73 | // Transposition is needed because HLSL uses column-major matrices by default
74 | const mem = allocateUploadMemory(...);
75 | zm.storeMat(mem, zm.transpose(object_to_clip));
76 |
77 | // In HLSL: out_position_sv = mul(float4(in_position, 1.0), object_to_clip);
78 |
79 | //
80 | // 'WASD' camera movement example
81 | //
82 | {
83 | const speed = zm.f32x4s(10.0);
84 | const delta_time = zm.f32x4s(demo.frame_stats.delta_time);
85 | const transform = zm.mul(zm.rotationX(demo.camera.pitch), zm.rotationY(demo.camera.yaw));
86 | var forward = zm.normalize3(zm.mul(zm.f32x4(0.0, 0.0, 1.0, 0.0), transform));
87 |
88 | zm.storeArr3(&demo.camera.forward, forward);
89 |
90 | const right = speed * delta_time * zm.normalize3(zm.cross3(zm.f32x4(0.0, 1.0, 0.0, 0.0), forward));
91 | forward = speed * delta_time * forward;
92 |
93 | var cam_pos = zm.loadArr3(demo.camera.position);
94 |
95 | if (keyDown('W')) {
96 | cam_pos += forward;
97 | } else if (keyDown('S')) {
98 | cam_pos -= forward;
99 | }
100 | if (keyDown('D')) {
101 | cam_pos += right;
102 | } else if (keyDown('A')) {
103 | cam_pos -= right;
104 | }
105 |
106 | zm.storeArr3(&demo.camera.position, cam_pos);
107 | }
108 |
109 | //
110 | // SIMD wave equation solver example (works with vector width 4, 8 and 16)
111 | // 'T' can be F32x4, F32x8 or F32x16
112 | //
113 | var z_index: i32 = 0;
114 | while (z_index < grid_size) : (z_index += 1) {
115 | const z = scale * @intToFloat(f32, z_index - grid_size / 2);
116 | const vz = zm.splat(T, z);
117 |
118 | var x_index: i32 = 0;
119 | while (x_index < grid_size) : (x_index += zm.veclen(T)) {
120 | const x = scale * @intToFloat(f32, x_index - grid_size / 2);
121 | const vx = zm.splat(T, x) + voffset * zm.splat(T, scale);
122 |
123 | const d = zm.sqrt(vx * vx + vz * vz);
124 | const vy = zm.sin(d - vtime);
125 |
126 | const index = @intCast(usize, x_index + z_index * grid_size);
127 | zm.store(xslice[index..], vx, 0);
128 | zm.store(yslice[index..], vy, 0);
129 | zm.store(zslice[index..], vz, 0);
130 | }
131 | }
132 | }
133 | ```
134 |
--------------------------------------------------------------------------------
/libs/zmath/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | pub const Options = struct {
4 | prefer_determinism: bool = false,
5 | };
6 |
7 | pub const Package = struct {
8 | module: *std.Build.Module,
9 | options: Options,
10 | options_module: *std.Build.Module,
11 | };
12 |
13 | pub fn package(
14 | b: *std.Build,
15 | args: struct {
16 | options: Options = .{},
17 | },
18 | ) Package {
19 | const step = b.addOptions();
20 | step.addOption(bool, "prefer_determinism", args.options.prefer_determinism);
21 |
22 | const options_module = step.createModule();
23 |
24 | const module = b.createModule(.{
25 | .source_file = .{ .path = thisDir() ++ "/src/main.zig" },
26 | .dependencies = &.{
27 | .{ .name = "zmath_options", .module = options_module },
28 | },
29 | });
30 |
31 | return .{
32 | .module = module,
33 | .options = args.options,
34 | .options_module = options_module,
35 | };
36 | }
37 |
38 | pub fn build(b: *std.Build) void {
39 | const build_mode = b.standardOptimizeOption(.{});
40 | const target = b.standardTargetOptions(.{});
41 | const tests = buildTests(b, build_mode, target);
42 |
43 | const test_step = b.step("test", "Run zmath tests");
44 | test_step.dependOn(&tests.step);
45 | }
46 |
47 | pub fn buildTests(
48 | b: *std.Build,
49 | build_mode: std.builtin.Mode,
50 | target: std.zig.CrossTarget,
51 | ) *std.Build.CompileStep {
52 | const tests = b.addTest(.{
53 | .root_source_file = .{ .path = thisDir() ++ "/src/main.zig" },
54 | .target = target,
55 | .optimize = build_mode,
56 | });
57 | const zmath_pkg = package(b, .{});
58 | tests.addModule("zmath_options", zmath_pkg.options_module);
59 | return tests;
60 | }
61 |
62 | pub fn buildBenchmarks(
63 | b: *std.Build,
64 | target: std.zig.CrossTarget,
65 | ) *std.Build.CompileStep {
66 | const exe = b.addExecutable(.{
67 | .name = "benchmark",
68 | .root_source_file = .{ .path = thisDir() ++ "/src/benchmark.zig" },
69 | .target = target,
70 | .optimize = .ReleaseFast,
71 | });
72 | const zmath_pkg = package(b, .{});
73 | exe.addModule("zmath", zmath_pkg.module);
74 | return exe;
75 | }
76 |
77 | inline fn thisDir() []const u8 {
78 | return comptime std.fs.path.dirname(@src().file) orelse ".";
79 | }
80 |
--------------------------------------------------------------------------------
/libs/zmath/src/benchmark.zig:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------------------------------------------
2 | // zmath - benchmarks
3 | // -------------------------------------------------------------------------------------------------
4 | // 'zig build benchmark' in the root project directory will build and run 'ReleaseFast' configuration.
5 | //
6 | // -------------------------------------------------------------------------------------------------
7 | // 'AMD Ryzen 9 3950X 16-Core Processor', Windows 11, Zig 0.10.0-dev.2620+0e9458a3f
8 | // -------------------------------------------------------------------------------------------------
9 | // matrix mul benchmark (AOS) - scalar version: 1.5880s, zmath version: 1.0642s
10 | // cross3, scale, bias benchmark (AOS) - scalar version: 0.9318s, zmath version: 0.6888s
11 | // cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.2258s, zmath version: 1.1095s
12 | // quaternion mul benchmark (AOS) - scalar version: 1.4123s, zmath version: 0.6958s
13 | // wave benchmark (SOA) - scalar version: 4.8165s, zmath version: 0.7338s
14 | //
15 | // -------------------------------------------------------------------------------------------------
16 | // 'AMD Ryzen 7 5800X 8-Core Processer', Linux 5.17.14, Zig 0.10.0-dev.2624+d506275a0
17 | // -------------------------------------------------------------------------------------------------
18 | // matrix mul benchmark (AOS) - scalar version: 1.3672s, zmath version: 0.8617s
19 | // cross3, scale, bias benchmark (AOS) - scalar version: 0.6586s, zmath version: 0.4803s
20 | // cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.0620s, zmath version: 0.8942s
21 | // quaternion mul benchmark (AOS) - scalar version: 1.1324s, zmath version: 0.6064s
22 | // wave benchmark (SOA) - scalar version: 3.6598s, zmath version: 0.4231s
23 | //
24 | // -------------------------------------------------------------------------------------------------
25 | // 'Apple M1 Max', macOS Version 12.4, Zig 0.10.0-dev.2657+74442f350
26 | // -------------------------------------------------------------------------------------------------
27 | // matrix mul benchmark (AOS) - scalar version: 1.0297s, zmath version: 1.0538s
28 | // cross3, scale, bias benchmark (AOS) - scalar version: 0.6294s, zmath version: 0.6532s
29 | // cross3, dot3, scale, bias benchmark (AOS) - scalar version: 0.9807s, zmath version: 1.0988s
30 | // quaternion mul benchmark (AOS) - scalar version: 1.5413s, zmath version: 0.7800s
31 | // wave benchmark (SOA) - scalar version: 3.4220s, zmath version: 1.0255s
32 | //
33 | // -------------------------------------------------------------------------------------------------
34 | // '11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz', Windows 11, Zig 0.10.0-dev.2620+0e9458a3f
35 | // -------------------------------------------------------------------------------------------------
36 | // matrix mul benchmark (AOS) - scalar version: 2.2308s, zmath version: 0.9376s
37 | // cross3, scale, bias benchmark (AOS) - scalar version: 1.0821s, zmath version: 0.5110s
38 | // cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.6580s, zmath version: 0.9167s
39 | // quaternion mul benchmark (AOS) - scalar version: 2.0139s, zmath version: 0.5856s
40 | // wave benchmark (SOA) - scalar version: 3.7832s, zmath version: 0.3642s
41 | //
42 | // -------------------------------------------------------------------------------------------------
43 |
44 | pub fn main() !void {
45 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
46 | defer _ = gpa.deinit();
47 | const allocator = gpa.allocator();
48 |
49 | // m = mul(ma, mb); data set fits in L1 cache; AOS data layout.
50 | try mat4MulBenchmark(allocator, 100_000);
51 |
52 | // v = 0.01 * cross3(va, vb) + vec3(1.0); data set fits in L1 cache; AOS data layout.
53 | try cross3ScaleBiasBenchmark(allocator, 10_000);
54 |
55 | // v = dot3(va, vb) * (0.1 * cross3(va, vb) + vec3(1.0)); data set fits in L1 cache; AOS data layout.
56 | try cross3Dot3ScaleBiasBenchmark(allocator, 10_000);
57 |
58 | // q = qmul(qa, qb); data set fits in L1 cache; AOS data layout.
59 | try quatBenchmark(allocator, 10_000);
60 |
61 | // d = sqrt(x * x + z * z); y = sin(d - t); SOA layout.
62 | try waveBenchmark(allocator, 1_000);
63 | }
64 |
65 | const std = @import("std");
66 | const time = std.time;
67 | const Timer = time.Timer;
68 | const zm = @import("zmath");
69 |
70 | var prng = std.rand.DefaultPrng.init(0);
71 | const random = prng.random();
72 |
73 | noinline fn mat4MulBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
74 | std.debug.print("{s:>42} - ", .{"matrix mul benchmark (AOS)"});
75 |
76 | var data0 = std.ArrayList([16]f32).init(allocator);
77 | defer data0.deinit();
78 | var data1 = std.ArrayList([16]f32).init(allocator);
79 | defer data1.deinit();
80 |
81 | var i: usize = 0;
82 | while (i < 64) : (i += 1) {
83 | try data0.append([16]f32{
84 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
85 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
86 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
87 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
88 | });
89 | try data1.append([16]f32{
90 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
91 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
92 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
93 | random.float(f32), random.float(f32), random.float(f32), random.float(f32),
94 | });
95 | }
96 |
97 | // Warmup, fills L1 cache.
98 | i = 0;
99 | while (i < 100) : (i += 1) {
100 | for (data1.items) |b| {
101 | for (data0.items) |a| {
102 | const ma = zm.loadMat(a[0..]);
103 | const mb = zm.loadMat(b[0..]);
104 | const r = zm.mul(ma, mb);
105 | std.mem.doNotOptimizeAway(&r);
106 | }
107 | }
108 | }
109 |
110 | {
111 | i = 0;
112 | var timer = try Timer.start();
113 | const start = timer.lap();
114 | while (i < count) : (i += 1) {
115 | for (data1.items) |b| {
116 | for (data0.items) |a| {
117 | const r = [16]f32{
118 | a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12],
119 | a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13],
120 | a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14],
121 | a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15],
122 | a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12],
123 | a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13],
124 | a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14],
125 | a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15],
126 | a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12],
127 | a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13],
128 | a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14],
129 | a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15],
130 | a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12],
131 | a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13],
132 | a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14],
133 | a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15],
134 | };
135 | std.mem.doNotOptimizeAway(&r);
136 | }
137 | }
138 | }
139 | const end = timer.read();
140 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
141 |
142 | std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
143 | }
144 |
145 | {
146 | i = 0;
147 | var timer = try Timer.start();
148 | const start = timer.lap();
149 | while (i < count) : (i += 1) {
150 | for (data1.items) |b| {
151 | for (data0.items) |a| {
152 | const ma = zm.loadMat(a[0..]);
153 | const mb = zm.loadMat(b[0..]);
154 | const r = zm.mul(ma, mb);
155 | std.mem.doNotOptimizeAway(&r);
156 | }
157 | }
158 | }
159 | const end = timer.read();
160 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
161 |
162 | std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
163 | }
164 | }
165 |
166 | noinline fn cross3ScaleBiasBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
167 | std.debug.print("{s:>42} - ", .{"cross3, scale, bias benchmark (AOS)"});
168 |
169 | var data0 = std.ArrayList([3]f32).init(allocator);
170 | defer data0.deinit();
171 | var data1 = std.ArrayList([3]f32).init(allocator);
172 | defer data1.deinit();
173 |
174 | var i: usize = 0;
175 | while (i < 256) : (i += 1) {
176 | try data0.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
177 | try data1.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
178 | }
179 |
180 | // Warmup, fills L1 cache.
181 | i = 0;
182 | while (i < 100) : (i += 1) {
183 | for (data1.items) |b| {
184 | for (data0.items) |a| {
185 | const va = zm.loadArr3(a);
186 | const vb = zm.loadArr3(b);
187 | const cp = zm.f32x4s(0.01) * zm.cross3(va, vb) + zm.f32x4s(1.0);
188 | std.mem.doNotOptimizeAway(&cp);
189 | }
190 | }
191 | }
192 |
193 | {
194 | i = 0;
195 | var timer = try Timer.start();
196 | const start = timer.lap();
197 | while (i < count) : (i += 1) {
198 | for (data1.items) |b| {
199 | for (data0.items) |a| {
200 | const r = [3]f32{
201 | 0.01 * (a[1] * b[2] - a[2] * b[1]) + 1.0,
202 | 0.01 * (a[2] * b[0] - a[0] * b[2]) + 1.0,
203 | 0.01 * (a[0] * b[1] - a[1] * b[0]) + 1.0,
204 | };
205 | std.mem.doNotOptimizeAway(&r);
206 | }
207 | }
208 | }
209 | const end = timer.read();
210 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
211 |
212 | std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
213 | }
214 |
215 | {
216 | i = 0;
217 | var timer = try Timer.start();
218 | const start = timer.lap();
219 | while (i < count) : (i += 1) {
220 | for (data1.items) |b| {
221 | for (data0.items) |a| {
222 | const va = zm.loadArr3(a);
223 | const vb = zm.loadArr3(b);
224 | const cp = zm.f32x4s(0.01) * zm.cross3(va, vb) + zm.f32x4s(1.0);
225 | std.mem.doNotOptimizeAway(&cp);
226 | }
227 | }
228 | }
229 | const end = timer.read();
230 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
231 |
232 | std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
233 | }
234 | }
235 |
236 | noinline fn cross3Dot3ScaleBiasBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
237 | std.debug.print("{s:>42} - ", .{"cross3, dot3, scale, bias benchmark (AOS)"});
238 |
239 | var data0 = std.ArrayList([3]f32).init(allocator);
240 | defer data0.deinit();
241 | var data1 = std.ArrayList([3]f32).init(allocator);
242 | defer data1.deinit();
243 |
244 | var i: usize = 0;
245 | while (i < 256) : (i += 1) {
246 | try data0.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
247 | try data1.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
248 | }
249 |
250 | // Warmup, fills L1 cache.
251 | i = 0;
252 | while (i < 100) : (i += 1) {
253 | for (data1.items) |b| {
254 | for (data0.items) |a| {
255 | const va = zm.loadArr3(a);
256 | const vb = zm.loadArr3(b);
257 | const r = (zm.dot3(va, vb) * (zm.f32x4s(0.1) * zm.cross3(va, vb) + zm.f32x4s(1.0)))[0];
258 | std.mem.doNotOptimizeAway(&r);
259 | }
260 | }
261 | }
262 |
263 | {
264 | i = 0;
265 | var timer = try Timer.start();
266 | const start = timer.lap();
267 | while (i < count) : (i += 1) {
268 | for (data1.items) |b| {
269 | for (data0.items) |a| {
270 | const d = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
271 | const r = [3]f32{
272 | d * (0.1 * (a[1] * b[2] - a[2] * b[1]) + 1.0),
273 | d * (0.1 * (a[2] * b[0] - a[0] * b[2]) + 1.0),
274 | d * (0.1 * (a[0] * b[1] - a[1] * b[0]) + 1.0),
275 | };
276 | std.mem.doNotOptimizeAway(&r);
277 | }
278 | }
279 | }
280 | const end = timer.read();
281 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
282 |
283 | std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
284 | }
285 |
286 | {
287 | i = 0;
288 | var timer = try Timer.start();
289 | const start = timer.lap();
290 | while (i < count) : (i += 1) {
291 | for (data1.items) |b| {
292 | for (data0.items) |a| {
293 | const va = zm.loadArr3(a);
294 | const vb = zm.loadArr3(b);
295 | const r = zm.dot3(va, vb) * (zm.f32x4s(0.1) * zm.cross3(va, vb) + zm.f32x4s(1.0));
296 | std.mem.doNotOptimizeAway(&r);
297 | }
298 | }
299 | }
300 | const end = timer.read();
301 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
302 |
303 | std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
304 | }
305 | }
306 |
307 | noinline fn quatBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
308 | std.debug.print("{s:>42} - ", .{"quaternion mul benchmark (AOS)"});
309 |
310 | var data0 = std.ArrayList([4]f32).init(allocator);
311 | defer data0.deinit();
312 | var data1 = std.ArrayList([4]f32).init(allocator);
313 | defer data1.deinit();
314 |
315 | var i: usize = 0;
316 | while (i < 256) : (i += 1) {
317 | try data0.append([4]f32{ random.float(f32), random.float(f32), random.float(f32), random.float(f32) });
318 | try data1.append([4]f32{ random.float(f32), random.float(f32), random.float(f32), random.float(f32) });
319 | }
320 |
321 | // Warmup, fills L1 cache.
322 | i = 0;
323 | while (i < 100) : (i += 1) {
324 | for (data1.items) |b| {
325 | for (data0.items) |a| {
326 | const va = zm.loadArr4(a);
327 | const vb = zm.loadArr4(b);
328 | const r = zm.qmul(va, vb);
329 | std.mem.doNotOptimizeAway(&r);
330 | }
331 | }
332 | }
333 |
334 | {
335 | i = 0;
336 | var timer = try Timer.start();
337 | const start = timer.lap();
338 | while (i < count) : (i += 1) {
339 | for (data1.items) |b| {
340 | for (data0.items) |a| {
341 | const r = [4]f32{
342 | (b[3] * a[0]) + (b[0] * a[3]) + (b[1] * a[2]) - (b[2] * a[1]),
343 | (b[3] * a[1]) - (b[0] * a[2]) + (b[1] * a[3]) + (b[2] * a[0]),
344 | (b[3] * a[2]) + (b[0] * a[1]) - (b[1] * a[0]) + (b[2] * a[3]),
345 | (b[3] * a[3]) - (b[0] * a[0]) - (b[1] * a[1]) - (b[2] * a[2]),
346 | };
347 | std.mem.doNotOptimizeAway(&r);
348 | }
349 | }
350 | }
351 | const end = timer.read();
352 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
353 |
354 | std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
355 | }
356 |
357 | {
358 | i = 0;
359 | var timer = try Timer.start();
360 | const start = timer.lap();
361 | while (i < count) : (i += 1) {
362 | for (data1.items) |b| {
363 | for (data0.items) |a| {
364 | const va = zm.loadArr4(a);
365 | const vb = zm.loadArr4(b);
366 | const r = zm.qmul(va, vb);
367 | std.mem.doNotOptimizeAway(&r);
368 | }
369 | }
370 | }
371 | const end = timer.read();
372 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
373 |
374 | std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
375 | }
376 | }
377 |
378 | noinline fn waveBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
379 | _ = allocator;
380 | std.debug.print("{s:>42} - ", .{"wave benchmark (SOA)"});
381 |
382 | const grid_size = 1024;
383 | {
384 | var t: f32 = 0.0;
385 |
386 | const scale: f32 = 0.05;
387 |
388 | var timer = try Timer.start();
389 | const start = timer.lap();
390 |
391 | var iter: usize = 0;
392 | while (iter < count) : (iter += 1) {
393 | var z_index: i32 = 0;
394 | while (z_index < grid_size) : (z_index += 1) {
395 | const z = scale * @intToFloat(f32, z_index - grid_size / 2);
396 |
397 | var x_index: i32 = 0;
398 | while (x_index < grid_size) : (x_index += 4) {
399 | const x0 = scale * @intToFloat(f32, x_index + 0 - grid_size / 2);
400 | const x1 = scale * @intToFloat(f32, x_index + 1 - grid_size / 2);
401 | const x2 = scale * @intToFloat(f32, x_index + 2 - grid_size / 2);
402 | const x3 = scale * @intToFloat(f32, x_index + 3 - grid_size / 2);
403 |
404 | const d0 = zm.sqrt(x0 * x0 + z * z);
405 | const d1 = zm.sqrt(x1 * x1 + z * z);
406 | const d2 = zm.sqrt(x2 * x2 + z * z);
407 | const d3 = zm.sqrt(x3 * x3 + z * z);
408 |
409 | const y0 = zm.sin(d0 - t);
410 | const y1 = zm.sin(d1 - t);
411 | const y2 = zm.sin(d2 - t);
412 | const y3 = zm.sin(d3 - t);
413 |
414 | std.mem.doNotOptimizeAway(&y0);
415 | std.mem.doNotOptimizeAway(&y1);
416 | std.mem.doNotOptimizeAway(&y2);
417 | std.mem.doNotOptimizeAway(&y3);
418 | }
419 | }
420 | t += 0.001;
421 | }
422 | const end = timer.read();
423 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
424 |
425 | std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
426 | }
427 |
428 | {
429 | const T = zm.F32x16;
430 |
431 | const static = struct {
432 | const offsets = [16]f32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
433 | };
434 | const voffset = zm.load(static.offsets[0..], T, 0);
435 | var vt = zm.splat(T, 0.0);
436 |
437 | const scale: f32 = 0.05;
438 |
439 | var timer = try Timer.start();
440 | const start = timer.lap();
441 |
442 | var iter: usize = 0;
443 | while (iter < count) : (iter += 1) {
444 | var z_index: i32 = 0;
445 | while (z_index < grid_size) : (z_index += 1) {
446 | const z = scale * @intToFloat(f32, z_index - grid_size / 2);
447 | const vz = zm.splat(T, z);
448 |
449 | var x_index: i32 = 0;
450 | while (x_index < grid_size) : (x_index += zm.veclen(T)) {
451 | const x = scale * @intToFloat(f32, x_index - grid_size / 2);
452 | const vx = zm.splat(T, x) + voffset * zm.splat(T, scale);
453 |
454 | const d = zm.sqrt(vx * vx + vz * vz);
455 |
456 | const vy = zm.sin(d - vt);
457 |
458 | std.mem.doNotOptimizeAway(&vy);
459 | }
460 | }
461 | vt += zm.splat(T, 0.001);
462 | }
463 | const end = timer.read();
464 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
465 |
466 | std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
467 | }
468 | }
469 |
--------------------------------------------------------------------------------
/libs/zmath/src/main.zig:
--------------------------------------------------------------------------------
1 | //--------------------------------------------------------------------------------------------------
2 | //
3 | // SIMD math library for game developers
4 | // https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
5 | //
6 | // See zmath.zig for more details.
7 | // See util.zig for additional functionality.
8 | //
9 | //--------------------------------------------------------------------------------------------------
10 | pub const version = @import("std").SemanticVersion{ .major = 0, .minor = 9, .patch = 5 };
11 |
12 | pub usingnamespace @import("zmath.zig");
13 | pub const util = @import("util.zig");
14 |
15 | // ensure transitive closure of test coverage
16 | comptime {
17 | _ = util;
18 | }
19 |
--------------------------------------------------------------------------------
/libs/zmath/src/util.zig:
--------------------------------------------------------------------------------
1 | // ==============================================================================
2 | //
3 | // Collection of useful functions building on top of, and extending, core zmath.
4 | // https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
5 | //
6 | // ------------------------------------------------------------------------------
7 | // 1. Matrix functions
8 | // ------------------------------------------------------------------------------
9 | //
10 | // As an example, in a left handed Y-up system:
11 | // getAxisX is equivalent to the right vector
12 | // getAxisY is equivalent to the up vector
13 | // getAxisZ is equivalent to the forward vector
14 | //
15 | // getTranslationVec(m: Mat) Vec
16 | // getAxisX(m: Mat) Vec
17 | // getAxisY(m: Mat) Vec
18 | // getAxisZ(m: Mat) Vec
19 | //
20 | //
21 | // ------------------------------------------------------------------------------
22 | // 2. Angle functions
23 | // ------------------------------------------------------------------------------
24 | //
25 | // angleMod(angle: f32) f32
26 | //
27 | // ==============================================================================
28 |
29 | const zm = @import("zmath.zig");
30 | const std = @import("std");
31 | const math = std.math;
32 | const expect = std.testing.expect;
33 |
34 | pub fn getTranslationVec(m: zm.Mat) zm.Vec {
35 | var translation = m[3];
36 | translation[3] = 0;
37 | return translation;
38 | }
39 |
40 | pub fn getScaleVec(m: zm.Mat) zm.Vec {
41 | const scale_x = zm.length3(zm.f32x4(m[0][0], m[1][0], m[2][0], 0))[0];
42 | const scale_y = zm.length3(zm.f32x4(m[0][1], m[1][1], m[2][1], 0))[0];
43 | const scale_z = zm.length3(zm.f32x4(m[0][2], m[1][2], m[2][2], 0))[0];
44 | return zm.f32x4(scale_x, scale_y, scale_z, 0);
45 | }
46 |
47 | pub fn getRotationQuat(_m: zm.Mat) zm.Quat {
48 | // Ortho normalize given matrix.
49 | const c1 = zm.normalize3(zm.f32x4(_m[0][0], _m[1][0], _m[2][0], 0));
50 | const c2 = zm.normalize3(zm.f32x4(_m[0][1], _m[1][1], _m[2][1], 0));
51 | const c3 = zm.normalize3(zm.f32x4(_m[0][2], _m[1][2], _m[2][2], 0));
52 | var m = _m;
53 | m[0][0] = c1[0];
54 | m[1][0] = c1[1];
55 | m[2][0] = c1[2];
56 | m[0][1] = c2[0];
57 | m[1][1] = c2[1];
58 | m[2][1] = c2[2];
59 | m[0][2] = c3[0];
60 | m[1][2] = c3[1];
61 | m[2][2] = c3[2];
62 |
63 | // Extract rotation
64 | return zm.quatFromMat(m);
65 | }
66 |
67 | pub fn getAxisX(m: zm.Mat) zm.Vec {
68 | return zm.normalize3(zm.f32x4(m[0][0], m[0][1], m[0][2], 0.0));
69 | }
70 |
71 | pub fn getAxisY(m: zm.Mat) zm.Vec {
72 | return zm.normalize3(zm.f32x4(m[1][0], m[1][1], m[1][2], 0.0));
73 | }
74 |
75 | pub fn getAxisZ(m: zm.Mat) zm.Vec {
76 | return zm.normalize3(zm.f32x4(m[2][0], m[2][1], m[2][2], 0.0));
77 | }
78 |
79 | test "zmath.util.mat.translation" {
80 | // zig fmt: off
81 | const mat_data = [18]f32{
82 | 1.0,
83 | 2.0, 3.0, 4.0, 5.0,
84 | 6.0, 7.0, 8.0, 9.0,
85 | 10.0,11.0, 12.0,13.0,
86 | 14.0, 15.0, 16.0, 17.0,
87 | 18.0,
88 | };
89 | // zig fmt: on
90 | const mat = zm.loadMat(mat_data[1..]);
91 | const translation = getTranslationVec(mat);
92 | try expect(zm.approxEqAbs(translation, zm.f32x4(14.0, 15.0, 16.0, 0.0), 0.0001));
93 | }
94 |
95 | test "zmath.util.mat.scale" {
96 | const mat = zm.mul(zm.scaling(3, 4, 5), zm.translation(6, 7, 8));
97 | const scale = getScaleVec(mat);
98 | try expect(zm.approxEqAbs(scale, zm.f32x4(3.0, 4.0, 5.0, 0.0), 0.0001));
99 | }
100 |
101 | test "zmath.util.mat.rotation" {
102 | const rotate_origin = zm.matFromRollPitchYaw(0.1, 1.2, 2.3);
103 | const mat = zm.mul(zm.mul(rotate_origin, zm.scaling(3, 4, 5)), zm.translation(6, 7, 8));
104 | const rotate_get = getRotationQuat(mat);
105 | const v0 = zm.mul(zm.f32x4s(1), rotate_origin);
106 | const v1 = zm.mul(zm.f32x4s(1), zm.quatToMat(rotate_get));
107 | try expect(zm.approxEqAbs(v0, v1, 0.0001));
108 | }
109 |
110 | test "zmath.util.mat.z_vec" {
111 | const degToRad = std.math.degreesToRadians;
112 | var identity = zm.identity();
113 | var z_vec = getAxisZ(identity);
114 | try expect(zm.approxEqAbs(z_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.0001));
115 | const rot_yaw = zm.rotationY(degToRad(f32, 90));
116 | identity = zm.mul(identity, rot_yaw);
117 | z_vec = getAxisZ(identity);
118 | try expect(zm.approxEqAbs(z_vec, zm.f32x4(1.0, 0.0, 0.0, 0), 0.0001));
119 | }
120 |
121 | test "zmath.util.mat.y_vec" {
122 | const degToRad = std.math.degreesToRadians;
123 | var identity = zm.identity();
124 | var y_vec = getAxisY(identity);
125 | try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
126 | const rot_yaw = zm.rotationY(degToRad(f32, 90));
127 | identity = zm.mul(identity, rot_yaw);
128 | y_vec = getAxisY(identity);
129 | try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
130 | const rot_pitch = zm.rotationX(degToRad(f32, 90));
131 | identity = zm.mul(identity, rot_pitch);
132 | y_vec = getAxisY(identity);
133 | try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.01));
134 | }
135 |
136 | test "zmath.util.mat.right" {
137 | const degToRad = std.math.degreesToRadians;
138 | var identity = zm.identity();
139 | var right = getAxisX(identity);
140 | try expect(zm.approxEqAbs(right, zm.f32x4(1.0, 0.0, 0.0, 0), 0.01));
141 | const rot_yaw = zm.rotationY(degToRad(f32, 90));
142 | identity = zm.mul(identity, rot_yaw);
143 | right = getAxisX(identity);
144 | try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 0.0, -1.0, 0), 0.01));
145 | const rot_pitch = zm.rotationX(degToRad(f32, 90));
146 | identity = zm.mul(identity, rot_pitch);
147 | right = getAxisX(identity);
148 | try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
149 | }
150 |
151 | // ------------------------------------------------------------------------------
152 | // This software is available under 2 licenses -- choose whichever you prefer.
153 | // ------------------------------------------------------------------------------
154 | // ALTERNATIVE A - MIT License
155 | // Copyright (c) 2022 Michal Ziulek and Contributors
156 | // Permission is hereby granted, free of charge, to any person obtaining identity copy of
157 | // this software and associated documentation files (the "Software"), to deal in
158 | // the Software without restriction, including without limitation the rights to
159 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
160 | // of the Software, and to permit persons to whom the Software is furnished to do
161 | // so, subject to the following conditions:
162 | // The above copyright notice and this permission notice shall be included in all
163 | // copies or substantial portions of the Software.
164 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
165 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
166 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
167 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
168 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
169 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
170 | // SOFTWARE.
171 | // ------------------------------------------------------------------------------
172 | // ALTERNATIVE B - Public Domain (www.unlicense.org)
173 | // This is free and unencumbered software released into the public domain.
174 | // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
175 | // software, either in source code form or as identity compiled binary, for any purpose,
176 | // commercial or non-commercial, and by any means.
177 | // In jurisdictions that recognize copyright laws, the author or authors of this
178 | // software dedicate any and all copyright interest in the software to the public
179 | // domain. We make this dedication for the benefit of the public at large and to
180 | // the detriment of our heirs and successors. We intend this dedication to be an
181 | // overt act of relinquishment in perpetuity of all present and future rights to
182 | // this software under copyright law.
183 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
184 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
185 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
186 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
187 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
188 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
189 | // ------------------------------------------------------------------------------
190 |
--------------------------------------------------------------------------------
/libs/zstbi/README.md:
--------------------------------------------------------------------------------
1 | # zstbi v0.9.2 - stb image bindings
2 |
3 | ## Features
4 |
5 | * Supports Zig memory allocators
6 | * Supports decoding most popular formats
7 | * Supports HDR images
8 | * Supports 8-bits and 16-bits per channel
9 | * Supports image resizing
10 | * Supports image writing (.png, .jpg)
11 |
12 | ## Getting started
13 |
14 | Copy `zstbi` folder to a `libs` subdirectory of the root of your project.
15 |
16 | Then in your `build.zig` add:
17 | ```zig
18 | const zstbi = @import("libs/zstbi/build.zig");
19 |
20 | pub fn build(b: *std.Build) void {
21 | ...
22 | const zstbi_pkg = zstbi.package(b, .{});
23 |
24 | exe.addModule("zstbi", zstbi_pkg.module);
25 |
26 | zstbi.link(exe);
27 | }
28 | ```
29 | Now in your code you may import and use `zstbi`.
30 |
31 | Init the lib. `zstbi.init()` is cheap and you may call it whenever you need to change memory allocator. Must be called from the main thread.
32 | ```zig
33 | const zstbi = @import("zstbi");
34 |
35 | zstbi.init(allocator);
36 | defer zstbi.deinit();
37 | ```
38 |
39 | Load image:
40 | ```zig
41 | var image = try zstbi.Image.init("data/image.png", num_desired_channels);
42 | defer image.deinit();
43 | _ = image.data; // stored as []u8
44 | _ = image.width;
45 | _ = image.height;
46 | _ = image.num_components;
47 | _ = image.bytes_per_component;
48 | _ = image.bytes_per_row;
49 | _ = image.is_hdr;
50 |
51 | const new_resized_image = image.resize(1024, 1024);
52 | ```
53 |
54 | Get image info without loading:
55 | ```zig
56 | const image_info = zstbi.Image.info("data/image.jpg");
57 | _ = image_info.is_supported; // Is image format supported?
58 | _ = image_info.width;
59 | _ = image_info.height;
60 | _ = image_info.num_components;
61 | ```
62 | Misc functions:
63 | ```zig
64 | pub fn isHdr(filename: [:0]const u8) bool
65 | pub fn is16bit(filename: [:0]const u8) bool
66 |
67 | pub fn setFlipVerticallyOnLoad(should_flip: bool) void
68 | ```
69 |
--------------------------------------------------------------------------------
/libs/zstbi/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | pub const Package = struct {
4 | module: *std.Build.Module,
5 | };
6 |
7 | pub fn package(b: *std.Build, _: struct {}) Package {
8 | const module = b.createModule(.{
9 | .source_file = .{ .path = thisDir() ++ "/src/zstbi.zig" },
10 | });
11 | return .{ .module = module };
12 | }
13 |
14 | pub fn build(_: *std.Build) void {}
15 |
16 | pub fn buildTests(
17 | b: *std.Build,
18 | build_mode: std.builtin.Mode,
19 | target: std.zig.CrossTarget,
20 | ) *std.Build.CompileStep {
21 | const tests = b.addTest(.{
22 | .root_source_file = .{ .path = thisDir() ++ "/src/zstbi.zig" },
23 | .target = target,
24 | .optimize = build_mode,
25 | });
26 | link(tests);
27 | return tests;
28 | }
29 |
30 | pub fn link(exe: *std.Build.CompileStep) void {
31 | exe.linkSystemLibraryName("c");
32 | exe.addCSourceFile(thisDir() ++ "/libs/stbi/stb_image.c", &.{
33 | "-std=c99",
34 | "-fno-sanitize=undefined",
35 | });
36 | }
37 |
38 | inline fn thisDir() []const u8 {
39 | return comptime std.fs.path.dirname(@src().file) orelse ".";
40 | }
41 |
--------------------------------------------------------------------------------
/libs/zstbi/libs/stbi/stb_image.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void* (*zstbiMallocPtr)(size_t size) = NULL;
4 | void* (*zstbiReallocPtr)(void* ptr, size_t size) = NULL;
5 | void (*zstbiFreePtr)(void* ptr) = NULL;
6 |
7 | #define STBI_MALLOC(size) zstbiMallocPtr(size)
8 | #define STBI_REALLOC(ptr, size) zstbiReallocPtr(ptr, size)
9 | #define STBI_FREE(ptr) zstbiFreePtr(ptr)
10 |
11 | #define STB_IMAGE_IMPLEMENTATION
12 | #include "stb_image.h"
13 |
14 | void* (*zstbirMallocPtr)(size_t size, void* context) = NULL;
15 | void (*zstbirFreePtr)(void* ptr, void* context) = NULL;
16 |
17 | #define STBIR_MALLOC(size, context) zstbirMallocPtr(size, context)
18 | #define STBIR_FREE(ptr, context) zstbirFreePtr(ptr, context)
19 |
20 | #define STB_IMAGE_RESIZE_IMPLEMENTATION
21 | #include "stb_image_resize.h"
22 |
23 | void* (*zstbiwMallocPtr)(size_t size) = NULL;
24 | void* (*zstbiwReallocPtr)(void* ptr, size_t size) = NULL;
25 | void (*zstbiwFreePtr)(void* ptr) = NULL;
26 |
27 | #define STBIW_MALLOC(size) zstbiwMallocPtr(size)
28 | #define STBIW_REALLOC(ptr, size) zstbiwReallocPtr(ptr, size)
29 | #define STBIW_FREE(ptr) zstbiwFreePtr(ptr)
30 |
31 | #define STB_IMAGE_WRITE_IMPLEMENTATION
32 | #include "stb_image_write.h"
33 |
--------------------------------------------------------------------------------
/libs/zstbi/src/zstbi.zig:
--------------------------------------------------------------------------------
1 | pub const version = @import("std").SemanticVersion{ .major = 0, .minor = 9, .patch = 2 };
2 | const std = @import("std");
3 | const assert = std.debug.assert;
4 |
5 | pub fn init(allocator: std.mem.Allocator) void {
6 | assert(mem_allocator == null);
7 | mem_allocator = allocator;
8 | mem_allocations = std.AutoHashMap(usize, usize).init(allocator);
9 |
10 | // stb image
11 | zstbiMallocPtr = zstbiMalloc;
12 | zstbiReallocPtr = zstbiRealloc;
13 | zstbiFreePtr = zstbiFree;
14 | // stb image resize
15 | zstbirMallocPtr = zstbirMalloc;
16 | zstbirFreePtr = zstbirFree;
17 | // stb image write
18 | zstbiwMallocPtr = zstbiMalloc;
19 | zstbiwReallocPtr = zstbiRealloc;
20 | zstbiwFreePtr = zstbiFree;
21 | }
22 |
23 | pub fn deinit() void {
24 | assert(mem_allocator != null);
25 | assert(mem_allocations.?.count() == 0);
26 | mem_allocations.?.deinit();
27 | mem_allocations = null;
28 | mem_allocator = null;
29 | }
30 |
31 | pub const JpgWriteSettings = struct {
32 | quality: u32,
33 | };
34 |
35 | pub const ImageWriteFormat = union(enum) {
36 | png,
37 | jpg: JpgWriteSettings,
38 | };
39 |
40 | pub const ImageWriteError = error{
41 | CouldNotWriteImage,
42 | };
43 |
44 | pub const Image = struct {
45 | data: []u8,
46 | width: u32,
47 | height: u32,
48 | num_components: u32,
49 | bytes_per_component: u32,
50 | bytes_per_row: u32,
51 | is_hdr: bool,
52 |
53 | pub fn info(pathname: [:0]const u8) struct {
54 | is_supported: bool,
55 | width: u32,
56 | height: u32,
57 | num_components: u32,
58 | } {
59 | var w: c_int = 0;
60 | var h: c_int = 0;
61 | var c: c_int = 0;
62 | const is_supported = stbi_info(pathname, &w, &h, &c);
63 | return .{
64 | .is_supported = is_supported,
65 | .width = @intCast(u32, w),
66 | .height = @intCast(u32, h),
67 | .num_components = @intCast(u32, c),
68 | };
69 | }
70 |
71 | pub fn init(pathname: [:0]const u8, forced_num_channels: u32) !Image {
72 | var width: u32 = 0;
73 | var height: u32 = 0;
74 | var num_components: u32 = 0;
75 | var bytes_per_component: u32 = 0;
76 | var bytes_per_row: u32 = 0;
77 | var is_hdr = false;
78 |
79 | const data = if (isHdr(pathname)) data: {
80 | var x: c_int = undefined;
81 | var y: c_int = undefined;
82 | var ch: c_int = undefined;
83 | const ptr = stbi_loadf(
84 | pathname,
85 | &x,
86 | &y,
87 | &ch,
88 | @intCast(c_int, forced_num_channels),
89 | );
90 | if (ptr == null) return error.ImageInitFailed;
91 |
92 | num_components = if (forced_num_channels == 0) @intCast(u32, ch) else forced_num_channels;
93 | width = @intCast(u32, x);
94 | height = @intCast(u32, y);
95 | bytes_per_component = 2;
96 | bytes_per_row = width * num_components * bytes_per_component;
97 | is_hdr = true;
98 |
99 | // Convert each component from f32 to f16.
100 | var ptr_f16 = @ptrCast([*]f16, ptr.?);
101 | const num = width * height * num_components;
102 | var i: u32 = 0;
103 | while (i < num) : (i += 1) {
104 | ptr_f16[i] = @floatCast(f16, ptr.?[i]);
105 | }
106 | break :data @ptrCast([*]u8, ptr_f16)[0 .. height * bytes_per_row];
107 | } else data: {
108 | var x: c_int = undefined;
109 | var y: c_int = undefined;
110 | var ch: c_int = undefined;
111 | const is_16bit = is16bit(pathname);
112 | const ptr = if (is_16bit) @ptrCast(?[*]u8, stbi_load_16(
113 | pathname,
114 | &x,
115 | &y,
116 | &ch,
117 | @intCast(c_int, forced_num_channels),
118 | )) else stbi_load(
119 | pathname,
120 | &x,
121 | &y,
122 | &ch,
123 | @intCast(c_int, forced_num_channels),
124 | );
125 | if (ptr == null) return error.ImageInitFailed;
126 |
127 | num_components = if (forced_num_channels == 0) @intCast(u32, ch) else forced_num_channels;
128 | width = @intCast(u32, x);
129 | height = @intCast(u32, y);
130 | bytes_per_component = if (is_16bit) 2 else 1;
131 | bytes_per_row = width * num_components * bytes_per_component;
132 | is_hdr = false;
133 |
134 | break :data @ptrCast([*]u8, ptr)[0 .. height * bytes_per_row];
135 | };
136 |
137 | return Image{
138 | .data = data,
139 | .width = width,
140 | .height = height,
141 | .num_components = num_components,
142 | .bytes_per_component = bytes_per_component,
143 | .bytes_per_row = bytes_per_row,
144 | .is_hdr = is_hdr,
145 | };
146 | }
147 |
148 | pub fn initFromData(data: []const u8, forced_num_channels: u32) !Image {
149 | // TODO: Add support for HDR images (https://github.com/michal-z/zig-gamedev/issues/155).
150 | var width: u32 = 0;
151 | var height: u32 = 0;
152 | var num_components: u32 = 0;
153 | var bytes_per_component: u32 = 0;
154 | var bytes_per_row: u32 = 0;
155 |
156 | const image_data = data: {
157 | var x: c_int = undefined;
158 | var y: c_int = undefined;
159 | var ch: c_int = undefined;
160 | const ptr = stbi_load_from_memory(
161 | data.ptr,
162 | @intCast(c_int, data.len),
163 | &x,
164 | &y,
165 | &ch,
166 | @intCast(c_int, forced_num_channels),
167 | );
168 | if (ptr == null) return error.ImageInitFailed;
169 |
170 | num_components = if (forced_num_channels == 0) @intCast(u32, ch) else forced_num_channels;
171 | width = @intCast(u32, x);
172 | height = @intCast(u32, y);
173 | bytes_per_component = 1;
174 | bytes_per_row = width * num_components * bytes_per_component;
175 |
176 | break :data @ptrCast([*]u8, ptr)[0 .. height * bytes_per_row];
177 | };
178 |
179 | return Image{
180 | .data = image_data,
181 | .width = width,
182 | .height = height,
183 | .num_components = num_components,
184 | .bytes_per_component = bytes_per_component,
185 | .bytes_per_row = bytes_per_row,
186 | .is_hdr = false,
187 | };
188 | }
189 |
190 | pub fn resize(image: *const Image, new_width: u32, new_height: u32) Image {
191 | // TODO: Add support for HDR images
192 | const new_bytes_per_row = new_width * image.num_components * image.bytes_per_component;
193 | const new_size = new_height * new_bytes_per_row;
194 | const new_data = @ptrCast([*]u8, zstbiMalloc(new_size));
195 | stbir_resize_uint8(
196 | image.data.ptr,
197 | @intCast(c_int, image.width),
198 | @intCast(c_int, image.height),
199 | 0,
200 | new_data,
201 | @intCast(c_int, new_width),
202 | @intCast(c_int, new_height),
203 | 0,
204 | @intCast(c_int, image.num_components),
205 | );
206 | return .{
207 | .data = new_data[0..new_size],
208 | .width = new_width,
209 | .height = new_height,
210 | .num_components = image.num_components,
211 | .bytes_per_component = image.bytes_per_component,
212 | .bytes_per_row = new_bytes_per_row,
213 | .is_hdr = image.is_hdr,
214 | };
215 | }
216 |
217 | pub fn writeToFile(
218 | self: *const Image,
219 | filename: [:0]const u8,
220 | image_format: ImageWriteFormat,
221 | ) ImageWriteError!void {
222 | const w = @intCast(c_int, self.width);
223 | const h = @intCast(c_int, self.height);
224 | const comp = @intCast(c_int, self.num_components);
225 | const result = switch (image_format) {
226 | .png => stbi_write_png(filename.ptr, w, h, comp, self.data.ptr, 0),
227 | .jpg => |settings| stbi_write_jpg(
228 | filename.ptr,
229 | w,
230 | h,
231 | comp,
232 | self.data.ptr,
233 | @intCast(c_int, settings.quality),
234 | ),
235 | };
236 | // if the result is 0 then it means an error occured (per stb image write docs)
237 | if (result == 0) {
238 | return ImageWriteError.CouldNotWriteImage;
239 | }
240 | }
241 |
242 | pub fn deinit(image: *Image) void {
243 | stbi_image_free(image.data.ptr);
244 | image.* = undefined;
245 | }
246 | };
247 |
248 | /// `pub fn setHdrToLdrScale(scale: f32) void`
249 | pub const setHdrToLdrScale = stbi_hdr_to_ldr_scale;
250 |
251 | /// `pub fn setHdrToLdrGamma(gamma: f32) void`
252 | pub const setHdrToLdrGamma = stbi_hdr_to_ldr_gamma;
253 |
254 | /// `pub fn setLdrToHdrScale(scale: f32) void`
255 | pub const setLdrToHdrScale = stbi_ldr_to_hdr_scale;
256 |
257 | /// `pub fn setLdrToHdrGamma(gamma: f32) void`
258 | pub const setLdrToHdrGamma = stbi_ldr_to_hdr_gamma;
259 |
260 | pub fn isHdr(filename: [:0]const u8) bool {
261 | return stbi_is_hdr(filename) != 0;
262 | }
263 |
264 | pub fn is16bit(filename: [:0]const u8) bool {
265 | return stbi_is_16_bit(filename) != 0;
266 | }
267 |
268 | pub fn setFlipVerticallyOnLoad(should_flip: bool) void {
269 | stbi_set_flip_vertically_on_load(if (should_flip) 1 else 0);
270 | }
271 |
272 | var mem_allocator: ?std.mem.Allocator = null;
273 | var mem_allocations: ?std.AutoHashMap(usize, usize) = null;
274 | var mem_mutex: std.Thread.Mutex = .{};
275 | const mem_alignment = 16;
276 |
277 | extern var zstbiMallocPtr: ?*const fn (size: usize) callconv(.C) ?*anyopaque;
278 | extern var zstbiwMallocPtr: ?*const fn (size: usize) callconv(.C) ?*anyopaque;
279 |
280 | fn zstbiMalloc(size: usize) callconv(.C) ?*anyopaque {
281 | mem_mutex.lock();
282 | defer mem_mutex.unlock();
283 |
284 | const mem = mem_allocator.?.alignedAlloc(
285 | u8,
286 | mem_alignment,
287 | size,
288 | ) catch @panic("zstbi: out of memory");
289 |
290 | mem_allocations.?.put(@ptrToInt(mem.ptr), size) catch @panic("zstbi: out of memory");
291 |
292 | return mem.ptr;
293 | }
294 |
295 | extern var zstbiReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque;
296 | extern var zstbiwReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque;
297 |
298 | fn zstbiRealloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque {
299 | mem_mutex.lock();
300 | defer mem_mutex.unlock();
301 |
302 | const old_size = if (ptr != null) mem_allocations.?.get(@ptrToInt(ptr.?)).? else 0;
303 | const old_mem = if (old_size > 0)
304 | @ptrCast([*]align(mem_alignment) u8, @alignCast(mem_alignment, ptr))[0..old_size]
305 | else
306 | @as([*]align(mem_alignment) u8, undefined)[0..0];
307 |
308 | const new_mem = mem_allocator.?.realloc(old_mem, size) catch @panic("zstbi: out of memory");
309 |
310 | if (ptr != null) {
311 | const removed = mem_allocations.?.remove(@ptrToInt(ptr.?));
312 | std.debug.assert(removed);
313 | }
314 |
315 | mem_allocations.?.put(@ptrToInt(new_mem.ptr), size) catch @panic("zstbi: out of memory");
316 |
317 | return new_mem.ptr;
318 | }
319 |
320 | extern var zstbiFreePtr: ?*const fn (maybe_ptr: ?*anyopaque) callconv(.C) void;
321 | extern var zstbiwFreePtr: ?*const fn (maybe_ptr: ?*anyopaque) callconv(.C) void;
322 |
323 | fn zstbiFree(maybe_ptr: ?*anyopaque) callconv(.C) void {
324 | if (maybe_ptr) |ptr| {
325 | mem_mutex.lock();
326 | defer mem_mutex.unlock();
327 |
328 | const size = mem_allocations.?.fetchRemove(@ptrToInt(ptr)).?.value;
329 | const mem = @ptrCast([*]align(mem_alignment) u8, @alignCast(mem_alignment, ptr))[0..size];
330 | mem_allocator.?.free(mem);
331 | }
332 | }
333 |
334 | extern var zstbirMallocPtr: ?*const fn (size: usize, maybe_context: ?*anyopaque) callconv(.C) ?*anyopaque;
335 |
336 | fn zstbirMalloc(size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque {
337 | return zstbiMalloc(size);
338 | }
339 |
340 | extern var zstbirFreePtr: ?*const fn (maybe_ptr: ?*anyopaque, maybe_context: ?*anyopaque) callconv(.C) void;
341 |
342 | fn zstbirFree(maybe_ptr: ?*anyopaque, _: ?*anyopaque) callconv(.C) void {
343 | zstbiFree(maybe_ptr);
344 | }
345 |
346 | extern fn stbi_info(filename: [*:0]const u8, x: *c_int, y: *c_int, comp: *c_int) c_int;
347 |
348 | extern fn stbi_load(
349 | filename: [*:0]const u8,
350 | x: *c_int,
351 | y: *c_int,
352 | channels_in_file: *c_int,
353 | desired_channels: c_int,
354 | ) ?[*]u8;
355 |
356 | extern fn stbi_load_16(
357 | filename: [*:0]const u8,
358 | x: *c_int,
359 | y: *c_int,
360 | channels_in_file: *c_int,
361 | desired_channels: c_int,
362 | ) ?[*]u16;
363 |
364 | extern fn stbi_loadf(
365 | filename: [*:0]const u8,
366 | x: *c_int,
367 | y: *c_int,
368 | channels_in_file: *c_int,
369 | desired_channels: c_int,
370 | ) ?[*]f32;
371 |
372 | pub extern fn stbi_load_from_memory(
373 | buffer: [*]const u8,
374 | len: c_int,
375 | x: *c_int,
376 | y: *c_int,
377 | channels_in_file: *c_int,
378 | desired_channels: c_int,
379 | ) ?[*]u8;
380 |
381 | extern fn stbi_image_free(image_data: ?[*]u8) void;
382 |
383 | extern fn stbi_hdr_to_ldr_scale(scale: f32) void;
384 | extern fn stbi_hdr_to_ldr_gamma(gamma: f32) void;
385 | extern fn stbi_ldr_to_hdr_scale(scale: f32) void;
386 | extern fn stbi_ldr_to_hdr_gamma(gamma: f32) void;
387 |
388 | extern fn stbi_is_16_bit(filename: [*:0]const u8) c_int;
389 | extern fn stbi_is_hdr(filename: [*:0]const u8) c_int;
390 |
391 | extern fn stbi_set_flip_vertically_on_load(flag_true_if_should_flip: c_int) void;
392 |
393 | extern fn stbir_resize_uint8(
394 | input_pixels: [*]const u8,
395 | input_w: c_int,
396 | input_h: c_int,
397 | input_stride_in_bytes: c_int,
398 | output_pixels: [*]u8,
399 | output_w: c_int,
400 | output_h: c_int,
401 | output_stride_in_bytes: c_int,
402 | num_channels: c_int,
403 | ) void;
404 |
405 | extern fn stbi_write_jpg(
406 | filename: [*:0]const u8,
407 | w: c_int,
408 | h: c_int,
409 | comp: c_int,
410 | data: [*]const u8,
411 | quality: c_int,
412 | ) c_int;
413 | extern fn stbi_write_png(
414 | filename: [*:0]const u8,
415 | w: c_int,
416 | h: c_int,
417 | comp: c_int,
418 | data: [*]const u8,
419 | stride_in_bytes: c_int,
420 | ) c_int;
421 |
422 | test "zstbi.basic" {
423 | init(std.testing.allocator);
424 | defer deinit();
425 | }
426 |
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "content/";
10 |
11 | pub fn build(b: *std.build.Builder, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "basic_lighting",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/content/awesomeface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/basic_lighting/content/awesomeface.png
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/content/container.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/basic_lighting/content/container.jpg
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/content/light_shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 |
4 | out vec4 FragColor;
5 |
6 | void main()
7 | {
8 | FragColor = vec4(1.0f);
9 | };
10 |
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/content/light_shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 |
4 | uniform mat4 model;
5 | uniform mat4 view;
6 | uniform mat4 projection;
7 |
8 | void main()
9 | {
10 | gl_Position = projection * view * model * vec4(aPos, 1.0f);
11 | }
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/content/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 |
5 | in vec3 FragPos;
6 | in vec3 Normal;
7 |
8 | uniform vec3 lightPos;
9 | uniform vec3 objectColor;
10 | uniform vec3 lightColor;
11 | uniform vec3 viewPos;
12 |
13 | void main()
14 | {
15 | float ambientStrength = 0.1;
16 | vec3 ambient = ambientStrength * lightColor;
17 |
18 | float specularStrength = 0.75;
19 |
20 | vec3 norm = normalize(Normal);
21 | vec3 lightDir = normalize(lightPos - FragPos);
22 | float diff = max(dot(norm, lightDir), 0.0);
23 | vec3 diffuse = diff * lightColor;
24 |
25 | vec3 viewDir = normalize(viewPos - FragPos);
26 | vec3 reflectDir = reflect(-lightDir, norm);
27 | float spec = pow(max(dot(viewDir, reflectDir), 0.0), 256);
28 | vec3 specular = specularStrength * spec * lightColor;
29 |
30 | vec3 result = (ambient + diffuse + specular) * objectColor;
31 |
32 | FragColor = vec4(result, 1.0f);
33 | };
34 |
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/content/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec3 aNormal;
4 |
5 | uniform mat4 model;
6 | uniform mat4 view;
7 | uniform mat4 projection;
8 |
9 | out vec3 FragPos;
10 | out vec3 Normal;
11 |
12 | void main()
13 | {
14 | gl_Position = projection * view * vec4(aPos, 1.0f);
15 | FragPos = vec3(vec4(aPos, 1.0));
16 | Normal = aNormal;
17 | }
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/basic_lighting/image.png
--------------------------------------------------------------------------------
/src/getting_started/basic_lighting/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = std.math;
3 | const glfw = @import("glfw");
4 | const zstbi = @import("zstbi");
5 | const zm = @import("zmath");
6 | const gl = @import("gl");
7 | const Shader = @import("Shader");
8 | const Camera = @import("Camera");
9 | const common = @import("common");
10 |
11 | // Camera
12 | const camera_pos = zm.loadArr3(.{ 0.0, 0.0, 5.0 });
13 | var lastX: f64 = 0.0;
14 | var lastY: f64 = 0.0;
15 | var first_mouse = true;
16 | var camera = Camera.camera(camera_pos);
17 |
18 | // Timing
19 | var delta_time: f32 = 0.0;
20 | var last_frame: f32 = 0.0;
21 |
22 | // lighting
23 | var light_position = [_]f32{ 10.0, 10.0, 10.0 };
24 |
25 | const WindowSize = struct {
26 | pub const width: u32 = 800;
27 | pub const height: u32 = 600;
28 | };
29 |
30 | pub fn main() !void {
31 |
32 | // glfw: initialize and configure
33 | // ------------------------------
34 | if (!glfw.init(.{})) {
35 | std.log.err("GLFW initialization failed", .{});
36 | return;
37 | }
38 | defer glfw.terminate();
39 |
40 | // glfw window creation
41 | // --------------------
42 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
43 | .opengl_profile = .opengl_core_profile,
44 | .context_version_major = 4,
45 | .context_version_minor = 1,
46 | }) orelse {
47 | std.log.err("GLFW Window creation failed", .{});
48 | return;
49 | };
50 | defer window.destroy();
51 |
52 | glfw.makeContextCurrent(window);
53 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
54 | // Capture mouse, disable cursor visibility
55 | glfw.Window.setInputMode(window, glfw.Window.InputMode.cursor, glfw.Window.InputModeCursor.disabled);
56 | glfw.Window.setCursorPosCallback(window, mouseCallback);
57 | glfw.Window.setScrollCallback(window, mouseScrollCallback);
58 |
59 | // Load all OpenGL function pointers
60 | // ---------------------------------------
61 | const proc: glfw.GLProc = undefined;
62 | try gl.load(proc, glGetProcAddress);
63 |
64 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
65 | var allocator = gpa.allocator();
66 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
67 | defer arena_allocator_state.deinit();
68 | const arena = arena_allocator_state.allocator();
69 |
70 | // Enable OpenGL depth testing (use Z-buffer information)
71 | gl.enable(gl.DEPTH_TEST);
72 |
73 | // create shader program
74 | var shader_program: Shader = Shader.create(arena, "content/shader.vs", "content/shader.fs");
75 | var light_shader: Shader = Shader.create(arena, "content/light_shader.vs", "content/light_shader.fs");
76 |
77 | // set up vertex data (and buffer(s)) and configure vertex attributes
78 | // ------------------------------------------------------------------
79 |
80 | const vertices_2D = [_]f32{
81 | // positions // colors // texture coords
82 | 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
83 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
84 | -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
85 | -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
86 | };
87 |
88 | _ = vertices_2D;
89 |
90 | const vertices_3D = [_]f32{
91 | -0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
92 | 0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
93 | 0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
94 | 0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
95 | -0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
96 | -0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
97 |
98 | -0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
99 | 0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
100 | 0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
101 | 0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
102 | -0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
103 | -0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
104 |
105 | -0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
106 | -0.5, 0.5, -0.5, -1.0, 0.0, 0.0,
107 | -0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
108 | -0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
109 | -0.5, -0.5, 0.5, -1.0, 0.0, 0.0,
110 | -0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
111 |
112 | 0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
113 | 0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
114 | 0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
115 | 0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
116 | 0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
117 | 0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
118 |
119 | -0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
120 | 0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
121 | 0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
122 | 0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
123 | -0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
124 | -0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
125 |
126 | -0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
127 | 0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
128 | 0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
129 | 0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
130 | -0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
131 | -0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
132 | };
133 |
134 | var VBO: c_uint = undefined;
135 | var VAO: c_uint = undefined;
136 | var light_VAO: c_uint = undefined;
137 |
138 | gl.genVertexArrays(1, &VAO);
139 | defer gl.deleteVertexArrays(1, &VAO);
140 |
141 | gl.genVertexArrays(1, &light_VAO);
142 | defer gl.deleteVertexArrays(1, &light_VAO);
143 |
144 | gl.genBuffers(1, &VBO);
145 | defer gl.deleteBuffers(1, &VBO);
146 |
147 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
148 | gl.bindVertexArray(VAO);
149 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
150 | // Fill our buffer with the vertex data
151 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices_3D.len, &vertices_3D, gl.STATIC_DRAW);
152 |
153 | // vertex
154 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 6 * @sizeOf(f32), null);
155 | gl.enableVertexAttribArray(0);
156 |
157 | // normal attribute
158 | const normal_offset: [*c]c_uint = (3 * @sizeOf(f32));
159 | gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 6 * @sizeOf(f32), normal_offset);
160 | gl.enableVertexAttribArray(1);
161 |
162 | // Configure light VAO
163 | gl.bindVertexArray(light_VAO);
164 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
165 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 6 * @sizeOf(f32), null);
166 | gl.enableVertexAttribArray(0);
167 |
168 | // View matrix
169 | var view: [16]f32 = undefined;
170 |
171 | // Buffer to store Orojection matrix (in render loop)
172 | var proj: [16]f32 = undefined;
173 |
174 | var light_model: [16]f32 = undefined;
175 |
176 | while (!window.shouldClose()) {
177 |
178 | // Time per frame
179 | const current_frame = @floatCast(f32, glfw.getTime());
180 | delta_time = current_frame - last_frame;
181 | last_frame = current_frame;
182 |
183 | processInput(window);
184 |
185 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
186 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
187 |
188 | light_position[0] = 1.0 + zm.sin(@floatCast(f32, glfw.getTime())) * 2.0;
189 | light_position[1] = 1.0 + zm.sin(@floatCast(f32, glfw.getTime()) / 2.0) * 1.0;
190 |
191 | shader_program.use();
192 | shader_program.setVec3f("objectColor", .{ 1.0, 0.5, 0.31 });
193 | shader_program.setVec3f("lightColor", .{ 1.0, 1.0, 1.0 });
194 | shader_program.setVec3f("lightPos", light_position);
195 | shader_program.setVec3f("viewPos", zm.vecToArr3(camera.position));
196 |
197 | // Projection matrix
198 | const projM = x: {
199 | const window_size = window.getSize();
200 | const aspect = @intToFloat(f32, window_size.width) / @intToFloat(f32, window_size.height);
201 | var projM = zm.perspectiveFovRhGl(camera.zoom * common.RAD_CONVERSION, aspect, 0.1, 100.0);
202 | break :x projM;
203 | };
204 | zm.storeMat(&proj, projM);
205 | shader_program.setMat4f("projection", proj);
206 |
207 | // View matrix: Camera
208 | const viewM = camera.getViewMatrix();
209 | zm.storeMat(&view, viewM);
210 | shader_program.setMat4f("view", view);
211 |
212 | gl.bindVertexArray(VAO);
213 | gl.drawArrays(gl.TRIANGLES, 0, 36);
214 |
215 | const light_trans = zm.translation(light_position[0], light_position[1], light_position[2]);
216 | const light_modelM = zm.mul(light_trans, zm.scaling(0.2, 0.2, 0.2));
217 | zm.storeMat(&light_model, light_modelM);
218 |
219 | light_shader.use();
220 | light_shader.setMat4f("projection", proj);
221 | light_shader.setMat4f("view", view);
222 | light_shader.setMat4f("model", light_model);
223 | gl.bindVertexArray(light_VAO);
224 | gl.drawArrays(gl.TRIANGLES, 0, 36);
225 |
226 | window.swapBuffers();
227 | glfw.pollEvents();
228 | }
229 | }
230 |
231 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
232 | _ = p;
233 | return glfw.getProcAddress(proc);
234 | }
235 |
236 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
237 | _ = window;
238 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
239 | }
240 |
241 | fn processInput(window: glfw.Window) void {
242 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
243 | _ = glfw.Window.setShouldClose(window, true);
244 | }
245 |
246 | if (glfw.Window.getKey(window, glfw.Key.w) == glfw.Action.press) {
247 | camera.processKeyboard(Camera.CameraMovement.FORWARD, delta_time);
248 | }
249 | if (glfw.Window.getKey(window, glfw.Key.s) == glfw.Action.press) {
250 | camera.processKeyboard(Camera.CameraMovement.BACKWARD, delta_time);
251 | }
252 | if (glfw.Window.getKey(window, glfw.Key.a) == glfw.Action.press) {
253 | camera.processKeyboard(Camera.CameraMovement.LEFT, delta_time);
254 | }
255 | if (glfw.Window.getKey(window, glfw.Key.d) == glfw.Action.press) {
256 | camera.processKeyboard(Camera.CameraMovement.RIGHT, delta_time);
257 | }
258 | }
259 |
260 | fn mouseCallback(window: glfw.Window, xpos: f64, ypos: f64) void {
261 | _ = window;
262 |
263 | if (first_mouse) {
264 | lastX = xpos;
265 | lastY = ypos;
266 | first_mouse = false;
267 | }
268 |
269 | var xoffset = xpos - lastX;
270 | var yoffset = ypos - lastY;
271 |
272 | lastX = xpos;
273 | lastY = ypos;
274 |
275 | camera.processMouseMovement(xoffset, yoffset, true);
276 | }
277 |
278 | fn mouseScrollCallback(window: glfw.Window, xoffset: f64, yoffset: f64) void {
279 | _ = window;
280 | _ = xoffset;
281 |
282 | camera.processMouseScroll(yoffset);
283 | }
284 |
--------------------------------------------------------------------------------
/src/getting_started/camera_rotate/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "content/";
10 |
11 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "camera_rotate",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/camera_rotate/content/awesomeface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/camera_rotate/content/awesomeface.png
--------------------------------------------------------------------------------
/src/getting_started/camera_rotate/content/container.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/camera_rotate/content/container.jpg
--------------------------------------------------------------------------------
/src/getting_started/camera_rotate/content/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 | in vec2 texCoord;
5 |
6 | uniform sampler2D texture1;
7 | uniform sampler2D texture2;
8 |
9 | void main()
10 | {
11 | FragColor = mix(texture(texture1, texCoord),texture(texture2, texCoord), 0.5f);
12 | };
13 |
--------------------------------------------------------------------------------
/src/getting_started/camera_rotate/content/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec2 aTexCoord;
4 |
5 | out vec2 texCoord;
6 |
7 | uniform mat4 model;
8 | uniform mat4 view;
9 | uniform mat4 projection;
10 |
11 | void main()
12 | {
13 | gl_Position = projection * view * model * vec4(aPos, 1.0f);
14 | texCoord = aTexCoord;
15 | }
--------------------------------------------------------------------------------
/src/getting_started/camera_rotate/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = std.math;
3 | const glfw = @import("glfw");
4 | const zstbi = @import("zstbi");
5 | const zm = @import("zmath");
6 | const gl = @import("gl");
7 | const Shader = @import("Shader");
8 | const common = @import("common");
9 |
10 | const WindowSize = struct {
11 | pub const width: u32 = 800;
12 | pub const height: u32 = 600;
13 | };
14 |
15 | pub fn main() !void {
16 |
17 | // glfw: initialize and configure
18 | // ------------------------------
19 | if (!glfw.init(.{})) {
20 | std.log.err("GLFW initialization failed", .{});
21 | return;
22 | }
23 | defer glfw.terminate();
24 |
25 | // glfw window creation
26 | // --------------------
27 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
28 | .opengl_profile = .opengl_core_profile,
29 | .context_version_major = 4,
30 | .context_version_minor = 1,
31 | }) orelse {
32 | std.log.err("GLFW Window creation failed", .{});
33 | return;
34 | };
35 | defer window.destroy();
36 |
37 | glfw.makeContextCurrent(window);
38 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
39 |
40 | // Load all OpenGL function pointers
41 | // ---------------------------------------
42 | const proc: glfw.GLProc = undefined;
43 | try gl.load(proc, glGetProcAddress);
44 |
45 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
46 | var allocator = gpa.allocator();
47 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
48 | defer arena_allocator_state.deinit();
49 | const arena = arena_allocator_state.allocator();
50 |
51 | // create shader program
52 | var shader_program: Shader = Shader.create(arena, "content/shader.vs", "content/shader.fs");
53 |
54 | // set up vertex data (and buffer(s)) and configure vertex attributes
55 | // ------------------------------------------------------------------
56 |
57 | const vertices_2D = [_]f32{
58 | // positions // colors // texture coords
59 | 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
60 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
61 | -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
62 | -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
63 | };
64 |
65 | _ = vertices_2D;
66 |
67 | const vertices_3D = [_]f32{
68 | -0.5, -0.5, -0.5, 0.0, 0.0,
69 | 0.5, -0.5, -0.5, 1.0, 0.0,
70 | 0.5, 0.5, -0.5, 1.0, 1.0,
71 | 0.5, 0.5, -0.5, 1.0, 1.0,
72 | -0.5, 0.5, -0.5, 0.0, 1.0,
73 | -0.5, -0.5, -0.5, 0.0, 0.0,
74 |
75 | -0.5, -0.5, 0.5, 0.0, 0.0,
76 | 0.5, -0.5, 0.5, 1.0, 0.0,
77 | 0.5, 0.5, 0.5, 1.0, 1.0,
78 | 0.5, 0.5, 0.5, 1.0, 1.0,
79 | -0.5, 0.5, 0.5, 0.0, 1.0,
80 | -0.5, -0.5, 0.5, 0.0, 0.0,
81 |
82 | -0.5, 0.5, 0.5, 1.0, 0.0,
83 | -0.5, 0.5, -0.5, 1.0, 1.0,
84 | -0.5, -0.5, -0.5, 0.0, 1.0,
85 | -0.5, -0.5, -0.5, 0.0, 1.0,
86 | -0.5, -0.5, 0.5, 0.0, 0.0,
87 | -0.5, 0.5, 0.5, 1.0, 0.0,
88 |
89 | 0.5, 0.5, 0.5, 1.0, 0.0,
90 | 0.5, 0.5, -0.5, 1.0, 1.0,
91 | 0.5, -0.5, -0.5, 0.0, 1.0,
92 | 0.5, -0.5, -0.5, 0.0, 1.0,
93 | 0.5, -0.5, 0.5, 0.0, 0.0,
94 | 0.5, 0.5, 0.5, 1.0, 0.0,
95 |
96 | -0.5, -0.5, -0.5, 0.0, 1.0,
97 | 0.5, -0.5, -0.5, 1.0, 1.0,
98 | 0.5, -0.5, 0.5, 1.0, 0.0,
99 | 0.5, -0.5, 0.5, 1.0, 0.0,
100 | -0.5, -0.5, 0.5, 0.0, 0.0,
101 | -0.5, -0.5, -0.5, 0.0, 1.0,
102 |
103 | -0.5, 0.5, -0.5, 0.0, 1.0,
104 | 0.5, 0.5, -0.5, 1.0, 1.0,
105 | 0.5, 0.5, 0.5, 1.0, 0.0,
106 | 0.5, 0.5, 0.5, 1.0, 0.0,
107 | -0.5, 0.5, 0.5, 0.0, 0.0,
108 | -0.5, 0.5, -0.5, 0.0, 1.0,
109 | };
110 |
111 | const cube_positions = [_][3]f32{
112 | .{ 0.0, 0.0, 0.0 },
113 | .{ 2.0, 5.0, -15.0 },
114 | .{ -1.5, -2.2, -2.5 },
115 | .{ -3.8, -2.0, -12.3 },
116 | .{ 2.4, -0.4, -3.5 },
117 | .{ -1.7, 3.0, -7.5 },
118 | .{ 1.3, -2.0, -2.5 },
119 | .{ 1.5, 2.0, -2.5 },
120 | .{ 1.5, 0.2, -1.5 },
121 | .{ -1.3, 1.0, -1.5 },
122 | };
123 |
124 | var VBO: c_uint = undefined;
125 | var VAO: c_uint = undefined;
126 |
127 | gl.genVertexArrays(1, &VAO);
128 | defer gl.deleteVertexArrays(1, &VAO);
129 |
130 | gl.genBuffers(1, &VBO);
131 | defer gl.deleteBuffers(1, &VBO);
132 |
133 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
134 | gl.bindVertexArray(VAO);
135 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
136 | // Fill our buffer with the vertex data
137 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices_3D.len, &vertices_3D, gl.STATIC_DRAW);
138 |
139 | // vertex
140 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 5 * @sizeOf(f32), null);
141 | gl.enableVertexAttribArray(0);
142 |
143 | // texture coords
144 | const tex_offset: [*c]c_uint = (3 * @sizeOf(f32));
145 | gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 5 * @sizeOf(f32), tex_offset);
146 | gl.enableVertexAttribArray(1);
147 |
148 | // zstbi: loading an image.
149 | zstbi.init(allocator);
150 | defer zstbi.deinit();
151 |
152 | const image1_path = common.pathToContent(arena, "content/container.jpg") catch unreachable;
153 | var image1 = try zstbi.Image.init(&image1_path, 0);
154 | defer image1.deinit();
155 | std.debug.print(
156 | "\nImage 1 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n",
157 | .{ image1.width, image1.height, image1.num_components },
158 | );
159 |
160 | zstbi.setFlipVerticallyOnLoad(true);
161 | const image2_path = common.pathToContent(arena, "content/awesomeface.png") catch unreachable;
162 | var image2 = try zstbi.Image.init(&image2_path, 0);
163 | defer image2.deinit();
164 | std.debug.print(
165 | "\nImage 2 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n",
166 | .{ image2.width, image2.height, image2.num_components },
167 | );
168 |
169 | // Create and bind texture1 resource
170 | var texture1: c_uint = undefined;
171 |
172 | gl.genTextures(1, &texture1);
173 | gl.activeTexture(gl.TEXTURE0); // activate the texture unit first before binding texture
174 | gl.bindTexture(gl.TEXTURE_2D, texture1);
175 |
176 | // set the texture1 wrapping parameters
177 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
178 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
179 | // set texture1 filtering parameters
180 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
181 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
182 |
183 | // Generate the texture1
184 | gl.texImage2D(
185 | gl.TEXTURE_2D,
186 | 0,
187 | gl.RGB,
188 | @intCast(c_int, image1.width),
189 | @intCast(c_int, image1.height),
190 | 0,
191 | gl.RGB,
192 | gl.UNSIGNED_BYTE,
193 | @ptrCast([*c]const u8, image1.data),
194 | );
195 | gl.generateMipmap(gl.TEXTURE_2D);
196 |
197 | // Texture2
198 | var texture2: c_uint = undefined;
199 |
200 | gl.genTextures(1, &texture2);
201 | gl.activeTexture(gl.TEXTURE1); // activate the texture unit first before binding texture
202 | gl.bindTexture(gl.TEXTURE_2D, texture2);
203 |
204 | // set the texture1 wrapping parameters
205 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
206 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
207 | // set texture1 filtering parameters
208 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
209 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
210 |
211 | // Generate the texture1
212 | gl.texImage2D(
213 | gl.TEXTURE_2D,
214 | 0,
215 | gl.RGB,
216 | @intCast(c_int, image2.width),
217 | @intCast(c_int, image2.height),
218 | 0,
219 | gl.RGBA,
220 | gl.UNSIGNED_BYTE,
221 | @ptrCast([*c]const u8, image2.data),
222 | );
223 | gl.generateMipmap(gl.TEXTURE_2D);
224 |
225 | // Enable OpenGL depth testing (use Z-buffer information)
226 | gl.enable(gl.DEPTH_TEST);
227 |
228 | shader_program.use();
229 | shader_program.setInt("texture1", 0);
230 | shader_program.setInt("texture2", 1);
231 |
232 | // Create the transformation matrices:
233 | // Degree to radians conversion factor
234 | const rad_conversion = math.pi / 180.0;
235 |
236 | // Buffer to store Model matrix
237 | var model: [16]f32 = undefined;
238 |
239 | // View matrix
240 | var view: [16]f32 = undefined;
241 |
242 | // Buffer to store Orojection matrix (in render loop)
243 | var proj: [16]f32 = undefined;
244 |
245 | while (!window.shouldClose()) {
246 | processInput(window);
247 |
248 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
249 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
250 | gl.activeTexture(gl.TEXTURE0);
251 | gl.bindTexture(gl.TEXTURE_2D, texture1);
252 | gl.activeTexture(gl.TEXTURE1);
253 | gl.bindTexture(gl.TEXTURE_2D, texture2);
254 | gl.bindVertexArray(VAO);
255 |
256 | // Projection matrix
257 | const projM = x: {
258 | var window_size = window.getSize();
259 | var fov = @intToFloat(f32, window_size.width) / @intToFloat(f32, window_size.height);
260 | var projM = zm.perspectiveFovRhGl(45.0 * rad_conversion, fov, 0.1, 100.0);
261 | break :x projM;
262 | };
263 | zm.storeMat(&proj, projM);
264 | shader_program.setMat4f("projection", proj);
265 |
266 | // View matrix: Camera
267 | const radius: f32 = 10.0;
268 | const camX: f32 = @floatCast(f32, @sin(glfw.getTime())) * radius;
269 | const camZ: f32 = @floatCast(f32, @cos(glfw.getTime())) * radius;
270 | const viewM = zm.lookAtRh(
271 | zm.loadArr3(.{ camX, 0.0, camZ }),
272 | zm.loadArr3(.{ 0.0, 0.0, 0.0 }),
273 | zm.loadArr3(.{ 0.0, 1.0, 0.0 }),
274 | );
275 | zm.storeMat(&view, viewM);
276 | shader_program.setMat4f("view", view);
277 |
278 | for (cube_positions, 0..) |cube_position, i| {
279 | // Model matrix
280 | const cube_trans = zm.translation(cube_position[0], cube_position[1], cube_position[2]);
281 | const rotation_direction = (((@mod(@intToFloat(f32, i + 1), 2.0)) * 2.0) - 1.0);
282 | const cube_rot = zm.matFromAxisAngle(
283 | zm.f32x4(1.0, 0.3, 0.5, 1.0),
284 | @floatCast(f32, glfw.getTime()) * 55.0 * rotation_direction * rad_conversion,
285 | );
286 | const modelM = zm.mul(cube_rot, cube_trans);
287 | zm.storeMat(&model, modelM);
288 | shader_program.setMat4f("model", model);
289 |
290 | gl.drawArrays(gl.TRIANGLES, 0, 36);
291 | }
292 |
293 | window.swapBuffers();
294 | glfw.pollEvents();
295 | }
296 | }
297 |
298 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
299 | _ = p;
300 | return glfw.getProcAddress(proc);
301 | }
302 |
303 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
304 | _ = window;
305 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
306 | }
307 |
308 | fn processInput(window: glfw.Window) void {
309 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
310 | _ = glfw.Window.setShouldClose(window, true);
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "content/";
10 |
11 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "coordinate_system",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/content/awesomeface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/coordinate_systems/content/awesomeface.png
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/content/container.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/coordinate_systems/content/container.jpg
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/content/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 | in vec2 texCoord;
5 |
6 | uniform sampler2D texture1;
7 | uniform sampler2D texture2;
8 |
9 | void main()
10 | {
11 | FragColor = mix(texture(texture1, texCoord),texture(texture2, texCoord), 0.5f);
12 | };
13 |
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/content/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec2 aTexCoord;
4 |
5 | out vec2 texCoord;
6 |
7 | uniform mat4 model;
8 | uniform mat4 view;
9 | uniform mat4 projection;
10 |
11 | void main()
12 | {
13 | gl_Position = projection * view * model * vec4(aPos, 1.0f);
14 | texCoord = aTexCoord;
15 | }
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/coordinate_systems/image.png
--------------------------------------------------------------------------------
/src/getting_started/coordinate_systems/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = std.math;
3 | const glfw = @import("glfw");
4 | const zstbi = @import("zstbi");
5 | const zm = @import("zmath");
6 | const gl = @import("gl");
7 | const Shader = @import("Shader");
8 | const common = @import("common");
9 |
10 | const WindowSize = struct {
11 | pub const width: u32 = 800;
12 | pub const height: u32 = 600;
13 | };
14 |
15 | pub fn main() !void {
16 |
17 | // glfw: initialize and configure
18 | // ------------------------------
19 | if (!glfw.init(.{})) {
20 | std.log.err("GLFW initialization failed", .{});
21 | return;
22 | }
23 | defer glfw.terminate();
24 |
25 | // glfw window creation
26 | // --------------------
27 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
28 | .opengl_profile = .opengl_core_profile,
29 | .context_version_major = 4,
30 | .context_version_minor = 1,
31 | }) orelse {
32 | std.log.err("GLFW Window creation failed", .{});
33 | return;
34 | };
35 | defer window.destroy();
36 |
37 | glfw.makeContextCurrent(window);
38 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
39 |
40 | // Load all OpenGL function pointers
41 | // ---------------------------------------
42 | const proc: glfw.GLProc = undefined;
43 | try gl.load(proc, glGetProcAddress);
44 |
45 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
46 | var allocator = gpa.allocator();
47 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
48 | defer arena_allocator_state.deinit();
49 | const arena = arena_allocator_state.allocator();
50 |
51 | // create shader program
52 | var shader_program: Shader = Shader.create(arena, "content/shader.vs", "content/shader.fs");
53 |
54 | // set up vertex data (and buffer(s)) and configure vertex attributes
55 | // ------------------------------------------------------------------
56 |
57 | const vertices_2D = [_]f32{
58 | // positions // colors // texture coords
59 | 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
60 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
61 | -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
62 | -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
63 | };
64 |
65 | _ = vertices_2D;
66 |
67 | const vertices_3D = [_]f32{
68 | -0.5, -0.5, -0.5, 0.0, 0.0,
69 | 0.5, -0.5, -0.5, 1.0, 0.0,
70 | 0.5, 0.5, -0.5, 1.0, 1.0,
71 | 0.5, 0.5, -0.5, 1.0, 1.0,
72 | -0.5, 0.5, -0.5, 0.0, 1.0,
73 | -0.5, -0.5, -0.5, 0.0, 0.0,
74 |
75 | -0.5, -0.5, 0.5, 0.0, 0.0,
76 | 0.5, -0.5, 0.5, 1.0, 0.0,
77 | 0.5, 0.5, 0.5, 1.0, 1.0,
78 | 0.5, 0.5, 0.5, 1.0, 1.0,
79 | -0.5, 0.5, 0.5, 0.0, 1.0,
80 | -0.5, -0.5, 0.5, 0.0, 0.0,
81 |
82 | -0.5, 0.5, 0.5, 1.0, 0.0,
83 | -0.5, 0.5, -0.5, 1.0, 1.0,
84 | -0.5, -0.5, -0.5, 0.0, 1.0,
85 | -0.5, -0.5, -0.5, 0.0, 1.0,
86 | -0.5, -0.5, 0.5, 0.0, 0.0,
87 | -0.5, 0.5, 0.5, 1.0, 0.0,
88 |
89 | 0.5, 0.5, 0.5, 1.0, 0.0,
90 | 0.5, 0.5, -0.5, 1.0, 1.0,
91 | 0.5, -0.5, -0.5, 0.0, 1.0,
92 | 0.5, -0.5, -0.5, 0.0, 1.0,
93 | 0.5, -0.5, 0.5, 0.0, 0.0,
94 | 0.5, 0.5, 0.5, 1.0, 0.0,
95 |
96 | -0.5, -0.5, -0.5, 0.0, 1.0,
97 | 0.5, -0.5, -0.5, 1.0, 1.0,
98 | 0.5, -0.5, 0.5, 1.0, 0.0,
99 | 0.5, -0.5, 0.5, 1.0, 0.0,
100 | -0.5, -0.5, 0.5, 0.0, 0.0,
101 | -0.5, -0.5, -0.5, 0.0, 1.0,
102 |
103 | -0.5, 0.5, -0.5, 0.0, 1.0,
104 | 0.5, 0.5, -0.5, 1.0, 1.0,
105 | 0.5, 0.5, 0.5, 1.0, 0.0,
106 | 0.5, 0.5, 0.5, 1.0, 0.0,
107 | -0.5, 0.5, 0.5, 0.0, 0.0,
108 | -0.5, 0.5, -0.5, 0.0, 1.0,
109 | };
110 |
111 | const cube_positions = [_][3]f32{
112 | .{ 0.0, 0.0, 0.0 },
113 | .{ 2.0, 5.0, -15.0 },
114 | .{ -1.5, -2.2, -2.5 },
115 | .{ -3.8, -2.0, -12.3 },
116 | .{ 2.4, -0.4, -3.5 },
117 | .{ -1.7, 3.0, -7.5 },
118 | .{ 1.3, -2.0, -2.5 },
119 | .{ 1.5, 2.0, -2.5 },
120 | .{ 1.5, 0.2, -1.5 },
121 | .{ -1.3, 1.0, -1.5 },
122 | };
123 |
124 | var VBO: c_uint = undefined;
125 | var VAO: c_uint = undefined;
126 |
127 | gl.genVertexArrays(1, &VAO);
128 | defer gl.deleteVertexArrays(1, &VAO);
129 |
130 | gl.genBuffers(1, &VBO);
131 | defer gl.deleteBuffers(1, &VBO);
132 |
133 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
134 | gl.bindVertexArray(VAO);
135 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
136 | // Fill our buffer with the vertex data
137 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices_3D.len, &vertices_3D, gl.STATIC_DRAW);
138 |
139 | // vertex
140 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 5 * @sizeOf(f32), null);
141 | gl.enableVertexAttribArray(0);
142 |
143 | // texture coords
144 | const tex_offset: [*c]c_uint = (3 * @sizeOf(f32));
145 | gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 5 * @sizeOf(f32), tex_offset);
146 | gl.enableVertexAttribArray(1);
147 |
148 | // zstbi: loading an image.
149 | zstbi.init(allocator);
150 | defer zstbi.deinit();
151 |
152 | const image1_path = common.pathToContent(arena, "content/container.jpg") catch unreachable;
153 | var image1 = try zstbi.Image.init(&image1_path, 0);
154 | defer image1.deinit();
155 | std.debug.print("\nImage 1 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n", .{ image1.width, image1.height, image1.num_components });
156 |
157 | zstbi.setFlipVerticallyOnLoad(true);
158 | const image2_path = common.pathToContent(arena, "content/awesomeface.png") catch unreachable;
159 | var image2 = try zstbi.Image.init(&image2_path, 0);
160 | defer image2.deinit();
161 | std.debug.print("\nImage 2 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n", .{ image2.width, image2.height, image2.num_components });
162 |
163 | // Create and bind texture1 resource
164 | var texture1: c_uint = undefined;
165 |
166 | gl.genTextures(1, &texture1);
167 | gl.activeTexture(gl.TEXTURE0); // activate the texture unit first before binding texture
168 | gl.bindTexture(gl.TEXTURE_2D, texture1);
169 |
170 | // set the texture1 wrapping parameters
171 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
172 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
173 | // set texture1 filtering parameters
174 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
175 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
176 |
177 | // Generate the texture1
178 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image1.width), @intCast(c_int, image1.height), 0, gl.RGB, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image1.data));
179 | gl.generateMipmap(gl.TEXTURE_2D);
180 |
181 | // Texture2
182 | var texture2: c_uint = undefined;
183 |
184 | gl.genTextures(1, &texture2);
185 | gl.activeTexture(gl.TEXTURE1); // activate the texture unit first before binding texture
186 | gl.bindTexture(gl.TEXTURE_2D, texture2);
187 |
188 | // set the texture1 wrapping parameters
189 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
190 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
191 | // set texture1 filtering parameters
192 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
193 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
194 |
195 | // Generate the texture1
196 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image2.width), @intCast(c_int, image2.height), 0, gl.RGBA, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image2.data));
197 | gl.generateMipmap(gl.TEXTURE_2D);
198 |
199 | // Enable OpenGL depth testing (use Z-buffer information)
200 | gl.enable(gl.DEPTH_TEST);
201 |
202 | shader_program.use();
203 | shader_program.setInt("texture1", 0);
204 | shader_program.setInt("texture2", 1);
205 |
206 | // Create the transformation matrices:
207 | // Degree to radians conversion factor
208 | const rad_conversion = math.pi / 180.0;
209 |
210 | // Buffer to store Model matrix
211 | var model: [16]f32 = undefined;
212 |
213 | // View matrix
214 | const viewM = zm.translation(0.0, 0.0, -5.0);
215 | var view: [16]f32 = undefined;
216 | zm.storeMat(&view, viewM);
217 | shader_program.setMat4f("view", view);
218 |
219 | // Buffer to store Orojection matrix (in render loop)
220 | var proj: [16]f32 = undefined;
221 |
222 | while (!window.shouldClose()) {
223 | processInput(window);
224 |
225 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
226 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
227 | gl.activeTexture(gl.TEXTURE0);
228 | gl.bindTexture(gl.TEXTURE_2D, texture1);
229 | gl.activeTexture(gl.TEXTURE1);
230 | gl.bindTexture(gl.TEXTURE_2D, texture2);
231 | gl.bindVertexArray(VAO);
232 |
233 | // Projection matrix
234 | const projM = x: {
235 | var window_size = window.getSize();
236 | var fov = @intToFloat(f32, window_size.width) / @intToFloat(f32, window_size.height);
237 | var projM = zm.perspectiveFovRhGl(45.0 * rad_conversion, fov, 0.1, 100.0);
238 | break :x projM;
239 | };
240 | zm.storeMat(&proj, projM);
241 |
242 | shader_program.setMat4f("projection", proj);
243 |
244 | for (cube_positions, 0..) |cube_position, i| {
245 | const cube_trans = zm.translation(cube_position[0], cube_position[1], cube_position[2]);
246 | const rotation_direction = (((@mod(@intToFloat(f32, i + 1), 2.0)) * 2.0) - 1.0);
247 | const cube_rot = zm.matFromAxisAngle(
248 | zm.f32x4(1.0, 0.3, 0.5, 1.0),
249 | @floatCast(f32, glfw.getTime()) * 55.0 * rotation_direction * rad_conversion,
250 | );
251 | const modelM = zm.mul(cube_rot, cube_trans);
252 | zm.storeMat(&model, modelM);
253 | shader_program.setMat4f("model", model);
254 | gl.drawArrays(gl.TRIANGLES, 0, 36);
255 | }
256 |
257 | window.swapBuffers();
258 | glfw.pollEvents();
259 | }
260 | }
261 |
262 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
263 | _ = p;
264 | return glfw.getProcAddress(proc);
265 | }
266 |
267 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
268 | _ = window;
269 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
270 | }
271 |
272 | fn processInput(window: glfw.Window) void {
273 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
274 | _ = glfw.Window.setShouldClose(window, true);
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/src/getting_started/hello_rectangle/README.md:
--------------------------------------------------------------------------------
1 | # Indexed drawing of a rectangle with Element Buffer Objects (EBO) aka (index buffer).
2 |
3 | From [Hello Triangle](https://learnopengl.com/Getting-started/Hello-Triangle) chapter: Element Buffer Objects section.
4 |
5 | 
--------------------------------------------------------------------------------
/src/getting_started/hello_rectangle/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
10 | const exe = b.addExecutable(.{
11 | .name = "hello_rectangle",
12 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
13 | .target = options.target,
14 | .optimize = options.build_mode,
15 | });
16 |
17 | return exe;
18 | }
19 |
--------------------------------------------------------------------------------
/src/getting_started/hello_rectangle/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/hello_rectangle/image.png
--------------------------------------------------------------------------------
/src/getting_started/hello_rectangle/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const glfw = @import("glfw");
3 | const gl = @import("gl");
4 |
5 | const vertexShaderSource =
6 | \\ #version 410 core
7 | \\ layout (location = 0) in vec3 aPos;
8 | \\ void main()
9 | \\ {
10 | \\ gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
11 | \\ }
12 | ;
13 |
14 | const fragmentShaderSource =
15 | \\ #version 410 core
16 | \\ out vec4 FragColor;
17 | \\ void main() {
18 | \\ FragColor = vec4(1.0, 0.5, 0.2, 1.0);
19 | \\ }
20 | ;
21 |
22 | const WindowSize = struct {
23 | pub const width: u32 = 800;
24 | pub const height: u32 = 600;
25 | };
26 |
27 | pub fn main() !void {
28 |
29 | // glfw: initialize and configure
30 | // ------------------------------
31 | if (!glfw.init(.{})) {
32 | std.log.err("GLFW initialization failed", .{});
33 | return;
34 | }
35 | defer glfw.terminate();
36 |
37 | // glfw window creation
38 | // --------------------
39 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
40 | .opengl_profile = .opengl_core_profile,
41 | .context_version_major = 4,
42 | .context_version_minor = 1,
43 | }) orelse {
44 | std.log.err("GLFW Window creation failed", .{});
45 | return;
46 | };
47 | defer window.destroy();
48 |
49 | glfw.makeContextCurrent(window);
50 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
51 |
52 | // Load all OpenGL function pointers
53 | // ---------------------------------------
54 | const proc: glfw.GLProc = undefined;
55 | try gl.load(proc, glGetProcAddress);
56 |
57 | // Create vertex shader
58 | var vertexShader: c_uint = undefined;
59 | vertexShader = gl.createShader(gl.VERTEX_SHADER);
60 | defer gl.deleteShader(vertexShader);
61 |
62 | // Attach the shader source to the vertex shader object and compile it
63 | gl.shaderSource(vertexShader, 1, @ptrCast([*c]const [*c]const u8, &vertexShaderSource), 0);
64 | gl.compileShader(vertexShader);
65 |
66 | // Check if vertex shader was compiled successfully
67 | var success: c_int = undefined;
68 | var infoLog: [512]u8 = [_]u8{0} ** 512;
69 |
70 | gl.getShaderiv(vertexShader, gl.COMPILE_STATUS, &success);
71 |
72 | if (success == 0) {
73 | gl.getShaderInfoLog(vertexShader, 512, 0, &infoLog);
74 | std.log.err("{s}", .{infoLog});
75 | }
76 |
77 | // Fragment shader
78 | var fragmentShader: c_uint = undefined;
79 | fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
80 | defer gl.deleteShader(fragmentShader);
81 |
82 | gl.shaderSource(fragmentShader, 1, @ptrCast([*c]const [*c]const u8, &fragmentShaderSource), 0);
83 | gl.compileShader(fragmentShader);
84 |
85 | gl.getShaderiv(fragmentShader, gl.COMPILE_STATUS, &success);
86 |
87 | if (success == 0) {
88 | gl.getShaderInfoLog(fragmentShader, 512, 0, &infoLog);
89 | std.log.err("{s}", .{infoLog});
90 | }
91 |
92 | // create a program object
93 | var shaderProgram: c_uint = undefined;
94 | shaderProgram = gl.createProgram();
95 | defer gl.deleteProgram(shaderProgram);
96 |
97 | // attach compiled shader objects to the program object and link
98 | gl.attachShader(shaderProgram, vertexShader);
99 | gl.attachShader(shaderProgram, fragmentShader);
100 | gl.linkProgram(shaderProgram);
101 |
102 | // check if shader linking was successfull
103 | gl.getProgramiv(shaderProgram, gl.LINK_STATUS, &success);
104 | if (success == 0) {
105 | gl.getProgramInfoLog(shaderProgram, 512, 0, &infoLog);
106 | std.log.err("{s}", .{infoLog});
107 | }
108 |
109 | // set up vertex data (and buffer(s)) and configure vertex attributes
110 | // ------------------------------------------------------------------
111 | // const vertices = [9]f32{ -0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0 };
112 | const vertices = [12]f32{
113 | 0.5, 0.5, 0.0, // top right
114 | 0.5, -0.5, 0.0, // bottom right
115 | -0.5, -0.5, 0.0, // bottom left
116 | -0.5, 0.5, 0.0, // top left
117 | };
118 |
119 | const indices = [6]c_uint{
120 | // note that we start from 0!
121 | 0, 1, 3, // first triangle
122 | 1, 2, 3, // second triangle
123 | };
124 |
125 | var VBO: c_uint = undefined;
126 | var VAO: c_uint = undefined;
127 | var EBO: c_uint = undefined;
128 |
129 | gl.genVertexArrays(1, &VAO);
130 | defer gl.deleteVertexArrays(1, &VAO);
131 |
132 | gl.genBuffers(1, &VBO);
133 | defer gl.deleteBuffers(1, &VBO);
134 |
135 | gl.genBuffers(1, &EBO);
136 | defer gl.deleteBuffers(1, &EBO);
137 |
138 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
139 | gl.bindVertexArray(VAO);
140 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
141 | // Fill our buffer with the vertex data
142 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, gl.STATIC_DRAW);
143 | // copy our index array in an element buffer for OpenGL to use
144 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, EBO);
145 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 6 * @sizeOf(c_uint), &indices, gl.STATIC_DRAW);
146 |
147 | // Specify and link our vertext attribute description
148 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * @sizeOf(f32), null);
149 | gl.enableVertexAttribArray(0);
150 |
151 | // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
152 | gl.bindBuffer(gl.ARRAY_BUFFER, 0);
153 |
154 | // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
155 | // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
156 | gl.bindVertexArray(0);
157 |
158 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0);
159 |
160 | // Wireframe mode
161 | gl.polygonMode(gl.FRONT_AND_BACK, gl.LINE);
162 |
163 | while (!window.shouldClose()) {
164 | processInput(window);
165 |
166 | gl.clearColor(0.2, 0.3, 0.3, 1.0);
167 | gl.clear(gl.COLOR_BUFFER_BIT);
168 |
169 | // Activate shaderProgram
170 | gl.useProgram(shaderProgram);
171 | gl.bindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
172 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, EBO);
173 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null);
174 | gl.bindVertexArray(0);
175 |
176 | window.swapBuffers();
177 | glfw.pollEvents();
178 | }
179 | }
180 |
181 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
182 | _ = p;
183 | return glfw.getProcAddress(proc);
184 | }
185 |
186 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
187 | _ = window;
188 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
189 | }
190 |
191 | fn processInput(window: glfw.Window) void {
192 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
193 | _ = glfw.Window.setShouldClose(window, true);
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/README.md:
--------------------------------------------------------------------------------
1 | # [Hello Triangle](https://learnopengl.com/Getting-started/Hello-Triangle) book chapter.
2 |
3 |
4 | Exercise 1: Try to draw 2 triangles next to each other using glDrawArrays by adding more vertices to your data
5 |
6 |
7 | Exercise 2: Now create the same 2 triangles using two different VAOs and VBOs for their data
8 |
9 |
10 | Exercise 3: Create two shader programs where the second program uses a different fragment shader that outputs the color yellow; draw both triangles again where one outputs the color yellow.
11 |
12 |
13 | ```
14 | const std = @import("std");
15 | const glfw = @import("glfw");
16 | const gl = @import("gl");
17 |
18 | const vertexShaderSource =
19 | \\ #version 410 core
20 | \\ layout (location = 0) in vec3 aPos;
21 | \\ void main()
22 | \\ {
23 | \\ gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
24 | \\ }
25 | ;
26 |
27 | const fragmentShaderSource_1 =
28 | \\ #version 410 core
29 | \\ out vec4 FragColor;
30 | \\ void main() {
31 | \\ FragColor = vec4(1.0, 0.5, 0.2, 1.0);
32 | \\ }
33 | ;
34 |
35 | const fragmentShaderSource_2 =
36 | \\ #version 410 core
37 | \\ out vec4 FragColor;
38 | \\ void main() {
39 | \\ FragColor = vec4(1.0, 1.0, 0.2, 1.0);
40 | \\ }
41 | ;
42 |
43 | const WindowSize = struct {
44 | pub const width: u32 = 800;
45 | pub const height: u32 = 600;
46 | };
47 |
48 | pub fn main() !void {
49 |
50 | // glfw: initialize and configure
51 | // ------------------------------
52 | if (!glfw.init(.{})) {
53 | std.log.err("GLFW initialization failed", .{});
54 | return;
55 | }
56 | defer glfw.terminate();
57 |
58 | // glfw window creation
59 | // --------------------
60 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
61 | .opengl_profile = .opengl_core_profile,
62 | .context_version_major = 4,
63 | .context_version_minor = 1,
64 | }) orelse {
65 | std.log.err("GLFW Window creation failed", .{});
66 | return;
67 | };
68 | defer window.destroy();
69 |
70 | glfw.makeContextCurrent(window);
71 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
72 |
73 | // Load all OpenGL function pointers
74 | // ---------------------------------------
75 | const proc: glfw.GLProc = undefined;
76 | try gl.load(proc, glGetProcAddress);
77 |
78 | // Create vertex shader
79 | var vertexShader: c_uint = undefined;
80 | vertexShader = gl.createShader(gl.VERTEX_SHADER);
81 | defer gl.deleteShader(vertexShader);
82 |
83 | // Attach the shader source to the vertex shader object and compile it
84 | gl.shaderSource(vertexShader, 1, @ptrCast([*c]const [*c]const u8, &vertexShaderSource), 0);
85 | gl.compileShader(vertexShader);
86 |
87 | // Check if vertex shader was compiled successfully
88 | var success: c_int = undefined;
89 | var infoLog: [512]u8 = [_]u8{0} ** 512;
90 |
91 | gl.getShaderiv(vertexShader, gl.COMPILE_STATUS, &success);
92 |
93 | if (success == 0) {
94 | gl.getShaderInfoLog(vertexShader, 512, 0, &infoLog);
95 | std.log.err("{s}", .{infoLog});
96 | }
97 |
98 | // Fragment shader
99 | var fragmentShader_1: c_uint = undefined;
100 | fragmentShader_1 = gl.createShader(gl.FRAGMENT_SHADER);
101 | defer gl.deleteShader(fragmentShader_1);
102 |
103 | gl.shaderSource(fragmentShader_1, 1, @ptrCast([*c]const [*c]const u8, &fragmentShaderSource_1), 0);
104 | gl.compileShader(fragmentShader_1);
105 |
106 | gl.getShaderiv(fragmentShader_1, gl.COMPILE_STATUS, &success);
107 |
108 | if (success == 0) {
109 | gl.getShaderInfoLog(fragmentShader_1, 512, 0, &infoLog);
110 | std.log.err("{s}", .{infoLog});
111 | }
112 |
113 | // Fragment shader
114 | var fragmentShader_2: c_uint = undefined;
115 | fragmentShader_2 = gl.createShader(gl.FRAGMENT_SHADER);
116 | defer gl.deleteShader(fragmentShader_2);
117 |
118 | gl.shaderSource(fragmentShader_2, 1, @ptrCast([*c]const [*c]const u8, &fragmentShaderSource_2), 0);
119 | gl.compileShader(fragmentShader_2);
120 |
121 | gl.getShaderiv(fragmentShader_2, gl.COMPILE_STATUS, &success);
122 |
123 | if (success == 0) {
124 | gl.getShaderInfoLog(fragmentShader_2, 512, 0, &infoLog);
125 | std.log.err("{s}", .{infoLog});
126 | }
127 |
128 | // create a program object
129 | var shaderProgram_1: c_uint = undefined;
130 | shaderProgram_1 = gl.createProgram();
131 | defer gl.deleteProgram(shaderProgram_1);
132 |
133 | // create a program object
134 | var shaderProgram_2: c_uint = undefined;
135 | shaderProgram_2 = gl.createProgram();
136 | defer gl.deleteProgram(shaderProgram_2);
137 |
138 | // attach compiled shader objects to the program object and link
139 | gl.attachShader(shaderProgram_1, vertexShader);
140 | gl.attachShader(shaderProgram_1, fragmentShader_1);
141 | gl.linkProgram(shaderProgram_1);
142 |
143 | // attach compiled shader objects to the program object and link
144 | gl.attachShader(shaderProgram_2, vertexShader);
145 | gl.attachShader(shaderProgram_2, fragmentShader_2);
146 | gl.linkProgram(shaderProgram_2);
147 |
148 | // check if shader linking was successfull
149 | gl.getProgramiv(shaderProgram_1, gl.LINK_STATUS, &success);
150 | if (success == 0) {
151 | gl.getProgramInfoLog(shaderProgram_1, 512, 0, &infoLog);
152 | std.log.err("{s}", .{infoLog});
153 | }
154 |
155 | // check if shader linking was successfull
156 | gl.getProgramiv(shaderProgram_2, gl.LINK_STATUS, &success);
157 | if (success == 0) {
158 | gl.getProgramInfoLog(shaderProgram_2, 512, 0, &infoLog);
159 | std.log.err("{s}", .{infoLog});
160 | }
161 |
162 | // set up vertex data (and buffer(s)) and configure vertex attributes
163 | // ------------------------------------------------------------------
164 | const vertices_1 = [9]f32 {
165 | // Triangle 1
166 | -1.0, -0.5, 0.0, 0.0, -0.5, 0.0, -0.5, 0.5, 0.0,
167 | };
168 |
169 | const vertices_2 = [9]f32 {
170 | // Triangle 2
171 | 0.0, -0.5, 0.0, 1.0, -0.5, 0.0, 0.5, 0.5, 0.0
172 | };
173 |
174 | var VBOs: [2]c_uint = undefined;
175 | var VAOs: [2]c_uint = undefined;
176 |
177 | gl.genVertexArrays(2, &VAOs);
178 | defer gl.deleteVertexArrays(2, &VAOs);
179 |
180 | gl.genBuffers(2, &VBOs);
181 | defer gl.deleteBuffers(1, &VBOs);
182 |
183 | // bind the Vertex Array Object for trangle 1 first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
184 | gl.bindVertexArray(VAOs[0]);
185 | gl.bindBuffer(gl.ARRAY_BUFFER, VBOs[0]);
186 | // Fill our buffer with the vertex data for traingle 1
187 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices_1.len, &vertices_1, gl.STATIC_DRAW);
188 | // Specify and link our vertext attribute description
189 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * @sizeOf(f32), null);
190 | gl.enableVertexAttribArray(0);
191 |
192 | // bind the Vertex Array Object for triangle 2 first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
193 | gl.bindVertexArray(VAOs[1]);
194 | gl.bindBuffer(gl.ARRAY_BUFFER, VBOs[1]);
195 | // Fill our buffer with the vertex data
196 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices_2.len, &vertices_2, gl.STATIC_DRAW);
197 | // Specify and link our vertext attribute description
198 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * @sizeOf(f32), null);
199 | gl.enableVertexAttribArray(0);
200 |
201 | // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
202 | gl.bindBuffer(gl.ARRAY_BUFFER, 0);
203 |
204 | // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
205 | // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
206 | gl.bindVertexArray(0);
207 |
208 | while (!window.shouldClose()) {
209 | processInput(window);
210 |
211 | gl.clearColor(0.2, 0.3, 0.3, 1.0);
212 | gl.clear(gl.COLOR_BUFFER_BIT);
213 |
214 | // Activate shaderProgram
215 | gl.useProgram(shaderProgram_1);
216 | // Draw triangle 1
217 | gl.bindVertexArray(VAOs[0]);
218 | gl.drawArrays(gl.TRIANGLES, 0, 3);
219 |
220 | // Activate shaderProgram
221 | gl.useProgram(shaderProgram_2);
222 | // Draw triangle 2
223 | gl.bindVertexArray(VAOs[1]);
224 | gl.drawArrays(gl.TRIANGLES, 0, 3);
225 |
226 | window.swapBuffers();
227 | glfw.pollEvents();
228 | }
229 | }
230 |
231 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
232 | _ = p;
233 | return glfw.getProcAddress(proc);
234 | }
235 |
236 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
237 | _ = window;
238 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
239 | }
240 |
241 | fn processInput(window: glfw.Window) void {
242 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
243 | _ = glfw.Window.setShouldClose(window, true);
244 | }
245 | }
246 |
247 | ```
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
10 | const exe = b.addExecutable(.{
11 | .name = "hello_trangle",
12 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
13 | .target = options.target,
14 | .optimize = options.build_mode,
15 | });
16 |
17 | return exe;
18 | }
19 |
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/excercise_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/hello_triangle/excercise_1.png
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/exercise_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/hello_triangle/exercise_2.png
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/exercise_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/hello_triangle/exercise_3.png
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/hello_triangle/image.png
--------------------------------------------------------------------------------
/src/getting_started/hello_triangle/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const glfw = @import("glfw");
3 | const gl = @import("gl");
4 |
5 | const vertexShaderSource =
6 | \\ #version 410 core
7 | \\ layout (location = 0) in vec3 aPos;
8 | \\ void main()
9 | \\ {
10 | \\ gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
11 | \\ }
12 | ;
13 |
14 | const fragmentShaderSource =
15 | \\ #version 410 core
16 | \\ out vec4 FragColor;
17 | \\ void main() {
18 | \\ FragColor = vec4(1.0, 1.0, 0.2, 1.0);
19 | \\ }
20 | ;
21 |
22 | const WindowSize = struct {
23 | pub const width: u32 = 800;
24 | pub const height: u32 = 600;
25 | };
26 |
27 | pub fn main() !void {
28 |
29 | // glfw: initialize and configure
30 | // ------------------------------
31 | if (!glfw.init(.{})) {
32 | std.log.err("GLFW initialization failed", .{});
33 | return;
34 | }
35 | defer glfw.terminate();
36 |
37 | // glfw window creation
38 | // --------------------
39 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
40 | .opengl_profile = .opengl_core_profile,
41 | .context_version_major = 4,
42 | .context_version_minor = 1,
43 | }) orelse {
44 | std.log.err("GLFW Window creation failed", .{});
45 | return;
46 | };
47 | defer window.destroy();
48 |
49 | glfw.makeContextCurrent(window);
50 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
51 |
52 | // Load all OpenGL function pointers
53 | // ---------------------------------------
54 | const proc: glfw.GLProc = undefined;
55 | try gl.load(proc, glGetProcAddress);
56 |
57 | // Create vertex shader
58 | var vertexShader: c_uint = undefined;
59 | vertexShader = gl.createShader(gl.VERTEX_SHADER);
60 | defer gl.deleteShader(vertexShader);
61 |
62 | // Attach the shader source to the vertex shader object and compile it
63 | gl.shaderSource(vertexShader, 1, @ptrCast([*c]const [*c]const u8, &vertexShaderSource), 0);
64 | gl.compileShader(vertexShader);
65 |
66 | // Check if vertex shader was compiled successfully
67 | var success: c_int = undefined;
68 | var infoLog: [512]u8 = [_]u8{0} ** 512;
69 |
70 | gl.getShaderiv(vertexShader, gl.COMPILE_STATUS, &success);
71 |
72 | if (success == 0) {
73 | gl.getShaderInfoLog(vertexShader, 512, 0, &infoLog);
74 | std.log.err("{s}", .{infoLog});
75 | }
76 |
77 | // Fragment shader
78 | var fragmentShader: c_uint = undefined;
79 | fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
80 | defer gl.deleteShader(fragmentShader);
81 |
82 | gl.shaderSource(fragmentShader, 1, @ptrCast([*c]const [*c]const u8, &fragmentShaderSource), 0);
83 | gl.compileShader(fragmentShader);
84 |
85 | gl.getShaderiv(fragmentShader, gl.COMPILE_STATUS, &success);
86 |
87 | if (success == 0) {
88 | gl.getShaderInfoLog(fragmentShader, 512, 0, &infoLog);
89 | std.log.err("{s}", .{infoLog});
90 | }
91 |
92 | // create a program object
93 | var shaderProgram: c_uint = undefined;
94 | shaderProgram = gl.createProgram();
95 | std.debug.print("{any}", .{shaderProgram});
96 | defer gl.deleteProgram(shaderProgram);
97 |
98 | // attach compiled shader objects to the program object and link
99 | gl.attachShader(shaderProgram, vertexShader);
100 | gl.attachShader(shaderProgram, fragmentShader);
101 | gl.linkProgram(shaderProgram);
102 |
103 | // check if shader linking was successfull
104 | gl.getProgramiv(shaderProgram, gl.LINK_STATUS, &success);
105 | if (success == 0) {
106 | gl.getProgramInfoLog(shaderProgram, 512, 0, &infoLog);
107 | std.log.err("{s}", .{infoLog});
108 | }
109 |
110 | // set up vertex data (and buffer(s)) and configure vertex attributes
111 | // ------------------------------------------------------------------
112 | const vertices = [9]f32{ -0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0 };
113 | var VBO: c_uint = undefined;
114 | var VAO: c_uint = undefined;
115 |
116 | gl.genVertexArrays(1, &VAO);
117 | defer gl.deleteVertexArrays(1, &VAO);
118 |
119 | gl.genBuffers(1, &VBO);
120 | defer gl.deleteBuffers(1, &VBO);
121 |
122 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
123 | gl.bindVertexArray(VAO);
124 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
125 | // Fill our buffer with the vertex data
126 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, gl.STATIC_DRAW);
127 |
128 | // Specify and link our vertext attribute description
129 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * @sizeOf(f32), null);
130 | gl.enableVertexAttribArray(0);
131 |
132 | // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
133 |
134 | // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
135 | // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
136 |
137 | while (!window.shouldClose()) {
138 | processInput(window);
139 |
140 | gl.clearColor(0.2, 0.3, 0.3, 1.0);
141 | gl.clear(gl.COLOR_BUFFER_BIT);
142 |
143 | // Activate shaderProgram
144 | gl.useProgram(shaderProgram);
145 | gl.bindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
146 | gl.drawArrays(gl.TRIANGLES, 0, 3);
147 |
148 | window.swapBuffers();
149 | glfw.pollEvents();
150 | }
151 | }
152 |
153 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
154 | _ = p;
155 | return glfw.getProcAddress(proc);
156 | }
157 |
158 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
159 | _ = window;
160 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
161 | }
162 |
163 | fn processInput(window: glfw.Window) void {
164 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
165 | _ = glfw.Window.setShouldClose(window, true);
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/getting_started/shaders/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "shaders/";
10 |
11 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "shaders",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/shaders/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/shaders/image.png
--------------------------------------------------------------------------------
/src/getting_started/shaders/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const glfw = @import("glfw");
3 | const gl = @import("gl");
4 | const Shader = @import("Shader");
5 |
6 | const WindowSize = struct {
7 | pub const width: u32 = 800;
8 | pub const height: u32 = 600;
9 | };
10 |
11 | pub fn main() !void {
12 |
13 | // glfw: initialize and configure
14 | // ------------------------------
15 | if (!glfw.init(.{})) {
16 | std.log.err("GLFW initialization failed", .{});
17 | return;
18 | }
19 | defer glfw.terminate();
20 |
21 | // glfw window creation
22 | // --------------------
23 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
24 | .opengl_profile = .opengl_core_profile,
25 | .context_version_major = 4,
26 | .context_version_minor = 1,
27 | }) orelse {
28 | std.log.err("GLFW Window creation failed", .{});
29 | return;
30 | };
31 | defer window.destroy();
32 |
33 | glfw.makeContextCurrent(window);
34 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
35 |
36 | // Load all OpenGL function pointers
37 | // ---------------------------------------
38 | const proc: glfw.GLProc = undefined;
39 | try gl.load(proc, glGetProcAddress);
40 |
41 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
42 | var allocator = gpa.allocator();
43 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
44 | defer arena_allocator_state.deinit();
45 | const arena_allocator = arena_allocator_state.allocator();
46 |
47 | // create shader program
48 | var shader_program: Shader = Shader.create(arena_allocator, "shaders/shader_ex3.vs", "shaders/shader_ex3.fs");
49 |
50 | // set up vertex data (and buffer(s)) and configure vertex attributes
51 | // ------------------------------------------------------------------
52 | const vertices = [_]f32{
53 | -0.5, -0.5, 0.0, 1.0, 0.0, 0.0,
54 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0,
55 | 0.0, 0.5, 0.0, 0.0, 0.0, 1.0,
56 | };
57 |
58 | var VBO: c_uint = undefined;
59 | var VAO: c_uint = undefined;
60 |
61 | gl.genVertexArrays(1, &VAO);
62 | defer gl.deleteVertexArrays(1, &VAO);
63 |
64 | gl.genBuffers(1, &VBO);
65 | defer gl.deleteBuffers(1, &VBO);
66 |
67 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
68 | gl.bindVertexArray(VAO);
69 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
70 | // Fill our buffer with the vertex data
71 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, gl.STATIC_DRAW);
72 |
73 | // Specify and link our vertext attribute description
74 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 6 * @sizeOf(f32), null);
75 | gl.enableVertexAttribArray(0);
76 |
77 | const offset: [*c]c_uint = (3 * @sizeOf(f32));
78 | gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 6 * @sizeOf(f32), offset);
79 | gl.enableVertexAttribArray(1);
80 |
81 | while (!window.shouldClose()) {
82 | processInput(window);
83 |
84 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
85 | gl.clear(gl.COLOR_BUFFER_BIT);
86 |
87 | // update the uniform color
88 | const timeValue = glfw.getTime();
89 | const offsetValue = @floatCast(f32, @sin(timeValue * 2) / 2.0 + 0.5);
90 |
91 | shader_program.setVec3f("offset", [3]f32{ offsetValue, offsetValue, offsetValue });
92 | shader_program.use();
93 |
94 | gl.bindVertexArray(VAO);
95 | gl.drawArrays(gl.TRIANGLES, 0, 3);
96 |
97 | window.swapBuffers();
98 | glfw.pollEvents();
99 | }
100 | }
101 |
102 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
103 | _ = p;
104 | return glfw.getProcAddress(proc);
105 | }
106 |
107 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
108 | _ = window;
109 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
110 | }
111 |
112 | fn processInput(window: glfw.Window) void {
113 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
114 | _ = glfw.Window.setShouldClose(window, true);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/getting_started/shaders/shaders/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | out vec4 FragColor;
3 | in vec3 ourColor;
4 | void main()
5 | {
6 | FragColor = vec4(ourColor, 0.5);
7 | };
8 |
--------------------------------------------------------------------------------
/src/getting_started/shaders/shaders/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec3 aColor;
4 |
5 | uniform vec3 offset;
6 |
7 | out vec3 ourColor;
8 |
9 | void main()
10 | {
11 | gl_Position = vec4(aPos + offset, 1.0f);
12 | ourColor = aColor;
13 | }
--------------------------------------------------------------------------------
/src/getting_started/shaders/shaders/shader_ex3.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | out vec4 FragColor;
3 | in vec3 ourColor;
4 | in vec3 vertPos;
5 | void main()
6 | {
7 | FragColor = vec4(vertPos, 1.0);
8 | };
9 |
--------------------------------------------------------------------------------
/src/getting_started/shaders/shaders/shader_ex3.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec3 aColor;
4 |
5 | uniform vec3 offset;
6 |
7 | out vec3 ourColor;
8 | out vec3 vertPos;
9 |
10 | void main()
11 | {
12 | gl_Position = vec4(aPos * offset, 1.0f);
13 | ourColor = aColor;
14 | vertPos = aPos + offset;
15 | }
--------------------------------------------------------------------------------
/src/getting_started/simple_camera/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "content/";
10 |
11 | pub fn build(b: *std.build.Builder, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "simple_camera",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/simple_camera/content/awesomeface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/simple_camera/content/awesomeface.png
--------------------------------------------------------------------------------
/src/getting_started/simple_camera/content/container.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/simple_camera/content/container.jpg
--------------------------------------------------------------------------------
/src/getting_started/simple_camera/content/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 | in vec2 texCoord;
5 |
6 | uniform sampler2D texture1;
7 | uniform sampler2D texture2;
8 |
9 | void main()
10 | {
11 | FragColor = mix(texture(texture1, texCoord),texture(texture2, texCoord), 0.5f);
12 | };
13 |
--------------------------------------------------------------------------------
/src/getting_started/simple_camera/content/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec2 aTexCoord;
4 |
5 | out vec2 texCoord;
6 |
7 | uniform mat4 model;
8 | uniform mat4 view;
9 | uniform mat4 projection;
10 |
11 | void main()
12 | {
13 | gl_Position = projection * view * model * vec4(aPos, 1.0f);
14 | texCoord = aTexCoord;
15 | }
--------------------------------------------------------------------------------
/src/getting_started/simple_camera/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = std.math;
3 | const glfw = @import("glfw");
4 | const zstbi = @import("zstbi");
5 | const zm = @import("zmath");
6 | const gl = @import("gl");
7 | const Shader = @import("Shader");
8 | const Camera = @import("Camera");
9 | const common = @import("common");
10 |
11 | // Camera
12 | const camera_pos = zm.loadArr3(.{ 0.0, 0.0, 5.0 });
13 | var lastX: f64 = 0.0;
14 | var lastY: f64 = 0.0;
15 | var first_mouse = true;
16 | var camera = Camera.camera(camera_pos);
17 |
18 | // Timing
19 | var delta_time: f32 = 0.0;
20 | var last_frame: f32 = 0.0;
21 |
22 | const WindowSize = struct {
23 | pub const width: u32 = 800;
24 | pub const height: u32 = 600;
25 | };
26 |
27 | pub fn main() !void {
28 |
29 | // glfw: initialize and configure
30 | // ------------------------------
31 | if (!glfw.init(.{})) {
32 | std.log.err("GLFW initialization failed", .{});
33 | return;
34 | }
35 | defer glfw.terminate();
36 |
37 | // glfw window creation
38 | // --------------------
39 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
40 | .opengl_profile = .opengl_core_profile,
41 | .context_version_major = 4,
42 | .context_version_minor = 1,
43 | }) orelse {
44 | std.log.err("GLFW Window creation failed", .{});
45 | return;
46 | };
47 | defer window.destroy();
48 |
49 | glfw.makeContextCurrent(window);
50 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
51 | // Capture mouse, disable cursor visibility
52 | glfw.Window.setInputMode(window, glfw.Window.InputMode.cursor, glfw.Window.InputModeCursor.disabled);
53 | glfw.Window.setCursorPosCallback(window, mouseCallback);
54 | glfw.Window.setScrollCallback(window, mouseScrollCallback);
55 |
56 | // Load all OpenGL function pointers
57 | // ---------------------------------------
58 | const proc: glfw.GLProc = undefined;
59 | try gl.load(proc, glGetProcAddress);
60 |
61 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
62 | var allocator = gpa.allocator();
63 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
64 | defer arena_allocator_state.deinit();
65 | const arena = arena_allocator_state.allocator();
66 |
67 | // create shader program
68 | var shader_program: Shader = Shader.create(arena, "content/shader.vs", "content/shader.fs");
69 |
70 | // set up vertex data (and buffer(s)) and configure vertex attributes
71 | // ------------------------------------------------------------------
72 |
73 | const vertices_2D = [_]f32{
74 | // positions // colors // texture coords
75 | 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
76 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
77 | -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
78 | -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
79 | };
80 |
81 | _ = vertices_2D;
82 |
83 | const vertices_3D = [_]f32{ -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0 };
84 |
85 | const cube_positions = [_][3]f32{ .{ 0.0, 0.0, 0.0 }, .{ 2.0, 5.0, -15.0 }, .{ -1.5, -2.2, -2.5 }, .{ -3.8, -2.0, -12.3 }, .{ 2.4, -0.4, -3.5 }, .{ -1.7, 3.0, -7.5 }, .{ 1.3, -2.0, -2.5 }, .{ 1.5, 2.0, -2.5 }, .{ 1.5, 0.2, -1.5 }, .{ -1.3, 1.0, -1.5 } };
86 |
87 | var VBO: c_uint = undefined;
88 | var VAO: c_uint = undefined;
89 |
90 | gl.genVertexArrays(1, &VAO);
91 | defer gl.deleteVertexArrays(1, &VAO);
92 |
93 | gl.genBuffers(1, &VBO);
94 | defer gl.deleteBuffers(1, &VBO);
95 |
96 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
97 | gl.bindVertexArray(VAO);
98 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
99 | // Fill our buffer with the vertex data
100 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices_3D.len, &vertices_3D, gl.STATIC_DRAW);
101 |
102 | // vertex
103 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 5 * @sizeOf(f32), null);
104 | gl.enableVertexAttribArray(0);
105 |
106 | // texture coords
107 | const tex_offset: [*c]c_uint = (3 * @sizeOf(f32));
108 | gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 5 * @sizeOf(f32), tex_offset);
109 | gl.enableVertexAttribArray(1);
110 |
111 | // zstbi: loading an image.
112 | zstbi.init(allocator);
113 | defer zstbi.deinit();
114 |
115 | const image1_path = common.pathToContent(arena, "content/container.jpg") catch unreachable;
116 | var image1 = try zstbi.Image.init(&image1_path, 0);
117 | defer image1.deinit();
118 | std.debug.print("\nImage 1 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n", .{ image1.width, image1.height, image1.num_components });
119 |
120 | zstbi.setFlipVerticallyOnLoad(true);
121 | const image2_path = common.pathToContent(arena, "content/awesomeface.png") catch unreachable;
122 | var image2 = try zstbi.Image.init(&image2_path, 0);
123 | defer image2.deinit();
124 | std.debug.print("\nImage 2 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n", .{ image2.width, image2.height, image2.num_components });
125 |
126 | // Create and bind texture1 resource
127 | var texture1: c_uint = undefined;
128 |
129 | gl.genTextures(1, &texture1);
130 | gl.activeTexture(gl.TEXTURE0); // activate the texture unit first before binding texture
131 | gl.bindTexture(gl.TEXTURE_2D, texture1);
132 |
133 | // set the texture1 wrapping parameters
134 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
135 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
136 | // set texture1 filtering parameters
137 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
138 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
139 |
140 | // Generate the texture1
141 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image1.width), @intCast(c_int, image1.height), 0, gl.RGB, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image1.data));
142 | gl.generateMipmap(gl.TEXTURE_2D);
143 |
144 | // Texture2
145 | var texture2: c_uint = undefined;
146 |
147 | gl.genTextures(1, &texture2);
148 | gl.activeTexture(gl.TEXTURE1); // activate the texture unit first before binding texture
149 | gl.bindTexture(gl.TEXTURE_2D, texture2);
150 |
151 | // set the texture1 wrapping parameters
152 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
153 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
154 | // set texture1 filtering parameters
155 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
156 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
157 |
158 | // Generate the texture1
159 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image2.width), @intCast(c_int, image2.height), 0, gl.RGBA, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image2.data));
160 | gl.generateMipmap(gl.TEXTURE_2D);
161 |
162 | // Enable OpenGL depth testing (use Z-buffer information)
163 | gl.enable(gl.DEPTH_TEST);
164 |
165 | shader_program.use();
166 | shader_program.setInt("texture1", 0);
167 | shader_program.setInt("texture2", 1);
168 |
169 | // Buffer to store Model matrix
170 | var model: [16]f32 = undefined;
171 |
172 | // View matrix
173 | var view: [16]f32 = undefined;
174 |
175 | // Buffer to store Orojection matrix (in render loop)
176 | var proj: [16]f32 = undefined;
177 |
178 | while (!window.shouldClose()) {
179 |
180 | // Time per frame
181 | const current_frame = @floatCast(f32, glfw.getTime());
182 | delta_time = current_frame - last_frame;
183 | last_frame = current_frame;
184 |
185 | processInput(window);
186 |
187 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
188 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
189 | gl.activeTexture(gl.TEXTURE0);
190 | gl.bindTexture(gl.TEXTURE_2D, texture1);
191 | gl.activeTexture(gl.TEXTURE1);
192 | gl.bindTexture(gl.TEXTURE_2D, texture2);
193 | gl.bindVertexArray(VAO);
194 |
195 | // Projection matrix
196 | const projM = x: {
197 | const window_size = window.getSize();
198 | const aspect = @intToFloat(f32, window_size.width) / @intToFloat(f32, window_size.height);
199 | var projM = zm.perspectiveFovRhGl(camera.zoom * common.RAD_CONVERSION, aspect, 0.1, 100.0);
200 | break :x projM;
201 | };
202 | zm.storeMat(&proj, projM);
203 | shader_program.setMat4f("projection", proj);
204 |
205 | // View matrix: Camera
206 | const viewM = camera.getViewMatrix();
207 | zm.storeMat(&view, viewM);
208 | shader_program.setMat4f("view", view);
209 |
210 | for (cube_positions, 0..) |cube_position, i| {
211 | // Model matrix
212 | const cube_trans = zm.translation(cube_position[0], cube_position[1], cube_position[2]);
213 | const rotation_direction = (((@mod(@intToFloat(f32, i + 1), 2.0)) * 2.0) - 1.0);
214 | const cube_rot = zm.matFromAxisAngle(zm.f32x4(1.0, 0.3, 0.5, 1.0), @floatCast(f32, glfw.getTime()) * 55.0 * rotation_direction * common.RAD_CONVERSION);
215 | const modelM = zm.mul(cube_rot, cube_trans);
216 | zm.storeMat(&model, modelM);
217 | shader_program.setMat4f("model", model);
218 |
219 | gl.drawArrays(gl.TRIANGLES, 0, 36);
220 | }
221 |
222 | window.swapBuffers();
223 | glfw.pollEvents();
224 | }
225 | }
226 |
227 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
228 | _ = p;
229 | return glfw.getProcAddress(proc);
230 | }
231 |
232 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
233 | _ = window;
234 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
235 | }
236 |
237 | fn processInput(window: glfw.Window) void {
238 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
239 | _ = glfw.Window.setShouldClose(window, true);
240 | }
241 |
242 | if (glfw.Window.getKey(window, glfw.Key.w) == glfw.Action.press) {
243 | camera.processKeyboard(Camera.CameraMovement.FORWARD, delta_time);
244 | }
245 | if (glfw.Window.getKey(window, glfw.Key.s) == glfw.Action.press) {
246 | camera.processKeyboard(Camera.CameraMovement.BACKWARD, delta_time);
247 | }
248 | if (glfw.Window.getKey(window, glfw.Key.a) == glfw.Action.press) {
249 | camera.processKeyboard(Camera.CameraMovement.LEFT, delta_time);
250 | }
251 | if (glfw.Window.getKey(window, glfw.Key.d) == glfw.Action.press) {
252 | camera.processKeyboard(Camera.CameraMovement.RIGHT, delta_time);
253 | }
254 | }
255 |
256 | fn mouseCallback(window: glfw.Window, xpos: f64, ypos: f64) void {
257 | _ = window;
258 |
259 | if (first_mouse) {
260 | lastX = xpos;
261 | lastY = ypos;
262 | first_mouse = false;
263 | }
264 |
265 | var xoffset = xpos - lastX;
266 | var yoffset = ypos - lastY;
267 |
268 | lastX = xpos;
269 | lastY = ypos;
270 |
271 | camera.processMouseMovement(xoffset, yoffset, true);
272 | }
273 |
274 | fn mouseScrollCallback(window: glfw.Window, xoffset: f64, yoffset: f64) void {
275 | _ = window;
276 | _ = xoffset;
277 |
278 | camera.processMouseScroll(yoffset);
279 | }
280 |
--------------------------------------------------------------------------------
/src/getting_started/textures/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "content/";
10 |
11 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "textures",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/textures/content/awesomeface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/textures/content/awesomeface.png
--------------------------------------------------------------------------------
/src/getting_started/textures/content/container.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/textures/content/container.jpg
--------------------------------------------------------------------------------
/src/getting_started/textures/content/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 | in vec3 ourColor;
5 | in vec2 texCoord;
6 |
7 | uniform sampler2D texture1;
8 | uniform sampler2D texture2;
9 |
10 | void main()
11 | {
12 | FragColor = mix(texture(texture1, texCoord),texture(texture2, texCoord), 0.5f) * vec4(ourColor, 1.0f);
13 | };
14 |
--------------------------------------------------------------------------------
/src/getting_started/textures/content/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec3 aColor;
4 | layout (location = 2) in vec2 aTexCoord;
5 |
6 | out vec3 ourColor;
7 | out vec2 texCoord;
8 |
9 | void main()
10 | {
11 | gl_Position = vec4(aPos, 1.0f);
12 | ourColor = aColor;
13 | texCoord = aTexCoord;
14 | }
--------------------------------------------------------------------------------
/src/getting_started/textures/content/wall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/textures/content/wall.jpg
--------------------------------------------------------------------------------
/src/getting_started/textures/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/textures/image.png
--------------------------------------------------------------------------------
/src/getting_started/textures/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const glfw = @import("glfw");
3 | const zstbi = @import("zstbi");
4 | const gl = @import("gl");
5 | const Shader = @import("Shader");
6 | const common = @import("common");
7 |
8 | const WindowSize = struct {
9 | pub const width: u32 = 800;
10 | pub const height: u32 = 600;
11 | };
12 |
13 | pub fn main() !void {
14 |
15 | // glfw: initialize and configure
16 | // ------------------------------
17 | if (!glfw.init(.{})) {
18 | std.log.err("GLFW initialization failed", .{});
19 | return;
20 | }
21 | defer glfw.terminate();
22 |
23 | // glfw window creation
24 | // --------------------
25 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
26 | .opengl_profile = .opengl_core_profile,
27 | .context_version_major = 4,
28 | .context_version_minor = 1,
29 | }) orelse {
30 | std.log.err("GLFW Window creation failed", .{});
31 | return;
32 | };
33 | defer window.destroy();
34 |
35 | glfw.makeContextCurrent(window);
36 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
37 |
38 | // Load all OpenGL function pointers
39 | // ---------------------------------------
40 | const proc: glfw.GLProc = undefined;
41 | try gl.load(proc, glGetProcAddress);
42 |
43 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
44 | var allocator = gpa.allocator();
45 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
46 | defer arena_allocator_state.deinit();
47 | const arena = arena_allocator_state.allocator();
48 |
49 | // create shader program
50 | var shader_program: Shader = Shader.create(
51 | arena,
52 | "content/shader.vs",
53 | "content/shader.fs",
54 | );
55 |
56 | // set up vertex data (and buffer(s)) and configure vertex attributes
57 | // ------------------------------------------------------------------
58 |
59 | const vertices = [_]f32{
60 | // positions // colors // texture coords
61 | 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
62 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
63 | -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
64 | -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
65 | };
66 |
67 | const indices = [_]c_uint{
68 | // note that we start from 0!
69 | 0, 1, 3, // first triangle
70 | 1, 2, 3, // second triangle
71 | };
72 |
73 | var VBO: c_uint = undefined;
74 | var VAO: c_uint = undefined;
75 | var EBO: c_uint = undefined;
76 |
77 | gl.genVertexArrays(1, &VAO);
78 | defer gl.deleteVertexArrays(1, &VAO);
79 |
80 | gl.genBuffers(1, &VBO);
81 | defer gl.deleteBuffers(1, &VBO);
82 |
83 | gl.genBuffers(1, &EBO);
84 | defer gl.deleteBuffers(1, &EBO);
85 |
86 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
87 | gl.bindVertexArray(VAO);
88 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
89 | // Fill our buffer with the vertex data
90 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, gl.STATIC_DRAW);
91 |
92 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, EBO);
93 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 6 * @sizeOf(c_uint), &indices, gl.STATIC_DRAW);
94 |
95 | // vertex
96 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), null);
97 | gl.enableVertexAttribArray(0);
98 |
99 | // colors
100 | const col_offset: [*c]c_uint = (3 * @sizeOf(f32));
101 | gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), col_offset);
102 | gl.enableVertexAttribArray(1);
103 |
104 | // texture coords
105 | const tex_offset: [*c]c_uint = (6 * @sizeOf(f32));
106 | gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), tex_offset);
107 | gl.enableVertexAttribArray(2);
108 |
109 | // zstbi: loading an image.
110 | zstbi.init(allocator);
111 | defer zstbi.deinit();
112 |
113 | const image1_path = common.pathToContent(arena, "content/container.jpg") catch unreachable;
114 | var image1 = try zstbi.Image.init(&image1_path, 0);
115 | defer image1.deinit();
116 | std.debug.print(
117 | "\nImage 1 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n",
118 | .{ image1.width, image1.height, image1.num_components },
119 | );
120 |
121 | zstbi.setFlipVerticallyOnLoad(true);
122 | const image2_path = common.pathToContent(arena, "content/awesomeface.png") catch unreachable;
123 | var image2 = try zstbi.Image.init(&image2_path, 0);
124 | defer image2.deinit();
125 | std.debug.print(
126 | "\nImage 2 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n",
127 | .{ image2.width, image2.height, image2.num_components },
128 | );
129 |
130 | // Create and bind texture1 resource
131 | var texture1: c_uint = undefined;
132 |
133 | gl.genTextures(1, &texture1);
134 | gl.activeTexture(gl.TEXTURE0); // activate the texture unit first before binding texture
135 | gl.bindTexture(gl.TEXTURE_2D, texture1);
136 |
137 | // set the texture1 wrapping parameters
138 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
139 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
140 | // set texture1 filtering parameters
141 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
142 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
143 |
144 | // Generate the texture1
145 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image1.width), @intCast(c_int, image1.height), 0, gl.RGB, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image1.data));
146 | gl.generateMipmap(gl.TEXTURE_2D);
147 |
148 | // Texture2
149 | var texture2: c_uint = undefined;
150 |
151 | gl.genTextures(1, &texture2);
152 | gl.activeTexture(gl.TEXTURE1); // activate the texture unit first before binding texture
153 | gl.bindTexture(gl.TEXTURE_2D, texture2);
154 |
155 | // set the texture1 wrapping parameters
156 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
157 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
158 | // set texture1 filtering parameters
159 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
160 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
161 |
162 | // Generate the texture1
163 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image2.width), @intCast(c_int, image2.height), 0, gl.RGBA, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image2.data));
164 | gl.generateMipmap(gl.TEXTURE_2D);
165 |
166 | shader_program.use();
167 | shader_program.setInt("texture1", 0);
168 | shader_program.setInt("texture2", 1);
169 |
170 | while (!window.shouldClose()) {
171 | processInput(window);
172 |
173 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
174 | gl.clear(gl.COLOR_BUFFER_BIT);
175 | gl.activeTexture(gl.TEXTURE0);
176 | gl.bindTexture(gl.TEXTURE_2D, texture1);
177 | gl.activeTexture(gl.TEXTURE1);
178 | gl.bindTexture(gl.TEXTURE_2D, texture2);
179 | gl.bindVertexArray(VAO);
180 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null);
181 |
182 | window.swapBuffers();
183 | glfw.pollEvents();
184 | }
185 | }
186 |
187 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
188 | _ = p;
189 | return glfw.getProcAddress(proc);
190 | }
191 |
192 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
193 | _ = window;
194 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
195 | }
196 |
197 | fn processInput(window: glfw.Window) void {
198 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
199 | _ = glfw.Window.setShouldClose(window, true);
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/getting_started/transformations/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const Options = @import("../../../build.zig").Options;
4 |
5 | inline fn thisDir() []const u8 {
6 | return comptime std.fs.path.dirname(@src().file) orelse ".";
7 | }
8 |
9 | const content_dir = "content/";
10 |
11 | pub fn build(b: *std.Build, options: Options) *std.build.CompileStep {
12 | const exe = b.addExecutable(.{
13 | .name = "transformations",
14 | .root_source_file = .{ .path = thisDir() ++ "/main.zig" },
15 | .target = options.target,
16 | .optimize = options.build_mode,
17 | });
18 |
19 | const install_content_step = b.addInstallDirectory(.{
20 | .source_dir = thisDir() ++ "/" ++ content_dir,
21 | .install_dir = .{ .custom = "" },
22 | .install_subdir = "bin/" ++ content_dir,
23 | });
24 | exe.step.dependOn(&install_content_step.step);
25 |
26 | return exe;
27 | }
28 |
--------------------------------------------------------------------------------
/src/getting_started/transformations/content/awesomeface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/transformations/content/awesomeface.png
--------------------------------------------------------------------------------
/src/getting_started/transformations/content/container.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/transformations/content/container.jpg
--------------------------------------------------------------------------------
/src/getting_started/transformations/content/shader.fs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 | in vec3 ourColor;
5 | in vec2 texCoord;
6 |
7 | uniform sampler2D texture1;
8 | uniform sampler2D texture2;
9 |
10 | void main()
11 | {
12 | FragColor = mix(texture(texture1, texCoord),texture(texture2, texCoord), 0.5f) * vec4(ourColor, 1.0f);
13 | };
14 |
--------------------------------------------------------------------------------
/src/getting_started/transformations/content/shader.vs:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec3 aColor;
4 | layout (location = 2) in vec2 aTexCoord;
5 |
6 | out vec3 ourColor;
7 | out vec2 texCoord;
8 |
9 | uniform mat4 transform;
10 |
11 | void main()
12 | {
13 | gl_Position = transform * vec4(aPos, 1.0f);
14 | ourColor = aColor;
15 | texCoord = aTexCoord;
16 | }
--------------------------------------------------------------------------------
/src/getting_started/transformations/content/wall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/transformations/content/wall.jpg
--------------------------------------------------------------------------------
/src/getting_started/transformations/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftlinks/zig_learn_opengl/7208f4402c44d9cb697705218b2bbbc82a8ccdd2/src/getting_started/transformations/image.png
--------------------------------------------------------------------------------
/src/getting_started/transformations/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = std.math;
3 | const glfw = @import("glfw");
4 | const zstbi = @import("zstbi");
5 | const zm = @import("zmath");
6 | const gl = @import("gl");
7 | const Shader = @import("Shader");
8 | const common = @import("common");
9 |
10 | const WindowSize = struct {
11 | pub const width: u32 = 800;
12 | pub const height: u32 = 600;
13 | };
14 |
15 | pub fn main() !void {
16 |
17 | // glfw: initialize and configure
18 | // ------------------------------
19 | if (!glfw.init(.{})) {
20 | std.log.err("GLFW initialization failed", .{});
21 | return;
22 | }
23 | defer glfw.terminate();
24 |
25 | // glfw window creation
26 | // --------------------
27 | const window = glfw.Window.create(WindowSize.width, WindowSize.height, "mach-glfw + zig-opengl", null, null, .{
28 | .opengl_profile = .opengl_core_profile,
29 | .context_version_major = 4,
30 | .context_version_minor = 1,
31 | }) orelse {
32 | std.log.err("GLFW Window creation failed", .{});
33 | return;
34 | };
35 | defer window.destroy();
36 |
37 | glfw.makeContextCurrent(window);
38 | glfw.Window.setFramebufferSizeCallback(window, framebuffer_size_callback);
39 |
40 | // Load all OpenGL function pointers
41 | // ---------------------------------------
42 | const proc: glfw.GLProc = undefined;
43 | try gl.load(proc, glGetProcAddress);
44 |
45 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
46 | var allocator = gpa.allocator();
47 | var arena_allocator_state = std.heap.ArenaAllocator.init(allocator);
48 | defer arena_allocator_state.deinit();
49 | const arena = arena_allocator_state.allocator();
50 |
51 | // create shader program
52 | var shader_program: Shader = Shader.create(arena, "content/shader.vs", "content/shader.fs");
53 |
54 | // set up vertex data (and buffer(s)) and configure vertex attributes
55 | // ------------------------------------------------------------------
56 |
57 | const vertices = [_]f32{
58 | // positions // colors // texture coords
59 | 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
60 | 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
61 | -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
62 | -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
63 | };
64 |
65 | const indices = [_]c_uint{
66 | // note that we start from 0!
67 | 0, 1, 3, // first triangle
68 | 1, 2, 3, // second triangle
69 | };
70 |
71 | var VBO: c_uint = undefined;
72 | var VAO: c_uint = undefined;
73 | var EBO: c_uint = undefined;
74 |
75 | gl.genVertexArrays(1, &VAO);
76 | defer gl.deleteVertexArrays(1, &VAO);
77 |
78 | gl.genBuffers(1, &VBO);
79 | defer gl.deleteBuffers(1, &VBO);
80 |
81 | gl.genBuffers(1, &EBO);
82 | defer gl.deleteBuffers(1, &EBO);
83 |
84 | // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
85 | gl.bindVertexArray(VAO);
86 | gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
87 | // Fill our buffer with the vertex data
88 | gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, gl.STATIC_DRAW);
89 |
90 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, EBO);
91 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 6 * @sizeOf(c_uint), &indices, gl.STATIC_DRAW);
92 |
93 | // vertex
94 | gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), null);
95 | gl.enableVertexAttribArray(0);
96 |
97 | // colors
98 | const col_offset: [*c]c_uint = (3 * @sizeOf(f32));
99 | gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), col_offset);
100 | gl.enableVertexAttribArray(1);
101 |
102 | // texture coords
103 | const tex_offset: [*c]c_uint = (6 * @sizeOf(f32));
104 | gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), tex_offset);
105 | gl.enableVertexAttribArray(2);
106 |
107 | // zstbi: loading an image.
108 | zstbi.init(allocator);
109 | defer zstbi.deinit();
110 |
111 | const image1_path = common.pathToContent(arena, "content/container.jpg") catch unreachable;
112 | var image1 = try zstbi.Image.init(&image1_path, 0);
113 | defer image1.deinit();
114 | std.debug.print("\nImage 1 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n", .{ image1.width, image1.height, image1.num_components });
115 |
116 | zstbi.setFlipVerticallyOnLoad(true);
117 | const image2_path = common.pathToContent(arena, "content/awesomeface.png") catch unreachable;
118 | var image2 = try zstbi.Image.init(&image2_path, 0);
119 | defer image2.deinit();
120 | std.debug.print("\nImage 2 info:\n\n img width: {any}\n img height: {any}\n nchannels: {any}\n", .{ image2.width, image2.height, image2.num_components });
121 |
122 | // Create and bind texture1 resource
123 | var texture1: c_uint = undefined;
124 |
125 | gl.genTextures(1, &texture1);
126 | gl.activeTexture(gl.TEXTURE0); // activate the texture unit first before binding texture
127 | gl.bindTexture(gl.TEXTURE_2D, texture1);
128 |
129 | // set the texture1 wrapping parameters
130 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
131 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
132 | // set texture1 filtering parameters
133 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
134 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
135 |
136 | // Generate the texture1
137 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image1.width), @intCast(c_int, image1.height), 0, gl.RGB, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image1.data));
138 | gl.generateMipmap(gl.TEXTURE_2D);
139 |
140 | // Texture2
141 | var texture2: c_uint = undefined;
142 |
143 | gl.genTextures(1, &texture2);
144 | gl.activeTexture(gl.TEXTURE1); // activate the texture unit first before binding texture
145 | gl.bindTexture(gl.TEXTURE_2D, texture2);
146 |
147 | // set the texture1 wrapping parameters
148 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
149 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
150 | // set texture1 filtering parameters
151 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
152 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
153 |
154 | // Generate the texture1
155 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, @intCast(c_int, image2.width), @intCast(c_int, image2.height), 0, gl.RGBA, gl.UNSIGNED_BYTE, @ptrCast([*c]const u8, image2.data));
156 | gl.generateMipmap(gl.TEXTURE_2D);
157 |
158 | shader_program.use();
159 | shader_program.setInt("texture1", 0);
160 | shader_program.setInt("texture2", 1);
161 |
162 | while (!window.shouldClose()) {
163 | processInput(window);
164 |
165 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
166 | gl.clear(gl.COLOR_BUFFER_BIT);
167 | gl.activeTexture(gl.TEXTURE0);
168 | gl.bindTexture(gl.TEXTURE_2D, texture1);
169 | gl.activeTexture(gl.TEXTURE1);
170 | gl.bindTexture(gl.TEXTURE_2D, texture2);
171 | gl.bindVertexArray(VAO);
172 |
173 | // Construction of the tranformation matrix
174 | const rotZ = zm.rotationZ(@floatCast(f32, glfw.getTime()));
175 | const scale = zm.scaling(0.5, 0.5, 0.5);
176 | const transformM = zm.mul(rotZ, scale);
177 | var transform: [16]f32 = undefined;
178 | zm.storeMat(&transform, transformM);
179 |
180 | // Sending our transformation matrix to our vertex shader
181 | const transformLoc = gl.getUniformLocation(shader_program.ID, "transform");
182 | gl.uniformMatrix4fv(transformLoc, 1, gl.FALSE, &transform);
183 |
184 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null);
185 |
186 | window.swapBuffers();
187 | glfw.pollEvents();
188 | }
189 | }
190 |
191 | fn glGetProcAddress(p: glfw.GLProc, proc: [:0]const u8) ?gl.FunctionPointer {
192 | _ = p;
193 | return glfw.getProcAddress(proc);
194 | }
195 |
196 | fn framebuffer_size_callback(window: glfw.Window, width: u32, height: u32) void {
197 | _ = window;
198 | gl.viewport(0, 0, @intCast(c_int, width), @intCast(c_int, height));
199 | }
200 |
201 | fn processInput(window: glfw.Window) void {
202 | if (glfw.Window.getKey(window, glfw.Key.escape) == glfw.Action.press) {
203 | _ = glfw.Window.setShouldClose(window, true);
204 | }
205 | }
206 |
--------------------------------------------------------------------------------