initial GL stuff

stable
Ivory 2024-08-21 15:57:18 -04:00
commit 7146de6c50
20 changed files with 8802 additions and 0 deletions

2
.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
zig-out
.zig-cache

115
build.zig 100644
View File

@ -0,0 +1,115 @@
const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
// const GL_TYPE = enum([]const u8) {
// COMPAT = "compat",
// CORE = "core",
// };
//
// const GL_VERSION = enum([]const u8) {
// GL33 = "3.3",
// GL46 = "4.6",
// };
const glad_folder = "glad-4.6-compat";
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
// const lib = b.addStaticLibrary(.{
// .name = "test-game",
// // In this case the main source file is merely a path, however, in more
// // complicated build scripts, this could be a generated file.
// .root_source_file = b.path("src/root.zig"),
// .target = target,
// .optimize = optimize,
// });
//
// // This declares intent for the library to be installed into the standard
// // location when the user invokes the "install" step (the default step when
// // running `zig build`).
// b.installArtifact(lib);
const exe = b.addExecutable(.{
.name = "test-game",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.addCSourceFile(.{
.file = b.path(glad_folder ++ "/src/glad.c")
});
exe.addIncludePath(b.path(glad_folder ++ "/include"));
exe.linkLibC();
exe.linkSystemLibrary("glfw");
exe.linkSystemLibrary("GL");
exe.linkSystemLibrary("z");
exe.linkSystemLibrary("spng");
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
b.installArtifact(exe);
// This *creates* a Run step in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = b.addRunArtifact(exe);
// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep());
// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args);
}
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// // Creates a step for unit testing. This only builds the test executable
// // but does not run it.
// const lib_unit_tests = b.addTest(.{
// .root_source_file = b.path("src/root.zig"),
// .target = target,
// .optimize = optimize,
// });
//
// const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
// const exe_unit_tests = b.addTest(.{
// .root_source_file = b.path("src/main.zig"),
// .target = target,
// .optimize = optimize,
// });
// const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
// const test_step = b.step("test", "Run unit tests");
// test_step.dependOn(&run_lib_unit_tests.step);
// test_step.dependOn(&run_exe_unit_tests.step);
}

72
build.zig.zon 100644
View File

@ -0,0 +1,72 @@
.{
// This is the default name used by packages depending on this one. For
// example, when a user runs `zig fetch --save <url>`, this field is used
// as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = "test-game",
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
// This field is optional.
// This is currently advisory only; Zig does not yet do anything
// with this value.
//.minimum_zig_version = "0.11.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
},
// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}

View File

@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

12
src/Layer.zig 100644
View File

@ -0,0 +1,12 @@
const Layer = @This();
z_index: i32,
fn layer(z_index: i32) Layer {
return .{
.z_index = z_index
};
}
pub const FLOOR = layer(0);
pub const ENTITIES = layer(1);

45
src/Sprite.zig 100644
View File

@ -0,0 +1,45 @@
const Rectf = @import("geometry/Rectf.zig");
const Recti = @import("geometry/Recti.zig");
const Vec2f = @import("geometry/Vec2f.zig");
const Texture = @import("Texture.zig");
const Layer = @import("Layer.zig");
const shaders = @import("shaders.zig");
const c = @cImport({
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
const Sprite = @This();
texture_area: Rectf = undefined,
texture: *const Texture = undefined,
pub fn create(tex: *const Texture, rect: Recti) Sprite {
return .{
.texture_area = rect.scalef(Vec2f.new(
@floatFromInt(tex.width),
@floatFromInt(tex.height)
)),
.texture = tex,
};
}
pub fn draw(self: *const Sprite, screen_pos: *const Recti, layer: Layer) void {
self.texture.bind(c.GL_TEXTURE0);
c.glBegin(c.GL_QUADS);
{
c.glVertexAttrib2f(shaders.TEXCOORD_ATTRIBUTE_ID, self.texture_area.a.x, self.texture_area.a.y);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, 1, 1, 1, 1);
c.glVertex3i(screen_pos.a.x, screen_pos.a.y, layer.z_index);
c.glVertexAttrib2f(shaders.TEXCOORD_ATTRIBUTE_ID, self.texture_area.b.x, self.texture_area.a.y);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, 1, 1, 1, 1);
c.glVertex3i(screen_pos.b.x, screen_pos.a.y, layer.z_index);
c.glVertexAttrib2f(shaders.TEXCOORD_ATTRIBUTE_ID, self.texture_area.b.x, self.texture_area.b.y);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, 1, 1, 1, 1);
c.glVertex3i(screen_pos.b.x, screen_pos.b.y, layer.z_index);
c.glVertexAttrib2f(shaders.TEXCOORD_ATTRIBUTE_ID, self.texture_area.a.x, self.texture_area.b.y);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, 1, 1, 1, 1);
c.glVertex3i(screen_pos.a.x, screen_pos.b.y, layer.z_index);
}
c.glEnd();
}

71
src/Texture.zig 100644
View File

@ -0,0 +1,71 @@
const c = @cImport({
// @cInclude("png.h");
@cInclude("spng.h");
// @cInclude("zlib.h");
// @cInclude("stdio.h");
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
const std = @import("std");
const File = std.fs.File;
const Dir = std.fs.Dir;
const heap = std.heap.page_allocator;
const Texture = @This();
const Error = error {
Fuck
};
handle: u32,
width: u32,
height: u32,
pub fn bind(self: *const Texture, unit: u32) void {
c.glActiveTexture(unit);
c.glBindTexture(c.GL_TEXTURE_2D, self.handle);
}
pub fn create(path: []const u8) !Texture {
var file = try std.fs.cwd().openFile(path, .{});
defer file.close();
const file_size = (try file.stat()).size;
const buffer: []u8 = try heap.alloc(u8, file_size);
defer heap.free(buffer);
const reader = file.reader();
_ = try reader.readAll(buffer);
const context = c.spng_ctx_new(0);
defer c.spng_ctx_free(context);
_ = c.spng_set_png_buffer(context, @ptrCast(buffer), buffer.len);
var out_size: usize = undefined;
_ = c.spng_decoded_image_size(context, c.SPNG_FMT_RGBA8, &out_size);
const out_buf: []u8 = try heap.alloc(u8, out_size);
defer heap.free(out_buf);
_ = c.spng_decode_image(context, @ptrCast(out_buf), out_size, c.SPNG_FMT_RGBA8, 0);
var ihdr = c.struct_spng_ihdr {};
_ = c.spng_get_ihdr(context, &ihdr);
var tex_id: u32 = undefined;
c.glGenTextures(1, &tex_id);
c.glBindTexture(c.GL_TEXTURE_2D, tex_id);
c.glActiveTexture(c.GL_TEXTURE0);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST);
c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_RGBA, @intCast(ihdr.width), @intCast(ihdr.height), 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, @ptrCast(out_buf));
c.glBindTexture(c.GL_TEXTURE_2D, 0);
std.debug.print("texture generated: ID={} Path={s}\n", .{ tex_id, path });
return .{
.handle = @intCast(tex_id),
.width = ihdr.width,
.height = ihdr.height,
};
}

114
src/engine.zig 100644
View File

@ -0,0 +1,114 @@
const std = @import("std");
const Recti = @import("geometry/Recti.zig");
const Matrix4f = @import("geometry/Matrix4f.zig");
const shaders = @import("shaders.zig");
const Texture = @import("Texture.zig");
const Sprite = @import("Sprite.zig");
const Layer = @import("Layer.zig");
const c = @cImport({
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
const HashMap = std.HashMap;
// export const IGame = struct {
// render: fn () void,
// update: fn (f32) void,
// };
fn errorCallback(err: c_int, desc_c: [*c]const u8) callconv(.C) void {
const desc: *const u8 = @ptrCast(desc_c);
std.log.err("glfw error {x:0>8}: {s}", .{ err, desc });
}
// export fn run(game: *const IGame) !void {
pub fn run() !void {
// in case of errors, set callback early!
// for some reason this returns an error function as well??
_ = c.glfwSetErrorCallback(errorCallback);
// initialize glfw capabilities & cleanup
if (c.glfwInit() != 1) {
std.log.err("Failed to init glfw", .{});
return error.Initialization;
}
defer c.glfwTerminate();
// ensure glfw knows what we're about
// c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3);
c.glfwDefaultWindowHints();
c.glfwWindowHint(c.GLFW_SAMPLES, 4);
// c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 3);
// c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE);
// create the window & cleanup
const window = c.glfwCreateWindow(640, 480, "My Title", null, null) orelse {
std.log.err("Failed to open window", .{});
return error.Initialization;
};
defer c.glfwDestroyWindow(window);
// load opengl into the window
c.glfwMakeContextCurrent(window);
if (c.gladLoadGL() != 1) {
std.log.err("Failed to load GL context", .{});
return error.Initialization;
}
// configure the context!
c.glfwSwapInterval(1);
// compile shaders...
try shaders.load();
const texture = try Texture.create("textures.png");
// texture.bind(c.GL_TEXTURE0);
const sprite = Sprite.create(&texture, Recti.from_xywh(8*27, 8*4, 8, 16));
const clearBrightness: f32 = 0.09;
c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0);
// c.glEnable(c.GL_MULTISAMPLE);
c.glDisable(c.GL_MULTISAMPLE);
// c.glEnable(c.GL_BLEND);
// c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
// c.glEnable(c.GL_DEPTH_TEST);
// c.glDepthFunc(c.GL_LEQUAL);
// c.glDepthMask(c.GL_TRUE);
var width: c_int = undefined;
var height: c_int = undefined;
c.glfwGetFramebufferSize(window, @ptrCast(&width), @ptrCast(&height));
const projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100);
// run the main loop
while (c.glfwWindowShouldClose(window) == 0) {
shaders.set_projection_matrix(&projection);
// std.debug.print("size: {d} x {d}\n", .{width, height});
// c.glViewport(0, 0, width, height);
c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT);
// c.glBegin(c.GL_TRIANGLES);
//
// c.glVertex2f(0, 1);
// // c.glVertexAttrib4f(1, 1, 0, 0, 1);
// c.glVertex2f(-1, -1);
// // c.glVertexAttrib4f(1, 0, 1, 0, 1);
// c.glVertex2f(1, -1);
// // c.glVertexAttrib4f(1, 0, 0, 1, 1);
//
// c.glEnd();
sprite.draw(&Recti.from_xywh(100, 100, 32, 64), Layer.FLOOR);
// draw.rect(&geometry.Rect.from_xywh(100, 100, 256, 256), draw.Layer.FLOOR);
// game.render();
// game.update(1.0);
c.glfwSwapBuffers(window);
c.glfwPollEvents();
}
}

49
src/geometry.zig 100644
View File

@ -0,0 +1,49 @@
const Vec2i = @import("geometry/Vec2i.zig");
pub const Recti = struct {
x: i32,
y: i32,
w: i32,
h: i32,
a: Vec2i,
b: Vec2i,
pub fn from_xywh(x: i32, y: i32, w: i32, h: i32) Recti {
return .{
.x = x,
.y = y,
.w = w,
.h = h,
.a = Vec2i.new(x, y),
.b = Vec2i.new(x + w, y + h),
};
}
};
pub const Matrix4f = struct {
values: [16]f32 = identity,
const SIZE: i8 = 4 * 4;
const identity = from_values([16]f32 {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
});
pub fn from_values(values: [16]f32) Matrix4f {
return .{ .values = values };
}
pub fn orthographic(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) Matrix4f {
var ortho = Matrix4f.identity;
ortho.values[0 + 0 * 4] = 2.0 / (right - left);
ortho.values[1 + 1 * 4] = 2.0 / (top - bottom);
ortho.values[2 + 2 * 4] = 2.0 / (near - far);
ortho.values[0 + 3 * 4] = (left + right) / (left - right);
ortho.values[1 + 3 * 4] = (bottom + top) / (bottom - top);
ortho.values[2 + 3 * 4] = (far + near) / (far - near);
return ortho;
}
};

View File

@ -0,0 +1,27 @@
const Matrix4f = @This();
values: [16]f32 = identity,
const SIZE: i8 = 4 * 4;
const identity = from_values([16]f32 {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
});
pub fn from_values(values: [16]f32) Matrix4f {
return .{ .values = values };
}
pub fn orthographic(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) Matrix4f {
var ortho = Matrix4f.identity;
ortho.values[0 + 0 * 4] = 2.0 / (right - left);
ortho.values[1 + 1 * 4] = 2.0 / (top - bottom);
ortho.values[2 + 2 * 4] = 2.0 / (near - far);
ortho.values[0 + 3 * 4] = (left + right) / (left - right);
ortho.values[1 + 3 * 4] = (bottom + top) / (bottom - top);
ortho.values[2 + 3 * 4] = (far + near) / (far - near);
return ortho;
}

View File

@ -0,0 +1,32 @@
const Vec2f = @import("./Vec2f.zig");
const Recti = @import("./Recti.zig");
const Rectf = @This();
x: f32,
y: f32,
w: f32,
h: f32,
a: Vec2f,
b: Vec2f,
pub fn from_xywh(x: f32, y: f32, w: f32, h: f32) Rectf {
return .{
.x = x,
.y = y,
.w = w,
.h = h,
.a = Vec2f.new(x, y),
.b = Vec2f.new(x + w, y + h),
};
}
pub fn to_recti(self: *const Rectf) Recti {
return Recti.from_xywh(
@intCast(self.x),
@intCast(self.y),
@intCast(self.w),
@intCast(self.h),
);
}

View File

@ -0,0 +1,41 @@
const Vec2i = @import("./Vec2i.zig");
const Vec2f = @import("./Vec2f.zig");
const Rectf = @import("./Rectf.zig");
const Recti = @This();
x: i32,
y: i32,
w: i32,
h: i32,
a: Vec2i,
b: Vec2i,
pub fn from_xywh(x: i32, y: i32, w: i32, h: i32) Recti {
return .{
.x = x,
.y = y,
.w = w,
.h = h,
.a = Vec2i.new(x, y),
.b = Vec2i.new(x + w, y + h),
};
}
pub fn to_rectf(self: *const Recti) Rectf {
return Rectf.from_xywh(
@intCast(self.x),
@intCast(self.y),
@intCast(self.w),
@intCast(self.h),
);
}
pub fn scalef(self: *const Recti, scale: Vec2f) Rectf {
return Rectf.from_xywh(
@as(f32, @floatFromInt(self.x)) / scale.x,
@as(f32, @floatFromInt(self.y)) / scale.y,
@as(f32, @floatFromInt(self.w)) / scale.x,
@as(f32, @floatFromInt(self.h)) / scale.y,
);
}

View File

@ -0,0 +1,19 @@
const Vec2f = @This();
x: f32,
y: f32,
pub fn new(x: f32, y: f32) Vec2f {
return .{
.x = x,
.y = y,
};
}
pub const ZERO = new(0, 0);
pub const ONE = new(1, 1);
pub const NORTH = new(0, -1);
pub const SOUTH = new(0, 1);
pub const EAST = new(1, 0);
pub const WEST = new(-1, 0);

View File

@ -0,0 +1,18 @@
const Vec2i = @This();
x: i32,
y: i32,
pub fn new(x: i32, y: i32) Vec2i {
return .{
.x = x,
.y = y,
};
}
pub const ZERO = new(0, 0);
pub const ONE = new(1, 1);
pub const NORTH = new(0, -1);
pub const SOUTH = new(0, 1);
pub const EAST = new(1, 0);
pub const WEST = new(-1, 0);

7
src/main.zig 100644
View File

@ -0,0 +1,7 @@
const std = @import("std");
const engine = @import("engine.zig");
pub fn main() !void {
try engine.run();
}

6
src/root.zig 100644
View File

@ -0,0 +1,6 @@
const std = @import("std");
export fn add(a: i32, b: i32) i32 {
return a + b;
}

160
src/shaders.zig 100644
View File

@ -0,0 +1,160 @@
const std = @import("std");
const heap = std.heap.page_allocator;
const Matrix4f = @import("geometry/Matrix4f.zig");
const Textures = @import("Texture.zig");
const c = @cImport({
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
const Error = error {
NotInitialized,
AlreadyInitialized,
};
const VERT_SHADER_TEXT =
\\ #version 330
\\ uniform mat4 uProjection;
\\
\\ in vec3 Position;
\\ in vec4 Color;
\\ in vec2 TexCoord;
\\
\\ out vec4 vColor;
\\ out vec2 vTexCoord;
\\
\\ void main() {
\\ gl_Position = uProjection * vec4(Position, 1.0);
\\ vColor = Color;
\\ vTexCoord = TexCoord;
\\ }
;
const FRAG_SHADER_TEXT =
\\ #version 330
\\ uniform sampler2D uTexture0;
\\
\\ in vec4 vColor;
\\ in vec2 vTexCoord;
\\
\\ out vec4 color;
\\
\\ void main() {
\\ vec4 texColor = color = texture(uTexture0, vTexCoord);
\\ if(texColor == vec4(1, 0, 1, 1) || texColor == vec4(1, 0, 0, 1) || texColor.w == 0.0) {
\\ discard;
\\ return;
\\ }
\\ color = texColor * vColor;
\\ // color = vColor;
\\ // color = vec4(vTexCoord.x, vTexCoord.y, 0, 1);
\\ }
;
pub const POSITION_ATTRIBUTE_ID = 0;
pub const COLOR_ATTRIBUTE_ID = 1;
pub const TEXCOORD_ATTRIBUTE_ID = 2;
var flat: Shader = undefined;
var active_shader: *const Shader = undefined;
pub fn load() !void {
// if (active_shader != undefined) return Error.AlreadyInitialized;
flat = try Shader.compile(VERT_SHADER_TEXT, FRAG_SHADER_TEXT);
flat.enable();
return;
}
pub fn set_projection_matrix(matrix: *const Matrix4f) void {
active_shader.set_projection_matrix(matrix);
}
const Shader = struct {
vertex_shader: c.GLuint,
fragment_shader: c.GLuint,
program_handle: c.GLuint,
const Error = error {
CompilationFailed
};
// cache: HashMap<*const u8, i32> = undefined,
fn enable(self: *const Shader) void {
c.glUseProgram(self.program_handle);
active_shader = self;
c.glUniform1i(c.glGetUniformLocation(self.program_handle, "uTexture0"), 0);
}
fn compile(vert_shader_text: []const u8, frag_shader_text: []const u8) !Shader {
const vertex_shader: c.GLuint= c.glCreateShader(c.GL_VERTEX_SHADER);
{
c.glShaderSource(vertex_shader, 1, @ptrCast(&vert_shader_text), null);
c.glCompileShader(vertex_shader);
var compile_status: c.GLint = undefined;
c.glGetShaderiv(vertex_shader, c.GL_COMPILE_STATUS, &compile_status);
if (compile_status != c.GL_TRUE) {
std.log.err("Vertex shader compilation failed!", .{});
var error_text: [1000]u8 = undefined;
var len: i32 = undefined;
c.glGetShaderInfoLog(vertex_shader, 1000, &len, &error_text);
std.debug.print("{s}\n", .{ error_text[0..@intCast(len)] });
return Shader.Error.CompilationFailed;
}
}
const fragment_shader: c.GLuint = c.glCreateShader(c.GL_FRAGMENT_SHADER);
{
c.glShaderSource(fragment_shader, 1, @ptrCast(&frag_shader_text), null);
c.glCompileShader(fragment_shader);
var compile_status: c.GLint = undefined;
c.glGetShaderiv(fragment_shader, c.GL_COMPILE_STATUS, &compile_status);
if (compile_status != c.GL_TRUE) {
std.log.err("Fragment shader compilation failed!", .{});
var error_text: [1000]u8 = undefined;
var len: i32 = undefined;
c.glGetShaderInfoLog(fragment_shader, 1000, &len, &error_text);
std.debug.print("{s}\n", .{ error_text[0..@intCast(len)] });
return Shader.Error.CompilationFailed;
}
}
// TODO add logging for errors in compilation...
const program_handle: c.GLuint = c.glCreateProgram();
c.glAttachShader(program_handle, vertex_shader);
c.glAttachShader(program_handle, fragment_shader);
c.glLinkProgram(program_handle);
c.glValidateProgram(program_handle);
c.glBindAttribLocation(program_handle, POSITION_ATTRIBUTE_ID, "Position");
c.glBindAttribLocation(program_handle, COLOR_ATTRIBUTE_ID, "Color");
c.glBindAttribLocation(program_handle, TEXCOORD_ATTRIBUTE_ID, "TexCoord");
c.glDeleteShader(vertex_shader);
c.glDeleteShader(fragment_shader);
return Shader {
.vertex_shader = vertex_shader,
.fragment_shader = fragment_shader,
.program_handle = program_handle,
};
}
fn get_uniform_id(self: Shader, name: [*c]const u8) c.GLint {
return c.glGetUniformLocation(self.program_handle, name);
}
fn set_projection_matrix(self: Shader, matrix: *const Matrix4f) void {
const attribute_id: c.GLint = self.get_uniform_id("uProjection");
c.glUniformMatrix4fv(attribute_id, 1, c.GL_FALSE, @ptrCast(&matrix.values));
}
};

BIN
textures.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB