stable
Ivory 2024-09-20 18:00:50 -04:00
parent fb4b881def
commit 91251651b7
14 changed files with 446 additions and 36 deletions

View File

@ -20,7 +20,7 @@ window_size_offset_y: i32 = 0,
pub fn create() !*Camera {
const self = Camera {};
return try Scene.allocate(self);
return try Scene.EntityPool.allocate(self);
}
pub fn resize(self: *Camera, width: i32, height: i32) void {
@ -29,7 +29,7 @@ pub fn resize(self: *Camera, width: i32, height: i32) void {
}
pub fn destroy(self: *const Camera) void {
Scene.deallocate(self);
Scene.EntityPool.deallocate(self);
}
pub fn entity(self: *Camera) Entity {
@ -64,3 +64,7 @@ pub fn draw_sprite_i(self: *const Camera, sprite: *const Sprite, world_pos: Rect
pub fn set_focus(self: *Camera, f: Vec2f) void {
self.focus = f;
}
pub fn focus_layer(_: *const Camera) Layer {
return Layer.CAMERA;
}

View File

@ -30,7 +30,7 @@ pub fn with_opacity(self: *const Color, a: f32) Color {
.r = self.r,
.g = self.g,
.b = self.b,
.a = a
.a = a * self.a,
};
}

View File

@ -22,6 +22,9 @@ const Error = error {
AlreadyRunning
};
var mouse_x: i32 = 0;
var mouse_y: i32 = 0;
var hovered: bool = true;
var window: ?*c.GLFWwindow = null;
var current_scene: ?Scene = null;
var next_scene: ?Scene = null;
@ -29,18 +32,30 @@ var projection: Matrix4f = undefined;
fn glfw_on_error(err: c_int, desc_c: [*c]const u8) callconv(.C) void {
const desc: *const u8 = @ptrCast(desc_c);
std.log.err("[Engine:glfw_on_error] glfw error {x:0>8}: {s}", .{ err, desc });
std.log.err("[Engine:glfw_on_error] glfw error {x:0>8}: {s}\n", .{ err, desc });
}
fn glfw_on_resize (_: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
std.debug.print("[Engine:glfw_on_resize] {?}: {d} x {d}\n", .{ window, width, height });
fn glfw_on_resize(_: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
std.debug.print("[Engine:glfw_on_resize] {d} x {d}\n", .{ width, height });
c.glViewport(0, 0, width, height);
projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100);
shaders.set_projection_matrix(&projection);
if (current_scene != null) current_scene.?.resize(width, height);
}
fn get_size() struct { i32, i32 } {
fn glfw_on_mouse_move(_: ?*c.GLFWwindow, fx: f64, fy: f64) callconv(.C) void {
const x: i32 = @intFromFloat(fx);
const y: i32 = @intFromFloat(fy);
if (current_scene != null and hovered) {
current_scene.?.mouse_move(x, y);
}
}
fn glfw_on_mouse_enter(_: ?*c.GLFWwindow, mouse_within: c_int) callconv(.C) void {
hovered = mouse_within == c.GLFW_TRUE;
}
fn get_window_size() struct { i32, i32 } {
var width: c_int = undefined;
var height: c_int = undefined;
c.glfwGetFramebufferSize(window, @ptrCast(&width), @ptrCast(&height));
@ -88,6 +103,8 @@ pub fn setup() !void {
try assets.load();
_ = c.glfwSetWindowSizeCallback(window, glfw_on_resize);
_ = c.glfwSetCursorPosCallback(window, glfw_on_mouse_move);
_ = c.glfwSetCursorEnterCallback(window, glfw_on_mouse_enter);
const clearBrightness: f32 = 0.09;
c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0);
@ -99,7 +116,7 @@ pub fn setup() !void {
c.glDepthFunc(c.GL_LEQUAL);
c.glDepthMask(c.GL_TRUE);
const width, const height = get_size();
const width, const height = get_window_size();
c.glViewport(0, 0, width, height);
projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100);
shaders.set_projection_matrix(&projection);
@ -129,8 +146,12 @@ pub fn run() Error!void {
// start our scene
try current_scene.?.start();
// and send it the appropriate resize.
const width, const height = get_size();
const width, const height = get_window_size();
current_scene.?.resize(width, height);
if(hovered) {
current_scene.?.mouse_enter();
current_scene.?.mouse_move(mouse_x, mouse_y);
}
}
// setup for drawing
c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT);

View File

@ -20,6 +20,33 @@ _draw: *const fn(*const anyopaque) void,
_draw_opacity: *const fn(*const anyopaque) void,
_start: *const fn(*anyopaque, *Scene) void,
_resize: *const fn(*anyopaque, i32, i32) void,
_focusable: *const fn(*const anyopaque) bool,
_focus_layer: *const fn(*const anyopaque) Layer,
_focus_area: *const fn(*const anyopaque) Recti,
_mouse_move: *const fn(*anyopaque, i32, i32) void,
_focus: *const fn(*anyopaque) void,
_blur: *const fn(*anyopaque) void,
const ArrayTruthState = enum {
TRUE,
MIXED,
FALSE,
EMPTY,
};
fn bool_array_state(arr: []const bool) ArrayTruthState {
var any = false;
var all = true;
for (arr) |item| {
any = any or item;
all = all and item;
}
if (any and all) return .TRUE;
if (any and !all) return .MIXED;
if (!any and all) return .EMPTY;
if (!any and !all) return .FALSE;
return .FALSE;
}
pub fn init(ptr: anytype, options: EntityOptions) Entity {
std.debug.print("[Entity:init] creating entity {s} with tag \"{s}\"\n", .{
@ -41,12 +68,75 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
else => @compileError("Entity pointer must point to a struct or opaque type"),
}
comptime var input_ready = ArrayTruthState.FALSE;
comptime {
input_ready = bool_array_state(&[_]bool {
@hasDecl(type_info.Pointer.child, "focus_area"),
@hasDecl(type_info.Pointer.child, "focus_layer"),
@hasDecl(type_info.Pointer.child, "focus"),
@hasDecl(type_info.Pointer.child, "blur"),
@hasDecl(type_info.Pointer.child, "mouse_move"),
});
switch(input_ready) {
.TRUE => {},
.FALSE => {},
.EMPTY => {},
.MIXED => @compileError(@typeName(type_info.Pointer.child) ++ " does not defien all methods required for input focus"),
}
}
// @compileLog(MutablePointer);
// @compileLog(input_ready);
// also take the const pointer type, as we will use both mutable an immutable pointers.
// some entities mutate and some do not
const ConstPointer = *const @typeInfo(MutablePointer).Pointer.child;
// @compileLog("const pointer type: " ++ @typeName(ConstPointer));
const gen = struct {
pub fn _focusable(_: *const anyopaque) bool {
return input_ready == ArrayTruthState.TRUE;
}
pub fn _focus_layer(pointer: *const anyopaque) Layer {
const self: ConstPointer = @ptrCast(@alignCast(pointer));
switch (input_ready) {
.TRUE => return type_info.Pointer.child.focus_layer(self),
else => unreachable,
}
}
pub fn _focus_area(pointer: *const anyopaque) Recti {
const self: ConstPointer = @ptrCast(@alignCast(pointer));
switch(input_ready) {
.TRUE => return type_info.Pointer.child.focus_area(self),
else => unreachable,
}
}
pub fn _mouse_move(pointer: *anyopaque, x: i32, y: i32) void {
const self: MutablePointer = @ptrCast(@alignCast(pointer));
switch(input_ready) {
.TRUE => type_info.Pointer.child.mouse_move(self, x, y),
else => unreachable,
}
}
pub fn _focus(pointer: *anyopaque) void {
const self: MutablePointer = @ptrCast(@alignCast(pointer));
switch(input_ready) {
.TRUE => type_info.Pointer.child.focus(self),
else => unreachable,
}
}
pub fn _blur(pointer: *anyopaque) void {
const self: MutablePointer = @ptrCast(@alignCast(pointer));
switch(input_ready) {
.TRUE => type_info.Pointer.child.blur(self),
else => unreachable,
}
}
pub fn _name(_: *const anyopaque) []const u8 {
// const self: ConstPointer = @ptrCast(@alignCast(pointer));
return type_info.Pointer.child;
}
pub fn _destroy(pointer: *const anyopaque) void {
const self: ConstPointer = @ptrCast(@alignCast(pointer));
type_info.Pointer.child.destroy(self);
@ -83,7 +173,7 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
}
};
return .{
return Entity {
.ptr = ptr,
.tag = options.tag,
._destroy = gen._destroy,
@ -92,6 +182,12 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
._update = gen._update,
._draw = gen._draw,
._draw_opacity = gen._draw_opacity,
._focusable = gen._focusable,
._focus_layer = gen._focus_layer,
._focus_area = gen._focus_area,
._mouse_move = gen._mouse_move,
._focus = gen._focus,
._blur = gen._blur,
};
}
@ -119,6 +215,33 @@ pub fn resize(self: *const Entity, width: i32, height: i32) void {
self._resize(self.ptr, width, height);
}
pub fn focus(self: *const Entity) void {
self._focus(self.ptr);
}
pub fn blur(self: *const Entity) void {
self._blur(self.ptr);
}
// -=- focus related items
// defines if the entity has functions to define its focus area
pub fn focusable(self: *const Entity) bool {
return self._focusable(self.ptr);
}
pub fn focus_layer(self: *const Entity) Layer {
return self._focus_layer(self.ptr);
}
pub fn focus_area(self: *const Entity) Recti {
return self._focus_area(self.ptr);
}
pub fn mouse_move(self: *const Entity, x: i32, y: i32) void {
return self._mouse_move(self.ptr, x, y);
}
pub fn allocate_array(comptime T: type, size: usize, default: T) ![]T {
const ptr = try std.heap.page_allocator.alloc(T, size);
for (0..size) |idx| {

View File

@ -10,3 +10,5 @@ fn layer(z_index: i32) Layer {
pub const FLOOR = layer(0);
pub const ENTITIES = layer(1);
pub const CAMERA = layer(2);
pub const SELECTION = layer(3);

View File

@ -21,7 +21,7 @@ pub fn random() !*Pawn {
@intFromFloat(@floor(prng.random().float(f32) * @as(f32, @floatFromInt(Terrain.CHUNK_SIZE)))),
@intFromFloat(@floor(prng.random().float(f32) * @as(f32, @floatFromInt(Terrain.CHUNK_SIZE))))
);
return try Scene.allocate(self);
return try Scene.EntityPool.allocate(self);
}
pub fn create() !*Pawn {
@ -34,7 +34,7 @@ pub fn start(self: *Pawn, scene: *const Scene) void {
}
pub fn destroy(self: *const Pawn) void {
Scene.deallocate(self);
Scene.EntityPool.deallocate(self);
}
pub fn entity(self: *Pawn) Entity {

View File

@ -6,6 +6,7 @@ const Layer = @import("Layer.zig");
const Color = @import("Color.zig");
const Entity = @import("Entity.zig");
const Terrain = @import("Terrain.zig");
const Vec2i = @import("geometry/Vec2i.zig");
const Tag = @import("Tag.zig");
const std = @import("std");
const ArrayList = std.ArrayList;
@ -13,12 +14,17 @@ const AutoHashMap = std.AutoHashMap;
// cache and list bothe store entities. treat entities as fat pointers.
// Pointers into an arraylist WILL become invalid.
focus_items: ArrayList(Entity) = undefined,
entities: ArrayList(Entity) = undefined,
tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined,
mouse_inside: bool = false,
current_focus_item: ?Entity = null,
mouse_pos: ?Vec2i = null,
pub fn create() Scene {
var self = Scene {};
self.entities = ArrayList(Entity).init(std.heap.page_allocator);
self.focus_items = ArrayList(Entity).init(std.heap.page_allocator);
self.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator);
return self;
}
@ -60,7 +66,7 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void {
@compileError(
"Cannot add to scene type " ++
@typeName(instance_ptr_type) ++
"Must be a mutable pointer\n"
". Must be a mutable pointer\n"
);
if (instance_ptr_type_info.Pointer.size != .One)
@compileError("Pointer size must be One");
@ -78,12 +84,55 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void {
if (entity.tag.id != Tag.NONE.id) {
try self.tagged_entities.put(entity.tag.id, entity);
}
if (entity.focusable()) {
try self.focus_items.append(entity);
}
}
fn entity_layer_less_than(_: void, lhs: Entity, rhs: Entity) bool {
return lhs.focus_layer().z_index < rhs.focus_layer().z_index;
}
pub fn update(self: *Scene, dt: f32) void {
for (self.entities.items) |entity| {
entity.update(dt);
}
// this whole operation is wildly stupid and could be amde more efficient.
std.mem.sort(Entity, self.focus_items.items, {}, entity_layer_less_than);
if (self.mouse_pos) |mouse| {
var top_hovered: ?Entity = null;
for (self.focus_items.items) |item| {
if (item.focusable() and item.focus_area().contains(mouse)) {
top_hovered = item;
continue;
}
}
if (top_hovered != null) {
if (self.current_focus_item != null) {
// if we're changing hover target from something to something
if (self.current_focus_item.?.ptr != top_hovered.?.ptr) {
self.current_focus_item.?.blur();
self.current_focus_item = top_hovered;
self.current_focus_item.?.focus();
}
} else {
// we are hovering something, and werent before.
self.current_focus_item = top_hovered.?;
self.current_focus_item.?.focus();
}
} else if (self.current_focus_item != null) {
// here we were hovering something, but now are not.
self.current_focus_item.?.blur();
self.current_focus_item = null;
}
} else if (self.current_focus_item != null) {
self.current_focus_item.?.blur();
self.current_focus_item = null;
}
}
pub fn draw(self: *Scene) void {
@ -93,6 +142,9 @@ pub fn draw(self: *Scene) void {
for (self.entities.items) |entity| {
entity.draw_opacity();
}
for (self.focus_items.items) |item| {
item.focus_area().draw(item.focus_layer(), Color.WHITE.with_opacity(0.3));
}
}
pub fn resize(self: *Scene, width: i32, height: i32) void {
@ -101,16 +153,35 @@ pub fn resize(self: *Scene, width: i32, height: i32) void {
}
}
// --- helpfer functions for entities themselves
pub fn mouse_move(self: *Scene, x: i32, y: i32) void {
// std.debug.print("[Scene:mouse_move] {} {} {}\n", .{ self, x, y });
self.mouse_pos = Vec2i { .x = x, .y = y };
self.mouse_inside = true;
for (self.focus_items.items) |item| {
item.mouse_move(x, y);
}
}
// an entity can choose to allocate itself however it pleases, but these
// are good default functions that just throw things on the heap.
pub fn allocate(instance: anytype) !*@TypeOf(instance) {
pub fn mouse_enter(self: *Scene) void {
self.mouse_inside = true;
}
pub fn mouse_leave(self: *Scene) void {
self.mouse_inside = false;
self.mouse_pos = null;
}
// --- helpfer functions for entities themselves
pub const EntityPool = struct {
// an entity can choose to allocate itself however it pleases, but these
// are good default functions that just throw things on the heap.
pub fn allocate(instance: anytype) !*@TypeOf(instance) {
const ptr = try std.heap.page_allocator.create(@TypeOf(instance));
ptr.* = instance;
return ptr;
}
}
pub fn deallocate(instance: anytype) void {
pub fn deallocate(instance: anytype) void {
std.heap.page_allocator.destroy(instance);
}
}
};

67
src/Selection.zig 100644
View File

@ -0,0 +1,67 @@
const Selection = @This();
const Scene = @import("./Scene.zig");
const Entity = @import("./Entity.zig");
const Tag = @import("./Tag.zig");
const Layer = @import("./Layer.zig");
const Engine = @import("./Engine.zig");
const Recti = @import("./geometry/Recti.zig");
const std = @import("std");
const TAG = Tag.create(Selection);
selection: std.ArrayList(Selectable),
focused: bool = false,
pub fn create() !*Selection {
return try Scene.EntityPool.allocate(Selection {
.selection = std.ArrayList(Selectable).init(std.heap.page_allocator),
});
}
pub fn entity(self: *Selection) Entity {
return Entity.init(self, .{
.tag = TAG,
});
}
pub fn destroy(self: *const Selection) void {
self.selection.deinit();
Scene.EntityPool.deallocate(self);
}
pub fn focus_area(_: *const Selection) Recti {
// return Recti.from_xywh(0, 0, std.math.maxInt(i16), std.math.maxInt(i16));
return Recti.from_xywh(0, 0, 300, 1000);
}
pub fn focus_layer(_: *const Selection) Layer {
return Layer.ENTITIES;
}
pub fn focus(self: *Selection) void {
self.focused = true;
std.debug.print("[Selection:focus] Focused\n", .{});
}
pub fn blur(self: *Selection) void {
self.focused = false;
std.debug.print("[Selection:focus] Blurred\n", .{});
}
pub fn mouse_move(_: *Selection, _: i32, _: i32) void {
// std.debug.print("[Selection:mouse_move] {}, {}\n", .{ x, y });
}
const Selectable = struct {
ptr: *anyopaque,
pub fn init(item: anytype) Selectable {
// const target_type = @typeInfo(@TypeOf(item));
// const MutablePointer = @typeInfo(@TypeOf(item));
return Selectable {
.ptr = item
};
}
};

View File

@ -26,6 +26,7 @@ pub fn create(tex: *const Texture, rect: Recti) Sprite {
pub fn draw(self: *const Sprite, screen_pos: Recti, layer: Layer, color: Color) void {
self.texture.bind(c.GL_TEXTURE0);
shaders.enable_textures();
c.glBegin(c.GL_QUADS);
{
c.glVertexAttrib2f(shaders.TEXCOORD_ATTRIBUTE_ID, self.texture_area.a.x, self.texture_area.a.y);

View File

@ -107,7 +107,7 @@ fn generate_tiles(self: *Terrain) void {
}
pub fn create(seed: i64) !*Terrain {
var self: *Terrain = try Scene.allocate(Terrain {
var self: *Terrain = try Scene.EntityPool.allocate(Terrain {
.tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL),
.growth_noise = Noise.create(seed, 10, 0.03),
.texture_noise = Noise.create(seed + 1, 1, 3),
@ -134,7 +134,7 @@ pub fn destroy(self: *const Terrain) void {
self.stone_color_noise.destroy();
Entity.free_array(self.tiles);
Scene.deallocate(self);
Scene.EntityPool.deallocate(self);
}
fn chance(percent: f32) bool {
@ -173,6 +173,7 @@ pub fn update(self: *Terrain, dt: f32) void {
}
}
pub fn draw(self: *const Terrain) void {
for(0..CHUNK_LENGTH) |idx| {
const tile = self.tiles[idx];

View File

@ -2,6 +2,10 @@ const Vec2i = @import("./Vec2i.zig");
const Vec2f = @import("./Vec2f.zig");
const Rectf = @import("./Rectf.zig");
const std = @import("std");
const c = @cImport({
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
const Recti = @This();
@ -40,6 +44,13 @@ pub fn from_ab(a: Vec2i, b: Vec2i) Recti {
};
}
pub fn contains(self: *const Recti, point: Vec2i) bool {
return point.x >= self.a.x
and point.x < self.b.x
and point.y >= self.a.y
and point.y < self.b.y;
}
pub fn to_rectf(self: *const Recti) Rectf {
return Rectf.from_xywh(
@intCast(self.x),
@ -57,3 +68,72 @@ pub fn scalef(self: *const Recti, scale: Vec2f) Rectf {
@as(f32, @floatFromInt(self.h)) / scale.y,
);
}
const Color = @import("../Color.zig");
const Layer = @import("../Layer.zig");
const shaders = @import("../shaders.zig");
const assets = @import("../assets.zig");
pub fn draw(self: *const Recti, layer: Layer, color: Color) void {
self.draw_filled(layer, color.with_opacity(0.2));
self.draw_outline(layer, color);
}
pub fn draw_filled(self: *const Recti, layer: Layer, color: Color) void {
shaders.disable_textures();
c.glBegin(c.GL_QUADS);
{
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.a.x, self.a.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.b.x, self.a.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.b.x, self.b.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.a.x, self.b.y, layer.z_index);
}
c.glEnd();
}
pub fn draw_outline(self: *const Recti, layer: Layer, color: Color) void {
shaders.disable_textures();
c.glBegin(c.GL_LINES);
{
// top
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.a.x, self.a.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.b.x, self.a.y, layer.z_index);
// right
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.b.x, self.a.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.b.x, self.b.y, layer.z_index);
// bottom
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.b.x, self.b.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.a.x, self.b.y, layer.z_index);
// left
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.a.x, self.b.y, layer.z_index);
c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a);
c.glVertex3i(self.a.x, self.a.y, layer.z_index);
}
c.glEnd();
}

8
src/gui.zig 100644
View File

@ -0,0 +1,8 @@
const Button = struct {
}

View File

@ -2,7 +2,7 @@ const Scene = @import("Scene.zig");
const Terrain = @import("Terrain.zig");
const Pawn = @import("Pawn.zig");
const Camera = @import("Camera.zig");
const Selection = @import("Selection.zig");
pub fn game() !Scene {
var scene = Scene.create();
@ -10,6 +10,7 @@ pub fn game() !Scene {
// second try is for allocating terrain on the heap...
try scene.add(try Camera.create());
try scene.add(try Terrain.create(123));
try scene.add(try Selection.create());
for (0..5) |_| {
try scene.add(try Pawn.random());
}

View File

@ -33,6 +33,7 @@ const VERT_SHADER_TEXT =
const FRAG_SHADER_TEXT =
\\ #version 330
\\ uniform sampler2D uTexture0;
\\ uniform bool uNoTexture;
\\
\\ in vec4 vColor;
\\ in vec2 vTexCoord;
@ -40,15 +41,19 @@ const FRAG_SHADER_TEXT =
\\ out vec4 color;
\\
\\ void main() {
\\ if (!uNoTexture) {
\\ 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;
\\ } else {
\\ color = vColor;
\\ // color = vec4(1, 1, 1, 1);
\\ // color = vec4(vTexCoord.x, vTexCoord.y, 0, 1);
\\ }
\\ }
;
pub const POSITION_ATTRIBUTE_ID = 0;
@ -56,12 +61,13 @@ pub const COLOR_ATTRIBUTE_ID = 1;
pub const TEXCOORD_ATTRIBUTE_ID = 2;
var flat: Shader = undefined;
var active_shader: *const Shader = undefined;
var active_shader: *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();
flat.enable_textures();
return;
}
@ -70,20 +76,31 @@ pub fn set_projection_matrix(matrix: *const Matrix4f) void {
active_shader.set_projection_matrix(matrix);
}
pub fn enable_textures() void {
active_shader.enable_textures();
}
pub fn disable_textures() void {
active_shader.disable_textures();
}
const Shader = struct {
vertex_shader: c.GLuint,
fragment_shader: c.GLuint,
program_handle: c.GLuint,
textures_enabled: bool = false,
const Error = error {
CompilationFailed
};
fn enable(self: *const Shader) void {
fn enable(self: *Shader) void {
c.glUseProgram(self.program_handle);
active_shader = self;
c.glUniform1i(c.glGetUniformLocation(self.program_handle, "uTexture0"), 0);
self.enable_textures();
}
fn compile(vert_shader_text: []const u8, frag_shader_text: []const u8) !Shader {
@ -148,6 +165,20 @@ const Shader = struct {
const attribute_id: c.GLint = self.get_uniform_id("uProjection");
c.glUniformMatrix4fv(attribute_id, 1, c.GL_FALSE, @ptrCast(&matrix.values));
}
fn enable_textures(self: *Shader) void {
if (self.textures_enabled) return;
self.textures_enabled = true;
const attr_id: c.GLint = self.get_uniform_id("uNoTexture");
c.glUniform1i(attr_id, c.GL_FALSE);
}
fn disable_textures(self: *Shader) void {
if (!self.textures_enabled) return;
self.textures_enabled = false;
const attr_id: c.GLint = self.get_uniform_id("uNoTexture");
c.glUniform1i(attr_id, c.GL_TRUE);
}
};