initial commit
This commit is contained in:
commit
314ae8b573
|
@ -0,0 +1,4 @@
|
||||||
|
.zig-cache/
|
||||||
|
zig-out/
|
||||||
|
|
||||||
|
libs/libopenwindow.so
|
|
@ -0,0 +1,42 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const exe_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
exe_mod.addIncludePath(.{ .cwd_relative = "./deps/"});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "game",
|
||||||
|
.root_module = exe_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.root_module.addCMacro("GL_GLEXT_PROTOTYPES", "1");
|
||||||
|
|
||||||
|
exe.linkLibC();
|
||||||
|
exe.linkSystemLibrary("OpenGl");
|
||||||
|
exe.linkSystemLibrary("X11");
|
||||||
|
exe.linkSystemLibrary("GLX");
|
||||||
|
exe.linkSystemLibrary("gallium");
|
||||||
|
exe.addLibraryPath(.{ .cwd_relative = "./libs/" });
|
||||||
|
exe.linkSystemLibrary("openwindow");
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
.{
|
||||||
|
// 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 = .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",
|
||||||
|
|
||||||
|
// Together with name, this represents a globally unique package
|
||||||
|
// identifier. This field is generated by the Zig toolchain when the
|
||||||
|
// package is first created, and then *never changes*. This allows
|
||||||
|
// unambiguous detection of one package being an updated version of
|
||||||
|
// another.
|
||||||
|
//
|
||||||
|
// When forking a Zig project, this id should be regenerated (delete the
|
||||||
|
// field and run `zig build`) if the upstream project is still maintained.
|
||||||
|
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||||
|
// original project's identity. Thus it is recommended to leave the comment
|
||||||
|
// on the following line intact, so that it shows up in code reviews that
|
||||||
|
// modify the field.
|
||||||
|
.fingerprint = 0x232b318ceacc9a92, // Changing this has security and trust implications.
|
||||||
|
|
||||||
|
// Tracks the earliest Zig version that the package considers to be a
|
||||||
|
// supported use case.
|
||||||
|
.minimum_zig_version = "0.14.1",
|
||||||
|
|
||||||
|
// 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. If the contents of a URL change this will result in a hash mismatch
|
||||||
|
// // which will prevent zig from using it.
|
||||||
|
// .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",
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
#ifndef _OPEN_WINDOW_H_
|
||||||
|
#define _OPEN_WINDOW_H_
|
||||||
|
|
||||||
|
#define WINDOW_DEFAULT_FPS 60
|
||||||
|
|
||||||
|
#define WINDOW_KEY_COUNT 256
|
||||||
|
|
||||||
|
#define WINDOW_MOUSE_BUTTON_COUNT 5
|
||||||
|
#define WINDOW_MOUSE_BUTTON1 (Button1)
|
||||||
|
#define WINDOW_MOUSE_BUTTON2 (Button2)
|
||||||
|
#define WINDOW_MOUSE_BUTTON3 (Button3)
|
||||||
|
#define WINDOW_MOUSE_BUTTON4 (Button4)
|
||||||
|
#define WINDOW_MOUSE_BUTTON5 (Button5)
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
int gladLoadGL(void);
|
||||||
|
|
||||||
|
#include "GL/gl.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#define Window X11Window
|
||||||
|
#include <GL/glx.h>
|
||||||
|
#include <X11/X.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#undef Window
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GLXFBConfig config;
|
||||||
|
GLXContext context;
|
||||||
|
} GLX;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Display * display;
|
||||||
|
X11Window window;
|
||||||
|
Colormap cmap;
|
||||||
|
long eventMask;
|
||||||
|
} X11;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
char current[WINDOW_KEY_COUNT];
|
||||||
|
char last[WINDOW_KEY_COUNT];
|
||||||
|
} keyboard;
|
||||||
|
struct {
|
||||||
|
bool current[WINDOW_MOUSE_BUTTON_COUNT];
|
||||||
|
bool last[WINDOW_MOUSE_BUTTON_COUNT];
|
||||||
|
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} mouse;
|
||||||
|
} WindowIOState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct timespec * t1;
|
||||||
|
struct timespec * t2;
|
||||||
|
double dt;
|
||||||
|
} WindowDeltaTime;
|
||||||
|
|
||||||
|
typedef struct Window Window;
|
||||||
|
|
||||||
|
typedef struct Window {
|
||||||
|
WindowIOState io;
|
||||||
|
GLX glx;
|
||||||
|
X11 x;
|
||||||
|
WindowDeltaTime dt;
|
||||||
|
uint32_t fps;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} Window;
|
||||||
|
|
||||||
|
#define WINDOW_KEY_Q ("q")
|
||||||
|
#define WINDOW_KEY_W ("w")
|
||||||
|
#define WINDOW_KEY_E ("e")
|
||||||
|
#define WINDOW_KEY_R ("r")
|
||||||
|
#define WINDOW_KEY_T ("t")
|
||||||
|
#define WINDOW_KEY_Y ("y")
|
||||||
|
#define WINDOW_KEY_U ("u")
|
||||||
|
#define WINDOW_KEY_I ("i")
|
||||||
|
#define WINDOW_KEY_O ("o")
|
||||||
|
#define WINDOW_KEY_P ("p")
|
||||||
|
#define WINDOW_KEY_A ("a")
|
||||||
|
#define WINDOW_KEY_S ("s")
|
||||||
|
#define WINDOW_KEY_D ("d")
|
||||||
|
#define WINDOW_KEY_F ("f")
|
||||||
|
#define WINDOW_KEY_G ("g")
|
||||||
|
#define WINDOW_KEY_H ("h")
|
||||||
|
#define WINDOW_KEY_J ("j")
|
||||||
|
#define WINDOW_KEY_K ("k")
|
||||||
|
#define WINDOW_KEY_L ("l")
|
||||||
|
#define WINDOW_KEY_Z ("z")
|
||||||
|
#define WINDOW_KEY_X ("x")
|
||||||
|
#define WINDOW_KEY_C ("c")
|
||||||
|
#define WINDOW_KEY_V ("v")
|
||||||
|
#define WINDOW_KEY_B ("b")
|
||||||
|
#define WINDOW_KEY_N ("n")
|
||||||
|
#define WINDOW_KEY_M ("m")
|
||||||
|
|
||||||
|
#define WINDOW_KEY_1 ("1")
|
||||||
|
#define WINDOW_KEY_2 ("2")
|
||||||
|
#define WINDOW_KEY_3 ("3")
|
||||||
|
#define WINDOW_KEY_4 ("4")
|
||||||
|
#define WINDOW_KEY_5 ("5")
|
||||||
|
#define WINDOW_KEY_6 ("6")
|
||||||
|
#define WINDOW_KEY_7 ("7")
|
||||||
|
#define WINDOW_KEY_8 ("8")
|
||||||
|
#define WINDOW_KEY_9 ("9")
|
||||||
|
#define WINDOW_KEY_0 ("0")
|
||||||
|
|
||||||
|
#define WINDOW_KEY_ESC ("Escape")
|
||||||
|
#define WINDOW_KEY_SPACE ("space")
|
||||||
|
|
||||||
|
bool windowKeyPressed(Window * window, char * key);
|
||||||
|
bool windowKeyReleased(Window * window, char * key);
|
||||||
|
bool windowKeyHeld(Window * window, char * key);
|
||||||
|
|
||||||
|
bool windowKeyShift(Window * window);
|
||||||
|
bool windowKeyCtrl(Window * window);
|
||||||
|
bool windowKeyAlt(Window * window);
|
||||||
|
|
||||||
|
bool windowMousePressed(Window * window, int button);
|
||||||
|
bool windowMouseHeld(Window * window, int button);
|
||||||
|
bool windowMouseReleased(Window * window, int button);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
HINSTANCE instance;
|
||||||
|
HDC display;
|
||||||
|
HWND window;
|
||||||
|
HGLRC context;
|
||||||
|
} WGL;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BYTE current[WINDOW_KEY_COUNT];
|
||||||
|
BYTE last[WINDOW_KEY_COUNT];
|
||||||
|
} WindowIOState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LARGE_INTEGER t1;
|
||||||
|
LARGE_INTEGER t2;
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
double dt;
|
||||||
|
} WindowDeltaTime;
|
||||||
|
|
||||||
|
typedef struct Window Window;
|
||||||
|
|
||||||
|
typedef struct Window {
|
||||||
|
WindowIOState io;
|
||||||
|
WGL wgl;
|
||||||
|
WindowDeltaTime dt;
|
||||||
|
uint32_t fps;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} Window;
|
||||||
|
|
||||||
|
#define WINDOW_KEY_Q ('Q')
|
||||||
|
#define WINDOW_KEY_W ('W')
|
||||||
|
#define WINDOW_KEY_E ('E')
|
||||||
|
#define WINDOW_KEY_R ('R')
|
||||||
|
#define WINDOW_KEY_T ('T')
|
||||||
|
#define WINDOW_KEY_Y ('Y')
|
||||||
|
#define WINDOW_KEY_U ('U')
|
||||||
|
#define WINDOW_KEY_I ('I')
|
||||||
|
#define WINDOW_KEY_O ('O')
|
||||||
|
#define WINDOW_KEY_P ('P')
|
||||||
|
#define WINDOW_KEY_A ('A')
|
||||||
|
#define WINDOW_KEY_S ('S')
|
||||||
|
#define WINDOW_KEY_D ('D')
|
||||||
|
#define WINDOW_KEY_F ('F')
|
||||||
|
#define WINDOW_KEY_G ('G')
|
||||||
|
#define WINDOW_KEY_H ('H')
|
||||||
|
#define WINDOW_KEY_J ('J')
|
||||||
|
#define WINDOW_KEY_K ('K')
|
||||||
|
#define WINDOW_KEY_L ('L')
|
||||||
|
#define WINDOW_KEY_Z ('Z')
|
||||||
|
#define WINDOW_KEY_X ('X')
|
||||||
|
#define WINDOW_KEY_C ('C')
|
||||||
|
#define WINDOW_KEY_V ('V')
|
||||||
|
#define WINDOW_KEY_B ('B')
|
||||||
|
#define WINDOW_KEY_N ('N')
|
||||||
|
#define WINDOW_KEY_M ('M')
|
||||||
|
|
||||||
|
#define WINDOW_KEY_1 ('1')
|
||||||
|
#define WINDOW_KEY_2 ('2')
|
||||||
|
#define WINDOW_KEY_3 ('3')
|
||||||
|
#define WINDOW_KEY_4 ('4')
|
||||||
|
#define WINDOW_KEY_5 ('5')
|
||||||
|
#define WINDOW_KEY_6 ('6')
|
||||||
|
#define WINDOW_KEY_7 ('7')
|
||||||
|
#define WINDOW_KEY_8 ('8')
|
||||||
|
#define WINDOW_KEY_9 ('9')
|
||||||
|
#define WINDOW_KEY_0 ('0')
|
||||||
|
|
||||||
|
#define WINDOW_KEY_ESC (VK_ESCAPE)
|
||||||
|
#define WINDOW_KEY_SPACE (VK_SPACE)
|
||||||
|
|
||||||
|
bool windowKeyPressed(Window * window, int key);
|
||||||
|
bool windowKeyReleased(Window * window, int key);
|
||||||
|
bool windowKeyHeld(Window * window, int key);
|
||||||
|
|
||||||
|
bool windowKeyShift(Window * window);
|
||||||
|
bool windowKeyCtrl(Window * window);
|
||||||
|
bool windowKeyAlt(Window * window);
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
Window * openWindow(const char * name, size_t width, size_t height);
|
||||||
|
void windowSetFps(Window * window, uint32_t fps);
|
||||||
|
double windowGetDeltaTime(Window * window);
|
||||||
|
void windowGetSize(Window * window, int * width, int * height);
|
||||||
|
void windowGetMousePosition(Window * window, int * x, int * y);
|
||||||
|
void windowDraw(Window * window);
|
||||||
|
void windowHandleEvents(Window * window);
|
||||||
|
void closeWindow(Window * window);
|
||||||
|
|
||||||
|
int getOpenGLProcs(void);
|
||||||
|
|
||||||
|
#endif // _OPEN_WINDOW_H_
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Libs
|
||||||
|
|
||||||
|
1. [openwindow](https://gitea.hskl.org/MaciejSamborski/openwindow)
|
|
@ -0,0 +1,14 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const math = @import("math.zig");
|
||||||
|
|
||||||
|
const Camera = @This();
|
||||||
|
|
||||||
|
position: math.Vec2,
|
||||||
|
scale: f32,
|
||||||
|
|
||||||
|
pub fn init(position: math.Vec2) Camera {
|
||||||
|
return .{
|
||||||
|
.position = position,
|
||||||
|
.scale = 1.0,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
pub const Color = struct {
|
||||||
|
r: f32,
|
||||||
|
g: f32,
|
||||||
|
b: f32,
|
||||||
|
a: f32,
|
||||||
|
|
||||||
|
pub fn fromHex(hex: u32) Color {
|
||||||
|
return Color{
|
||||||
|
.r = @as(f32, @floatFromInt(((hex >> (8 * 3)) & 0xFF))) / @as(f32, @floatFromInt(0xFF)),
|
||||||
|
.g = @as(f32, @floatFromInt(((hex >> (8 * 2)) & 0xFF))) / @as(f32, @floatFromInt(0xFF)),
|
||||||
|
.b = @as(f32, @floatFromInt(((hex >> (8 * 1)) & 0xFF))) / @as(f32, @floatFromInt(0xFF)),
|
||||||
|
.a = @as(f32, @floatFromInt(((hex >> (8 * 0)) & 0xFF))) / @as(f32, @floatFromInt(0xFF)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,360 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const common = @import("common.zig");
|
||||||
|
const math = @import("math.zig");
|
||||||
|
|
||||||
|
const gl = @cImport(@cInclude("GL/gl.h"));
|
||||||
|
|
||||||
|
const Draw = @This();
|
||||||
|
const Renderer = @import("renderer.zig");
|
||||||
|
|
||||||
|
renderer: *Renderer,
|
||||||
|
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
drawables: struct {
|
||||||
|
general: struct {
|
||||||
|
triangles: Drawables(gl.GL_TRIANGLES),
|
||||||
|
triangleFan: Drawables(gl.GL_TRIANGLE_FAN),
|
||||||
|
},
|
||||||
|
instanced: struct {
|
||||||
|
triangles: DrawablesInstanced(gl.GL_TRIANGLES),
|
||||||
|
triangleFan: DrawablesInstanced(gl.GL_TRIANGLE_FAN),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
attribs: struct {
|
||||||
|
general: std.ArrayList(Attribute.General),
|
||||||
|
instanced: std.ArrayList(Attribute.Instanced),
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn init(renderer: *Renderer) !Draw {
|
||||||
|
return Draw{
|
||||||
|
.renderer = renderer,
|
||||||
|
.allocator = renderer.allocator,
|
||||||
|
.drawables = .{
|
||||||
|
.general = .{
|
||||||
|
.triangles = .init(renderer.allocator),
|
||||||
|
.triangleFan = .init(renderer.allocator),
|
||||||
|
},
|
||||||
|
.instanced = .{
|
||||||
|
.triangles = .init(renderer.allocator),
|
||||||
|
.triangleFan = .init(renderer.allocator),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.attribs = .{
|
||||||
|
.general = .init(renderer.allocator),
|
||||||
|
.instanced = .init(renderer.allocator),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Attribute = union(enum) {
|
||||||
|
instanced: Instanced,
|
||||||
|
general: General,
|
||||||
|
|
||||||
|
pub const AttributeOptions = struct { size: gl.GLint, type: gl.GLenum, normalized: gl.GLboolean };
|
||||||
|
|
||||||
|
const AttributeData = struct {
|
||||||
|
name: []const u8,
|
||||||
|
loc: gl.GLuint,
|
||||||
|
size: gl.GLint,
|
||||||
|
type: gl.GLenum,
|
||||||
|
normalized: gl.GLboolean,
|
||||||
|
offset: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Instanced = struct {
|
||||||
|
data: AttributeData,
|
||||||
|
|
||||||
|
pub var offset: usize = 0;
|
||||||
|
|
||||||
|
fn new(draw: *Draw, name: []const u8, options: AttributeOptions) Instanced {
|
||||||
|
offset += @intCast(options.size);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.data = .{
|
||||||
|
.name = name,
|
||||||
|
.loc = @intCast(gl.glGetAttribLocation(draw.renderer.program, @ptrCast(name))),
|
||||||
|
.size = options.size,
|
||||||
|
.type = options.type,
|
||||||
|
.normalized = options.normalized,
|
||||||
|
.offset = offset - @as(usize, @intCast(options.size)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const General = struct {
|
||||||
|
data: AttributeData,
|
||||||
|
|
||||||
|
pub var offset: usize = 0;
|
||||||
|
|
||||||
|
fn new(draw: *Draw, name: []const u8, options: AttributeOptions) General {
|
||||||
|
offset += @intCast(options.size);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.data = .{
|
||||||
|
.name = name,
|
||||||
|
.loc = @intCast(gl.glGetAttribLocation(draw.renderer.program, @ptrCast(name))),
|
||||||
|
.size = options.size,
|
||||||
|
.type = options.type,
|
||||||
|
.normalized = options.normalized,
|
||||||
|
.offset = offset - @as(usize, @intCast(options.size)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn addAttrib(draw: *Draw, name: []const u8, options: Attribute.AttributeOptions, instanced: bool) !void {
|
||||||
|
if (instanced)
|
||||||
|
try draw.attribs.instanced.append(.new(draw, name, options))
|
||||||
|
else
|
||||||
|
try draw.attribs.general.append( .new(draw, name, options))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAttrib(draw: *Draw, name: []const u8) ?*Attribute {
|
||||||
|
for (draw.attribs.items) |attr| {
|
||||||
|
if (std.mem.eql(u8, name, attr.name)) {
|
||||||
|
return &attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Uniform = struct {
|
||||||
|
loc: gl.GLuint,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn getUniform(draw: *Draw, name: []const u8) Uniform {
|
||||||
|
return .{
|
||||||
|
.loc = @intCast(gl.glGetUniformLocation(draw.renderer.program, @ptrCast(name))),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Drawables(mode: gl.GLenum) type {
|
||||||
|
return struct {
|
||||||
|
data: switch (mode) {
|
||||||
|
gl.GL_TRIANGLES => std.ArrayList(f32),
|
||||||
|
gl.GL_TRIANGLE_FAN => std.ArrayList(std.ArrayList(f32)),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
drawMode: gl.GLenum = mode,
|
||||||
|
|
||||||
|
fn init(allocator: std.mem.Allocator) @This() {
|
||||||
|
return .{
|
||||||
|
.data = .init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn DrawablesInstanced(mode: gl.GLenum) type {
|
||||||
|
return struct {
|
||||||
|
data: std.ArrayList(std.ArrayList(f32)),
|
||||||
|
instanceData: std.ArrayList(std.ArrayList(f32)),
|
||||||
|
drawMode: gl.GLenum = mode,
|
||||||
|
|
||||||
|
fn init(allocator: std.mem.Allocator) @This() {
|
||||||
|
return .{
|
||||||
|
.data = .init(allocator),
|
||||||
|
.instanceData = .init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawRectangle(draw: *Draw, corners: [4]@Vector(2, f32), depth: f32, color: common.Color) !void {
|
||||||
|
try draw.drawables.general.triangles.data.appendSlice(&.{
|
||||||
|
corners[3][0], corners[3][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[0][0], corners[0][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[1][0], corners[1][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
|
||||||
|
corners[3][0], corners[3][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[1][0], corners[1][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[2][0], corners[2][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawRectangleInstanced(draw: *Draw, corners: [4]@Vector(2, f32), offsets: []const @Vector(2, f32), depth: f32, color: common.Color) !void {
|
||||||
|
var data = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
var instanceData = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
corners[3][0], corners[3][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[0][0], corners[0][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[1][0], corners[1][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
|
||||||
|
corners[3][0], corners[3][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[1][0], corners[1][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[2][0], corners[2][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (offsets) |offset| {
|
||||||
|
try instanceData.appendSlice(&.{
|
||||||
|
offset[0], offset[1], depth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try draw.drawables.instanced.triangles.data.append(data);
|
||||||
|
try draw.drawables.instanced.triangles.instanceData.append(instanceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTriangle(draw: *Draw, corners: [3]@Vector(2, f32), depth: f32, color: common.Color) !void {
|
||||||
|
try draw.drawables.general.triangles.data.appendSlice(&.{
|
||||||
|
corners[0][0], corners[0][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[1][0], corners[1][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[2][0], corners[2][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTriangleInstanced(draw: *Draw, corners: [3]@Vector(2, f32), offsets: []const @Vector(2, f32), depth: f32, color: common.Color) !void {
|
||||||
|
var data = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
var instanceData = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
corners[0][0], corners[0][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[1][0], corners[1][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
corners[2][0], corners[2][1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (offsets) |offset| {
|
||||||
|
try instanceData.appendSlice(&.{
|
||||||
|
offset[0], offset[1], depth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try draw.drawables.instanced.triangles.data.append(data);
|
||||||
|
try draw.drawables.instanced.triangles.instanceData.append(instanceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CirclePrecission = 64;
|
||||||
|
|
||||||
|
pub fn drawCircle(
|
||||||
|
draw: *Draw,
|
||||||
|
origin: @Vector(2, f32),
|
||||||
|
depth: f32,
|
||||||
|
radius: f32,
|
||||||
|
arc: f32,
|
||||||
|
wound: enum { clockwise, counterclockwise },
|
||||||
|
color: common.Color
|
||||||
|
) !void {
|
||||||
|
var data: std.ArrayList(f32) = .init(draw.allocator);
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
origin[0], origin[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
|
||||||
|
const r = if (wound == .clockwise) radius else -radius;
|
||||||
|
|
||||||
|
for (0..@intFromFloat(std.math.round((CirclePrecission + 1) * arc))) |i| {
|
||||||
|
var v = math.Vec2{ 0, r };
|
||||||
|
|
||||||
|
math.rotateVector2(&v, 2.0 * std.math.pi / @as(f32, @floatFromInt(CirclePrecission)) * @as(f32, @floatFromInt(i)));
|
||||||
|
|
||||||
|
v += origin;
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
v[0], v[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try draw.drawables.general.triangleFan.data.append(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawCircleInstanced(
|
||||||
|
draw: *Draw,
|
||||||
|
origin: @Vector(2, f32),
|
||||||
|
offsets: []const @Vector(2, f32),
|
||||||
|
depth: f32,
|
||||||
|
radius: f32,
|
||||||
|
arc: f32,
|
||||||
|
wound: enum { clockwise, counterclockwise },
|
||||||
|
color: common.Color
|
||||||
|
) !void {
|
||||||
|
var data = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
var instanceData = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
origin[0], origin[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (0..@intFromFloat(std.math.round((CirclePrecission + 1) * arc))) |i| {
|
||||||
|
var v = math.Vec2{ 0, radius };
|
||||||
|
math.rotateVector2(&v, 2.0 * std.math.pi / @as(f32, @floatFromInt(CirclePrecission)) * @as(f32, @floatFromInt(i)));
|
||||||
|
|
||||||
|
if (wound == .counterclockwise) v *= @splat(-1);
|
||||||
|
|
||||||
|
v += origin;
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
v[0], v[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (offsets) |offset| {
|
||||||
|
try instanceData.appendSlice(&.{
|
||||||
|
offset[0], offset[1], depth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try draw.drawables.instanced.triangleFan.data.append(data);
|
||||||
|
try draw.drawables.instanced.triangleFan.instanceData.append(instanceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawLine(draw: *Draw, points: [2]@Vector(2, f32), width: f32, depth: f32, color: common.Color) !void {
|
||||||
|
const dx = points[1][0] - points[0][0];
|
||||||
|
const dy = points[1][1] - points[0][1];
|
||||||
|
|
||||||
|
var angle = std.math.atan2(dy, dx);
|
||||||
|
if (angle < 0) angle += std.math.pi * 2;
|
||||||
|
const inverse = @mod((angle + std.math.pi), (std.math.pi * 2));
|
||||||
|
|
||||||
|
const ps1 = math.findLinePoints(points[0], width, angle);
|
||||||
|
const ps2 = math.findLinePoints(points[1], width, inverse);
|
||||||
|
|
||||||
|
try draw.drawables.general.triangles.data.appendSlice(&.{
|
||||||
|
ps1.p1[0], ps1.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps1.p2[0], ps1.p2[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps2.p1[0], ps2.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
|
||||||
|
ps2.p1[0], ps2.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps2.p2[0], ps2.p2[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps1.p1[0], ps1.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawLineInstanced(draw: *Draw, points: [2]@Vector(2, f32), offsets: []const @Vector(2, f32), width: f32, depth: f32, color: common.Color) !void {
|
||||||
|
var data = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
var instanceData = std.ArrayList(f32).init(draw.allocator);
|
||||||
|
|
||||||
|
const dx = points[1][0] - points[0][0];
|
||||||
|
const dy = points[1][1] - points[0][1];
|
||||||
|
|
||||||
|
var angle = std.math.atan2(dy, dx);
|
||||||
|
if (angle < 0) angle += std.math.pi * 2;
|
||||||
|
const inverse = @mod((angle + std.math.pi), (std.math.pi * 2));
|
||||||
|
|
||||||
|
const ps1 = math.findLinePoints(points[0], width, angle);
|
||||||
|
const ps2 = math.findLinePoints(points[1], width, inverse);
|
||||||
|
|
||||||
|
try data.appendSlice(&.{
|
||||||
|
ps1.p1[0], ps1.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps1.p2[0], ps1.p2[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps2.p1[0], ps2.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
|
||||||
|
ps2.p1[0], ps2.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps2.p2[0], ps2.p2[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
ps1.p1[0], ps1.p1[1], depth, color.r, color.g, color.b, color.a,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (offsets) |offset| {
|
||||||
|
try instanceData.appendSlice(&.{
|
||||||
|
offset[0], offset[1], depth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try draw.drawables.instanced.triangles.data.append(data);
|
||||||
|
try draw.drawables.instanced.triangles.instanceData.append(instanceData);
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Renderer = @import("renderer.zig");
|
||||||
|
const Camera = @import("camera.zig");
|
||||||
|
const Draw = @import("draw.zig");
|
||||||
|
const Grid = @import("grid.zig");
|
||||||
|
|
||||||
|
const Game = @This();
|
||||||
|
|
||||||
|
const World = struct {
|
||||||
|
grid: Grid,
|
||||||
|
size: @Vector(2, usize),
|
||||||
|
|
||||||
|
fn init(allocator: std.mem.Allocator, width: usize, height: usize) !World {
|
||||||
|
return World{
|
||||||
|
.grid = try Grid.init(allocator, width, height),
|
||||||
|
.size = .{ width, height },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Player = struct {
|
||||||
|
position: PlayerPosition = .{ 0, 0 },
|
||||||
|
|
||||||
|
const PlayerPosition = @Vector(2, f32);
|
||||||
|
const default: Player = .{};
|
||||||
|
};
|
||||||
|
|
||||||
|
camera: Camera,
|
||||||
|
draw: Draw,
|
||||||
|
|
||||||
|
world: World,
|
||||||
|
player: Player,
|
||||||
|
|
||||||
|
pub fn init(renderer: *Renderer, width: u32, height: u32) !Game {
|
||||||
|
return .{
|
||||||
|
.camera = .init(.{ @as(f32, @floatFromInt(renderer.width.*))/2.0, @as(f32, @floatFromInt(renderer.height.*))/2.0 }),
|
||||||
|
.draw = try .init(renderer),
|
||||||
|
.world = try World.init(renderer.allocator, width, height),
|
||||||
|
.player = .default,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const common = @import("common.zig");
|
||||||
|
|
||||||
|
const Grid = @This();
|
||||||
|
|
||||||
|
pub const Cell = struct {
|
||||||
|
const Face = struct {
|
||||||
|
color: common.Color,
|
||||||
|
};
|
||||||
|
|
||||||
|
topFace: Face = .{
|
||||||
|
.color = common.Color.fromHex(0xFF0000FF),
|
||||||
|
},
|
||||||
|
leftFace: Face = .{
|
||||||
|
.color = common.Color.fromHex(0x00FF00FF),
|
||||||
|
},
|
||||||
|
rightFace: Face = .{
|
||||||
|
.color = common.Color.fromHex(0x0000FFFF),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CellMapKey = struct {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn coord(x: usize, y: usize) CellMapKey {
|
||||||
|
return .{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CellMap = std.AutoHashMap(CellMapKey, Cell);
|
||||||
|
|
||||||
|
map: CellMap,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator, width: usize, height: usize) !Grid {
|
||||||
|
var map: CellMap = CellMap.init(allocator);
|
||||||
|
try map.ensureTotalCapacity(@intCast(width * height));
|
||||||
|
|
||||||
|
for (0..width) |w| {
|
||||||
|
for (0..height) |h| {
|
||||||
|
try map.put(coord(w, h), Cell{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.map = map,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(grid: *Grid, key: CellMapKey) ?Cell {
|
||||||
|
return grid.map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put(grid: *Grid, key: CellMapKey, cell: Cell) !void {
|
||||||
|
try grid.map.put(key, cell);
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const gl = @cImport(@cInclude("GL/gl.h"));
|
||||||
|
const ow = @cImport(@cInclude("openwindow.h"));
|
||||||
|
|
||||||
|
const Renderer = @import("renderer.zig");
|
||||||
|
const Game = @import("game.zig");
|
||||||
|
const Grid = @import("grid.zig");
|
||||||
|
const Draw = @import("draw.zig");
|
||||||
|
|
||||||
|
const Transform = @import("transform.zig");
|
||||||
|
|
||||||
|
const math = @import("math.zig");
|
||||||
|
const common = @import("common.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const window: *ow.Window = ow.openWindow("game", 1920, 1080);
|
||||||
|
defer ow.closeWindow(window);
|
||||||
|
|
||||||
|
var aa = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
defer aa.deinit();
|
||||||
|
const allocator = aa.allocator();
|
||||||
|
|
||||||
|
var renderer = try Renderer.init(allocator, @ptrCast(&window.width), @ptrCast(&window.height));
|
||||||
|
|
||||||
|
var g = try Game.init(&renderer, 4, 4);
|
||||||
|
|
||||||
|
var d = try Draw.init(&renderer);
|
||||||
|
try addCommonAttribs(&d);
|
||||||
|
|
||||||
|
try d.drawLineInstanced(
|
||||||
|
.{ .{ 0, 1004 }, .{ 0, -4 }},
|
||||||
|
&.{
|
||||||
|
.{ 0, 0 },
|
||||||
|
.{ 100, 0 },
|
||||||
|
.{ 200, 0 },
|
||||||
|
.{ 300, 0 },
|
||||||
|
.{ 400, 0 },
|
||||||
|
.{ 500, 0 },
|
||||||
|
.{ 600, 0 },
|
||||||
|
.{ 700, 0 },
|
||||||
|
.{ 800, 0 },
|
||||||
|
.{ 900, 0 },
|
||||||
|
.{ 1000, 0 },
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
common.Color.fromHex(0x000000FF)
|
||||||
|
);
|
||||||
|
|
||||||
|
try d.drawLineInstanced(
|
||||||
|
.{ .{ 1004, 0 }, .{ -4, 0 }},
|
||||||
|
&.{
|
||||||
|
.{ 0, 0 },
|
||||||
|
.{ 0, 100 },
|
||||||
|
.{ 0, 200 },
|
||||||
|
.{ 0, 300 },
|
||||||
|
.{ 0, 400 },
|
||||||
|
.{ 0, 500 },
|
||||||
|
.{ 0, 600 },
|
||||||
|
.{ 0, 700 },
|
||||||
|
.{ 0, 800 },
|
||||||
|
.{ 0, 900 },
|
||||||
|
.{ 0, 1000 },
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
common.Color.fromHex(0x000000FF)
|
||||||
|
);
|
||||||
|
|
||||||
|
try d.drawRectangle(.{
|
||||||
|
.{ 0, 0 },
|
||||||
|
.{ 100, 0 },
|
||||||
|
.{ 100, 100 },
|
||||||
|
.{ 0, 100 },
|
||||||
|
}, 0, common.Color.fromHex(0xFF0000FF),
|
||||||
|
);
|
||||||
|
|
||||||
|
try d.drawCircle(.{ 50, 50 },
|
||||||
|
0.01,
|
||||||
|
50,
|
||||||
|
1.0,
|
||||||
|
.clockwise,
|
||||||
|
common.Color.fromHex(0x00FF00FF)
|
||||||
|
);
|
||||||
|
|
||||||
|
var globalTransform = Transform.init();
|
||||||
|
|
||||||
|
while (!ow.windowKeyPressed(window, @ptrCast(@constCast(ow.WINDOW_KEY_ESC)))) {
|
||||||
|
ow.windowHandleEvents(window);
|
||||||
|
defer ow.windowDraw(window);
|
||||||
|
|
||||||
|
handleInput(window, &g);
|
||||||
|
|
||||||
|
const ortho = math.Mat4x4.orthographic(0.0, @floatFromInt(window.width), 0.0, @floatFromInt(window.height), -1.0, 1.0);
|
||||||
|
globalTransform.set(.orthographic, ortho);
|
||||||
|
globalTransform.set(.translation, math.Mat4x4.translation(g.camera.position));
|
||||||
|
globalTransform.set(.scale, math.Mat4x4.scale(g.camera.scale));
|
||||||
|
gl.glUniformMatrix4fv(@intCast(d.getUniform("uTransform").loc), 1, gl.GL_FALSE, &globalTransform.get().data[0][0]);
|
||||||
|
try renderer.renderDraw(&d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addCommonAttribs(d: *Draw) !void {
|
||||||
|
|
||||||
|
// General
|
||||||
|
|
||||||
|
try d.addAttrib("aPos", .{
|
||||||
|
.size = 3,
|
||||||
|
.type = gl.GL_FLOAT,
|
||||||
|
.normalized = gl.GL_FALSE,
|
||||||
|
}, false);
|
||||||
|
try d.addAttrib("aColor", .{
|
||||||
|
.size = 4,
|
||||||
|
.type = gl.GL_FLOAT,
|
||||||
|
.normalized = gl.GL_FALSE,
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// Instanced
|
||||||
|
|
||||||
|
try d.addAttrib("aInstanceOffset", .{
|
||||||
|
.size = 3,
|
||||||
|
.type = gl.GL_FLOAT,
|
||||||
|
.normalized = gl.GL_FALSE,
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleInput(window: *ow.Window, game: *Game) void {
|
||||||
|
const dt: f32 = @floatCast(ow.windowGetDeltaTime(window));
|
||||||
|
const speed = 300;
|
||||||
|
|
||||||
|
if (ow.windowKeyHeld(window, @ptrCast(@constCast(ow.WINDOW_KEY_W))))
|
||||||
|
game.camera.position[1] -= speed*dt;
|
||||||
|
|
||||||
|
if (ow.windowKeyHeld(window, @ptrCast(@constCast(ow.WINDOW_KEY_S))))
|
||||||
|
game.camera.position[1] += speed*dt;
|
||||||
|
|
||||||
|
if (ow.windowKeyHeld(window, @ptrCast(@constCast(ow.WINDOW_KEY_A))))
|
||||||
|
game.camera.position[0] += speed*dt;
|
||||||
|
|
||||||
|
if (ow.windowKeyHeld(window, @ptrCast(@constCast(ow.WINDOW_KEY_D))))
|
||||||
|
game.camera.position[0] -= speed*dt;
|
||||||
|
|
||||||
|
if (ow.windowKeyHeld(window, @ptrCast(@constCast(ow.WINDOW_KEY_Q))))
|
||||||
|
game.camera.scale += 2*dt;
|
||||||
|
|
||||||
|
if (ow.windowKeyHeld(window, @ptrCast(@constCast(ow.WINDOW_KEY_E))))
|
||||||
|
game.camera.scale -= 2*dt;
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Mat4x4 = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
data: [4][4]f32,
|
||||||
|
|
||||||
|
pub fn init(data: [16]f32) Mat4x4 {
|
||||||
|
return .{
|
||||||
|
.data = .{
|
||||||
|
data[0..4].*,
|
||||||
|
data[4..8].*,
|
||||||
|
data[8..12].*,
|
||||||
|
data[12..16].*,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mult(matrix: *Self, other: Mat4x4) void {
|
||||||
|
var result: [4][4]f32 = undefined;
|
||||||
|
|
||||||
|
for (&result, 0..) |*col, j| {
|
||||||
|
for (col, 0..) |*elem, i| {
|
||||||
|
elem.* = matrix.data[0][i] * other.data[j][0] +
|
||||||
|
matrix.data[1][i] * other.data[j][1] +
|
||||||
|
matrix.data[2][i] * other.data[j][2] +
|
||||||
|
matrix.data[3][i] * other.data[j][3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix.data = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mults(matrix: *Self, scalar: f32) void {
|
||||||
|
for (&matrix.data) |*col| {
|
||||||
|
for (col) |*row| {
|
||||||
|
row.* *= scalar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform(matrix: *const Self, point: *Vec2) void {
|
||||||
|
const x = point[0];
|
||||||
|
const y = point[1];
|
||||||
|
|
||||||
|
point[0] = matrix.data[0][0]*x + matrix.data[1][0]*y + matrix.data[2][0]*0.0 + matrix.data[3][0]*1.0;
|
||||||
|
point[1] = matrix.data[0][1]*x + matrix.data[1][1]*y + matrix.data[2][1]*0.0 + matrix.data[3][1]*1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const zero: Mat4x4 = .init(@splat(0));
|
||||||
|
pub const id: Mat4x4 = .init(.{
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn orthographic(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) Mat4x4 {
|
||||||
|
return .init(.{
|
||||||
|
2 / (right - left), 0, 0, 0,
|
||||||
|
0, 2 / (top - bottom), 0, 0,
|
||||||
|
0, 0, -2 / (far - near), 0,
|
||||||
|
-((right + left)/(right - left)), -((top + bottom)/(top - bottom)), -((far + near)/(far - near)), 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translation(offset: Vec2) Mat4x4 {
|
||||||
|
return .init(.{
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
offset[0], offset[1], 0.0, 1.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale(scalar: f32) Mat4x4 {
|
||||||
|
return .init(.{
|
||||||
|
scalar, 0.0, 0.0, 0.0,
|
||||||
|
0.0, scalar, 0.0, 0.0,
|
||||||
|
0.0, 0.0, scalar, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Vec2 = @Vector(2, f32);
|
||||||
|
|
||||||
|
pub fn rotateVector2(vec: *Vec2, angle: f32) void {
|
||||||
|
const x: f32 = vec[0];
|
||||||
|
const y: f32 = vec[1];
|
||||||
|
vec[0] = x * std.math.cos(angle) - y * std.math.sin(angle);
|
||||||
|
vec[1] = x * std.math.sin(angle) + y * std.math.cos(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vector2FromRotation(angle: f32) Vec2 {
|
||||||
|
return .{
|
||||||
|
std.math.cos(angle),
|
||||||
|
std.math.sin(angle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn findLinePoints(point: Vec2, width: f32, angle: f32) struct { p1: Vec2, p2: Vec2 } {
|
||||||
|
const p1 = Vec2 { point[0] - width * std.math.sin(angle), point[1] + width * std.math.cos(angle) };
|
||||||
|
const p2 = Vec2 { point[0] + width * std.math.sin(angle), point[1] - width * std.math.cos(angle) };
|
||||||
|
|
||||||
|
return .{ .p1 = p1, .p2 = p2 };
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Game = @import("game.zig");
|
||||||
|
const Grid = @import("grid.zig");
|
||||||
|
const Draw = @import("draw.zig");
|
||||||
|
const gl = @cImport(@cInclude("GL/gl.h"));
|
||||||
|
|
||||||
|
const Renderer = @This();
|
||||||
|
|
||||||
|
const ShaderCompileError = error{
|
||||||
|
COMPILATION_FAILED,
|
||||||
|
};
|
||||||
|
|
||||||
|
const opengl = struct {
|
||||||
|
const shaderKind = enum {
|
||||||
|
VERTEX,
|
||||||
|
FRAGMENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn createShader(allocator: std.mem.Allocator, source: [:0]const u8, kind: shaderKind) !gl.GLuint {
|
||||||
|
const stype: c_uint = switch (kind) {
|
||||||
|
.VERTEX => gl.GL_VERTEX_SHADER,
|
||||||
|
.FRAGMENT => gl.GL_FRAGMENT_SHADER,
|
||||||
|
};
|
||||||
|
|
||||||
|
const shader = gl.glCreateShader(stype);
|
||||||
|
gl.glShaderSource(shader, 1, @ptrCast(&source.ptr), null);
|
||||||
|
gl.glCompileShader(shader);
|
||||||
|
|
||||||
|
var len: c_int = 0;
|
||||||
|
var info: []u8 = undefined;
|
||||||
|
|
||||||
|
gl.glGetShaderiv(shader, gl.GL_INFO_LOG_LENGTH, &len);
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
info = try allocator.alloc(u8, @intCast(len));
|
||||||
|
gl.glGetShaderInfoLog(shader, len, &len, @ptrCast(info));
|
||||||
|
std.debug.print("{s}\n", .{info});
|
||||||
|
allocator.free(info);
|
||||||
|
|
||||||
|
return ShaderCompileError.COMPILATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createProgram(allocator: std.mem.Allocator, vertex: [:0]const u8, fragment: [:0]const u8) !gl.GLuint {
|
||||||
|
const program = gl.glCreateProgram();
|
||||||
|
|
||||||
|
const vertexShader = try createShader(allocator, vertex, .VERTEX);
|
||||||
|
const fragmentShader = try createShader(allocator, fragment, .FRAGMENT);
|
||||||
|
|
||||||
|
gl.glAttachShader(program, vertexShader);
|
||||||
|
gl.glAttachShader(program, fragmentShader);
|
||||||
|
|
||||||
|
gl.glLinkProgram(program);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(allocator: std.mem.Allocator) !struct { program: c_uint, vao: c_uint, vbo: c_uint, vboi: c_uint } {
|
||||||
|
const program = try opengl.createProgram(allocator, vss, fss);
|
||||||
|
|
||||||
|
var vao: gl.GLuint = 0;
|
||||||
|
gl.glCreateVertexArrays(1, @ptrCast(&vao));
|
||||||
|
|
||||||
|
var vbo: gl.GLuint = 0;
|
||||||
|
gl.glCreateBuffers(1, @ptrCast(&vbo));
|
||||||
|
|
||||||
|
var vboi: gl.GLuint = 0;
|
||||||
|
gl.glCreateBuffers(1, @ptrCast(&vboi));
|
||||||
|
|
||||||
|
gl.glBindVertexArray(vao);
|
||||||
|
|
||||||
|
gl.glUseProgram(program);
|
||||||
|
|
||||||
|
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
gl.glEnable(gl.GL_BLEND);
|
||||||
|
|
||||||
|
gl.glEnable(gl.GL_DEPTH_TEST);
|
||||||
|
gl.glDepthFunc(gl.GL_LESS);
|
||||||
|
|
||||||
|
// TODO: enable
|
||||||
|
//gl.glEnable(gl.GL_CULL_FACE);
|
||||||
|
//gl.glCullFace(gl.GL_BACK);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.program = program,
|
||||||
|
.vao = vao,
|
||||||
|
.vbo = vbo,
|
||||||
|
.vboi = vboi
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const vss = @embedFile("shaders/main.vs");
|
||||||
|
const fss = @embedFile("shaders/main.fs");
|
||||||
|
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
program: gl.GLuint = 0,
|
||||||
|
vao: gl.GLuint = 0,
|
||||||
|
vbo: gl.GLuint = 0,
|
||||||
|
vboi: gl.GLuint = 0,
|
||||||
|
width: *u32 = undefined,
|
||||||
|
height: *u32 = undefined,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator, width: *u32, height: *u32) !Renderer {
|
||||||
|
// destructuring no worky :(
|
||||||
|
const initState = try opengl.init(allocator);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.program = initState.program,
|
||||||
|
.vao = initState.vao,
|
||||||
|
.vbo = initState.vbo,
|
||||||
|
.vboi = initState.vboi,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//pub fn renderGame(renderer: *const Renderer, allocator: std.mem.Allocator, game: *Game) !void {
|
||||||
|
// _ = game;
|
||||||
|
//
|
||||||
|
// var data = std.ArrayList(f32).init(allocator);
|
||||||
|
//
|
||||||
|
// try data.appendSlice(&[_]f32{ -0.8, -0.8, 1.0, 0.0, 0.0, -0.3, -0.8, 0.0, 1.0, 0.0, -0.8, -0.3, 0.0, 0.0, 1.0 });
|
||||||
|
//
|
||||||
|
// var acc: usize = 0;
|
||||||
|
// for (renderer.attribs.items) |attrib| {
|
||||||
|
// acc += @intCast(attrib.size);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// gl.glBindBuffer(gl.GL_ARRAY_BUFFER, renderer.vbo);
|
||||||
|
// gl.glBufferData(gl.GL_ARRAY_BUFFER, @as(gl.GLsizeiptr, @intCast(data.items.len)) * @sizeOf(f32), @ptrCast(data.items.ptr), gl.GL_STATIC_DRAW);
|
||||||
|
//
|
||||||
|
// for (renderer.attribs.items) |attrib| {
|
||||||
|
// gl.glEnableVertexAttribArray(attrib.loc);
|
||||||
|
// gl.glVertexAttribPointer(attrib.loc, attrib.size, attrib.type, attrib.normalized, @intCast(renderer.stride * @sizeOf(f32)), @ptrFromInt(attrib.offset));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// gl.glClearColor(0.8, 0.8, 0.8, 1.0);
|
||||||
|
// gl.glClear(gl.GL_COLOR_BUFFER_BIT);
|
||||||
|
//
|
||||||
|
// gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3);
|
||||||
|
//}
|
||||||
|
|
||||||
|
pub fn renderDraw(renderer: *Renderer, draw: *Draw) !void {
|
||||||
|
gl.glViewport(0, 0, @intCast(renderer.width.*), @intCast(renderer.height.*));
|
||||||
|
gl.glClearColor(1.0, 1.0, 1.0, 1.0);
|
||||||
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
gl.glUniform1i(@intCast(draw.getUniform("uInstanced").loc), 0);
|
||||||
|
|
||||||
|
inline for (@typeInfo(@TypeOf(draw.drawables.general)).@"struct".fields) |field| {
|
||||||
|
const drawables = @field(draw.drawables.general, field.name);
|
||||||
|
renderer.render(draw, drawables);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.glUniform1i(@intCast(draw.getUniform("uInstanced").loc), 1);
|
||||||
|
|
||||||
|
inline for (@typeInfo(@TypeOf(draw.drawables.instanced)).@"struct".fields) |field| {
|
||||||
|
const drawables = @field(draw.drawables.instanced, field.name);
|
||||||
|
renderer.renderInstanced(draw, drawables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(renderer: *Renderer, draw: *Draw, drawables: anytype) void {
|
||||||
|
if (drawables.data.items.len == 0) return;
|
||||||
|
|
||||||
|
switch (@TypeOf(drawables)) {
|
||||||
|
Draw.Drawables(gl.GL_TRIANGLES) => {
|
||||||
|
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, renderer.vbo);
|
||||||
|
gl.glBufferData(gl.GL_ARRAY_BUFFER, @as(gl.GLsizeiptr, @intCast(drawables.data.items.len)) * @sizeOf(f32), @ptrCast(drawables.data.items.ptr), gl.GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
for (draw.attribs.general.items) |attrib| {
|
||||||
|
gl.glEnableVertexAttribArray(attrib.data.loc);
|
||||||
|
gl.glVertexAttribPointer(attrib.data.loc, attrib.data.size, attrib.data.type, attrib.data.normalized, @intCast(Draw.Attribute.General.offset * @sizeOf(f32)), @ptrFromInt(attrib.data.offset * @sizeOf(f32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.glDrawArrays(gl.GL_TRIANGLES, 0, @intCast(drawables.data.items.len / Draw.Attribute.General.offset));
|
||||||
|
},
|
||||||
|
Draw.Drawables(gl.GL_TRIANGLE_FAN) => {
|
||||||
|
for (drawables.data.items) |data| {
|
||||||
|
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, renderer.vbo);
|
||||||
|
gl.glBufferData(gl.GL_ARRAY_BUFFER, @as(gl.GLsizeiptr, @intCast(data.items.len)) * @sizeOf(f32), @ptrCast(data.items.ptr), gl.GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
for (draw.attribs.general.items) |attrib| {
|
||||||
|
gl.glEnableVertexAttribArray(attrib.data.loc);
|
||||||
|
gl.glVertexAttribPointer(attrib.data.loc, attrib.data.size, attrib.data.type, attrib.data.normalized, @intCast(Draw.Attribute.General.offset * @sizeOf(f32)), @ptrFromInt(attrib.data.offset * @sizeOf(f32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.glDrawArrays(gl.GL_TRIANGLE_FAN, 0, @intCast(data.items.len / Draw.Attribute.General.offset));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderInstanced(renderer: *Renderer, draw: *Draw, drawables: anytype) void {
|
||||||
|
if (drawables.data.items.len == 0) return;
|
||||||
|
|
||||||
|
for (drawables.data.items, drawables.instanceData.items) |data, instanceData| {
|
||||||
|
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, renderer.vbo);
|
||||||
|
gl.glBufferData(gl.GL_ARRAY_BUFFER, @as(gl.GLsizeiptr, @intCast(data.items.len)) * @sizeOf(f32), @ptrCast(data.items.ptr), gl.GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
for (draw.attribs.general.items) |attrib| {
|
||||||
|
gl.glEnableVertexAttribArray(attrib.data.loc);
|
||||||
|
gl.glVertexAttribPointer(attrib.data.loc, attrib.data.size, attrib.data.type, attrib.data.normalized, @intCast(Draw.Attribute.General.offset * @sizeOf(f32)), @ptrFromInt(attrib.data.offset * @sizeOf(f32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, renderer.vboi);
|
||||||
|
gl.glBufferData(gl.GL_ARRAY_BUFFER, @as(gl.GLsizeiptr, @intCast(instanceData.items.len)) * @sizeOf(f32), @ptrCast(instanceData.items.ptr), gl.GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
for (draw.attribs.instanced.items) |attrib| {
|
||||||
|
gl.glEnableVertexAttribArray(attrib.data.loc);
|
||||||
|
gl.glVertexAttribPointer(attrib.data.loc, attrib.data.size, attrib.data.type, attrib.data.normalized, @intCast(Draw.Attribute.Instanced.offset * @sizeOf(f32)), @ptrFromInt(attrib.data.offset * @sizeOf(f32)));
|
||||||
|
gl.glVertexAttribDivisor(renderer.vao, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mode = if (@TypeOf(drawables) == Draw.DrawablesInstanced(gl.GL_TRIANGLES)) gl.GL_TRIANGLES else gl.GL_TRIANGLE_FAN;
|
||||||
|
|
||||||
|
gl.glDrawArraysInstanced(mode, 0, @intCast(data.items.len / Draw.Attribute.General.offset), @intCast(instanceData.items.len / Draw.Attribute.Instanced.offset));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec4 vColor;
|
||||||
|
out vec4 outColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outColor = vColor;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
uniform bool uInstanced;
|
||||||
|
uniform mat4 uTransform;
|
||||||
|
|
||||||
|
in vec3 aPos;
|
||||||
|
in vec3 aInstanceOffset;
|
||||||
|
in vec4 aColor;
|
||||||
|
out vec4 vColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (uInstanced) {
|
||||||
|
gl_Position = uTransform * vec4(aPos.xyz + aInstanceOffset.xyz, 1.0);
|
||||||
|
} else {
|
||||||
|
gl_Position = uTransform * vec4(aPos.xyz, 1.0);
|
||||||
|
}
|
||||||
|
vColor = aColor;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
const Transform = @This();
|
||||||
|
|
||||||
|
const math = @import("math.zig");
|
||||||
|
|
||||||
|
base: math.Mat4x4,
|
||||||
|
translation: math.Mat4x4,
|
||||||
|
scale: math.Mat4x4,
|
||||||
|
|
||||||
|
pub fn init() Transform {
|
||||||
|
return .{
|
||||||
|
.base = .id,
|
||||||
|
.translation = .id,
|
||||||
|
.scale = .id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransformType = enum {
|
||||||
|
orthographic,
|
||||||
|
translation,
|
||||||
|
scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn set(transform: *Transform, tag: TransformType, transformation: math.Mat4x4) void {
|
||||||
|
switch (tag) {
|
||||||
|
.orthographic => transform.base = transformation,
|
||||||
|
.translation => transform.translation = transformation,
|
||||||
|
.scale => transform.scale = transformation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(transform: *Transform, tag: TransformType, transformation: math.Mat4x4) void {
|
||||||
|
switch (tag) {
|
||||||
|
.orthographic => @compileError("you shouldn't add to orthographic transformation, use `.set` instead!"),
|
||||||
|
.translation => transform.translation.mult(transformation),
|
||||||
|
.scale => transform.scale.mult(transformation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(transform: *Transform) math.Mat4x4 {
|
||||||
|
const width: f32 = 2.0 / transform.base.data[0][0];
|
||||||
|
const height: f32 = 2.0 / transform.base.data[1][1];
|
||||||
|
|
||||||
|
var result = transform.base;
|
||||||
|
result.mult(math.Mat4x4.translation(math.Vec2 { width/2.0, height/2.0 }));
|
||||||
|
result.mult(transform.scale);
|
||||||
|
result.mult(math.Mat4x4.translation(math.Vec2 { -width/2.0, -height/2.0 }));
|
||||||
|
|
||||||
|
result.mult(transform.translation);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in New Issue