Add an abstraction for the Graphics pipeline (#1405)

* Add the vulkan headers from the new Cubyz-libs version

* Add a GraphicsPipeline abstraction that's based on Vulkan's pipeline parameters.

This will help get rid of all the glEnable implicit state, and it will make #102 easier.

* I'm leaving out the viewport for now, it seems that it would make migration difficult

* Small fixes and rename graphics.GraphicsPipeline to just graphics.Pipeline

* Pipelinify the UI shaders

* Fix formatting

* Pipelines everywhere

* Fix the crosshair and remove remaining glEnable and related calls

* fix format

* How many more things does it need to clear the thing correctly?

* Fix selection

* Fix bloom
This commit is contained in:
IntegratedQuantum 2025-05-08 19:37:26 +02:00 committed by GitHub
parent 2991febe3f
commit c2b03ff437
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 753 additions and 223 deletions

View File

@ -9,37 +9,37 @@
.lazy = true,
},
.cubyz_deps_headers = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_headers.tar.gz",
.hash = "N-V-__8AADG8MwD98GEQXXNawTiaQfCUrV7vK4ldKsWhfwvI",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_headers.tar.gz",
.hash = "N-V-__8AALzzRgA7omQPNlE657Y60UmkCdNdXsMES8VvMC6D",
.lazy = true,
},
.cubyz_deps_aarch64_macos = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_aarch64-macos-none.tar.gz",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_aarch64-macos-none.tar.gz",
.hash = "N-V-__8AANwbIwJ6ad1lQFbXKAwT6VwXJY4oLyjZwOfz4LUx",
.lazy = true,
},
.cubyz_deps_aarch64_linux = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_aarch64-linux-musl.tar.gz",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_aarch64-linux-musl.tar.gz",
.hash = "N-V-__8AAG6FdQLSQL-LHeF5o7k3vRCnIdl0n2I0R0UO18K-",
.lazy = true,
},
.cubyz_deps_aarch64_windows = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_aarch64-windows-gnu.tar.gz",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_aarch64-windows-gnu.tar.gz",
.hash = "N-V-__8AANCHkQIGwA8SWUxMHaxnVnkTwBO0yei3HwBLOd2T",
.lazy = true,
},
.cubyz_deps_x86_64_macos = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_x86_64-macos-none.tar.gz",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_x86_64-macos-none.tar.gz",
.hash = "N-V-__8AAIh5HAKP8i_TwkyD-6H_EL31J6Ereu9Kdx83J7Ks",
.lazy = true,
},
.cubyz_deps_x86_64_linux = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_x86_64-linux-musl.tar.gz",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_x86_64-linux-musl.tar.gz",
.hash = "N-V-__8AAAy-cwLTBDz5YQSt3h-PdE4Cm-NDI9RGnrAz_onE",
.lazy = true,
},
.cubyz_deps_x86_64_windows = .{
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/4/cubyz_deps_x86_64-windows-gnu.tar.gz",
.url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/5/cubyz_deps_x86_64-windows-gnu.tar.gz",
.hash = "N-V-__8AAMzGtQKYP6AdoTC84UjbvypxwtagkWBMqrPc8Uhx",
.lazy = true,
},

View File

@ -94,13 +94,21 @@ pub const ClientEntityManager = struct {
var modelBuffer: main.graphics.SSBO = undefined;
var modelSize: c_int = 0;
var modelTexture: main.graphics.Texture = undefined;
var shader: graphics.Shader = undefined; // Entities are sometimes small and sometimes big. Therefor it would mean a lot of work to still use smooth lighting. Therefor the non-smooth shader is used for those.
var pipeline: graphics.Pipeline = undefined; // Entities are sometimes small and sometimes big. Therefor it would mean a lot of work to still use smooth lighting. Therefor the non-smooth shader is used for those.
pub var entities: main.utils.VirtualList(ClientEntity, 1 << 20) = undefined;
pub var mutex: std.Thread.Mutex = .{};
pub fn init() void {
entities = .init();
shader = graphics.Shader.initAndGetUniforms("assets/cubyz/shaders/entity_vertex.vs", "assets/cubyz/shaders/entity_fragment.fs", "", &uniforms);
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/entity_vertex.vs",
"assets/cubyz/shaders/entity_fragment.fs",
"",
&uniforms,
.{},
.{.depthTest = true},
.{.attachments = &.{.alphaBlending}},
);
modelTexture = main.graphics.Texture.initFromFile("assets/cubyz/entity/textures/snail_player.png");
const modelFile = main.files.read(main.stackAllocator, "assets/cubyz/entity/models/snail_player.obj") catch |err| blk: {
@ -120,7 +128,7 @@ pub const ClientEntityManager = struct {
ent.deinit(main.globalAllocator);
}
entities.deinit();
shader.deinit();
pipeline.deinit();
}
pub fn clear() void {
@ -170,7 +178,7 @@ pub const ClientEntityManager = struct {
mutex.lock();
defer mutex.unlock();
update();
shader.bind();
pipeline.bind(null);
c.glBindVertexArray(main.renderer.chunk_meshing.vao);
c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix));
modelTexture.bindTo(0);

View File

@ -31,6 +31,7 @@ const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const c = @cImport({
@cInclude("glad/glad.h");
@cInclude("vulkan/vulkan.h");
});
pub const stb_image = @cImport({
@ -102,23 +103,29 @@ pub const draw = struct { // MARK: draw
}
newClip[2] = @max(newClip[2], 0);
newClip[3] = @max(newClip[3], 0);
} else {
c.glEnable(c.GL_SCISSOR_TEST);
}
c.glScissor(newClip[0], newClip[1], newClip[2], newClip[3]);
const oldClip = clip;
clip = newClip;
return oldClip;
}
pub fn getScissor() ?c.VkRect2D {
const clipRect = clip orelse return null;
return .{
.offset = .{
.x = clipRect[0],
.y = clipRect[1],
},
.extent = .{
.width = @intCast(clipRect[2]),
.height = @intCast(clipRect[3]),
},
};
}
/// Should be used to restore the old clip when leaving the render function.
pub fn restoreClip(previousClip: ?Vec4i) void {
clip = previousClip;
if(clip) |clipRef| {
c.glScissor(clipRef[0], clipRef[1], clipRef[2], clipRef[3]);
} else {
c.glDisable(c.GL_SCISSOR_TEST);
}
}
// ----------------------------------------------------------------------------
@ -129,12 +136,20 @@ pub const draw = struct { // MARK: draw
size: c_int,
rectColor: c_int,
} = undefined;
var rectShader: Shader = undefined;
var rectPipeline: Pipeline = undefined;
pub var rectVAO: c_uint = undefined;
var rectVBO: c_uint = undefined;
fn initRect() void {
rectShader = Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Rect.vs", "assets/cubyz/shaders/graphics/Rect.fs", "", &rectUniforms);
rectPipeline = Pipeline.init(
"assets/cubyz/shaders/graphics/Rect.vs",
"assets/cubyz/shaders/graphics/Rect.fs",
"",
&rectUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
const rawData = [_]f32{
0, 0,
0, 1,
@ -152,7 +167,7 @@ pub const draw = struct { // MARK: draw
}
fn deinitRect() void {
rectShader.deinit();
rectPipeline.deinit();
c.glDeleteVertexArrays(1, &rectVAO);
c.glDeleteBuffers(1, &rectVBO);
}
@ -164,7 +179,7 @@ pub const draw = struct { // MARK: draw
pos += translation;
dim *= @splat(scale);
rectShader.bind();
rectPipeline.bind(getScissor());
c.glUniform2f(rectUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(rectUniforms.start, pos[0], pos[1]);
@ -184,12 +199,20 @@ pub const draw = struct { // MARK: draw
rectColor: c_int,
lineWidth: c_int,
} = undefined;
var rectBorderShader: Shader = undefined;
var rectBorderPipeline: Pipeline = undefined;
var rectBorderVAO: c_uint = undefined;
var rectBorderVBO: c_uint = undefined;
fn initRectBorder() void {
rectBorderShader = Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/RectBorder.vs", "assets/cubyz/shaders/graphics/RectBorder.fs", "", &rectBorderUniforms);
rectBorderPipeline = Pipeline.init(
"assets/cubyz/shaders/graphics/RectBorder.vs",
"assets/cubyz/shaders/graphics/RectBorder.fs",
"",
&rectBorderUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
const rawData = [_]f32{
0, 0, 0, 0,
0, 0, 1, 1,
@ -213,7 +236,7 @@ pub const draw = struct { // MARK: draw
}
fn deinitRectBorder() void {
rectBorderShader.deinit();
rectBorderPipeline.deinit();
c.glDeleteVertexArrays(1, &rectBorderVAO);
c.glDeleteBuffers(1, &rectBorderVBO);
}
@ -227,7 +250,7 @@ pub const draw = struct { // MARK: draw
dim *= @splat(scale);
width *= scale;
rectBorderShader.bind();
rectBorderPipeline.bind(getScissor());
c.glUniform2f(rectBorderUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(rectBorderUniforms.start, pos[0], pos[1]);
@ -247,12 +270,20 @@ pub const draw = struct { // MARK: draw
direction: c_int,
lineColor: c_int,
} = undefined;
var lineShader: Shader = undefined;
var linePipeline: Pipeline = undefined;
var lineVAO: c_uint = undefined;
var lineVBO: c_uint = undefined;
fn initLine() void {
lineShader = Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Line.vs", "assets/cubyz/shaders/graphics/Line.fs", "", &lineUniforms);
linePipeline = Pipeline.init(
"assets/cubyz/shaders/graphics/Line.vs",
"assets/cubyz/shaders/graphics/Line.fs",
"",
&lineUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
const rawData = [_]f32{
0, 0,
1, 1,
@ -268,7 +299,7 @@ pub const draw = struct { // MARK: draw
}
fn deinitLine() void {
lineShader.deinit();
linePipeline.deinit();
c.glDeleteVertexArrays(1, &lineVAO);
c.glDeleteBuffers(1, &lineVBO);
}
@ -281,7 +312,7 @@ pub const draw = struct { // MARK: draw
pos2 *= @splat(scale);
pos2 += translation;
lineShader.bind();
linePipeline.bind(getScissor());
c.glUniform2f(lineUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(lineUniforms.start, pos1[0], pos1[1]);
@ -327,7 +358,7 @@ pub const draw = struct { // MARK: draw
pos += translation;
dim *= @splat(scale);
lineShader.bind();
linePipeline.bind(getScissor());
c.glUniform2f(lineUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(lineUniforms.start, pos[0], pos[1]); // Move the coordinates, so they are in the center of a pixel.
@ -346,12 +377,20 @@ pub const draw = struct { // MARK: draw
radius: c_int,
circleColor: c_int,
} = undefined;
var circleShader: Shader = undefined;
var circlePipeline: Pipeline = undefined;
var circleVAO: c_uint = undefined;
var circleVBO: c_uint = undefined;
fn initCircle() void {
circleShader = Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Circle.vs", "assets/cubyz/shaders/graphics/Circle.fs", "", &circleUniforms);
circlePipeline = Pipeline.init(
"assets/cubyz/shaders/graphics/Circle.vs",
"assets/cubyz/shaders/graphics/Circle.fs",
"",
&circleUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
const rawData = [_]f32{
-1, -1,
-1, 1,
@ -369,7 +408,7 @@ pub const draw = struct { // MARK: draw
}
fn deinitCircle() void {
circleShader.deinit();
circlePipeline.deinit();
c.glDeleteVertexArrays(1, &circleVAO);
c.glDeleteBuffers(1, &circleVBO);
}
@ -380,7 +419,7 @@ pub const draw = struct { // MARK: draw
center *= @splat(scale);
center += translation;
radius *= scale;
circleShader.bind();
circlePipeline.bind(getScissor());
c.glUniform2f(circleUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(circleUniforms.center, center[0], center[1]); // Move the coordinates, so they are in the center of a pixel.
@ -402,36 +441,28 @@ pub const draw = struct { // MARK: draw
uvOffset: c_int,
uvDim: c_int,
} = undefined;
var imageShader: Shader = undefined;
var imagePipeline: Pipeline = undefined;
fn initImage() void {
imageShader = Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Image.vs", "assets/cubyz/shaders/graphics/Image.fs", "", &imageUniforms);
imagePipeline = Pipeline.init(
"assets/cubyz/shaders/graphics/Image.vs",
"assets/cubyz/shaders/graphics/Image.fs",
"",
&imageUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
}
fn deinitImage() void {
imageShader.deinit();
imagePipeline.deinit();
}
pub fn boundImage(_pos: Vec2f, _dim: Vec2f) void {
var pos = _pos;
var dim = _dim;
pos *= @splat(scale);
pos += translation;
dim *= @splat(scale);
pos = @floor(pos);
dim = @ceil(dim);
imagePipeline.bind(getScissor());
imageShader.bind();
c.glUniform2f(imageUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(imageUniforms.start, pos[0], pos[1]);
c.glUniform2f(imageUniforms.size, dim[0], dim[1]);
c.glUniform1i(imageUniforms.color, @bitCast(color));
c.glUniform2f(imageUniforms.uvOffset, 0, 0);
c.glUniform2f(imageUniforms.uvDim, 1, 1);
c.glBindVertexArray(rectVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
customShadedImage(&imageUniforms, _pos, _dim);
}
pub fn boundSubImage(_pos: Vec2f, _dim: Vec2f, uvOffset: Vec2f, uvDim: Vec2f) void {
@ -443,7 +474,7 @@ pub const draw = struct { // MARK: draw
pos = @floor(pos);
dim = @ceil(dim);
imageShader.bind();
imagePipeline.bind(getScissor());
c.glUniform2f(imageUniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(imageUniforms.start, pos[0], pos[1]);
@ -456,6 +487,26 @@ pub const draw = struct { // MARK: draw
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
pub fn customShadedImage(uniforms: anytype, _pos: Vec2f, _dim: Vec2f) void {
var pos = _pos;
var dim = _dim;
pos *= @splat(scale);
pos += translation;
dim *= @splat(scale);
pos = @floor(pos);
dim = @ceil(dim);
c.glUniform2f(uniforms.screen, @floatFromInt(Window.width), @floatFromInt(Window.height));
c.glUniform2f(uniforms.start, pos[0], pos[1]);
c.glUniform2f(uniforms.size, dim[0], dim[1]);
c.glUniform1i(uniforms.color, @bitCast(color));
c.glUniform2f(uniforms.uvOffset, 0, 0);
c.glUniform2f(uniforms.uvDim, 1, 1);
c.glBindVertexArray(rectVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
// ----------------------------------------------------------------------------
// MARK: customShadedRect()
@ -928,7 +979,7 @@ pub const TextBuffer = struct { // MARK: TextBuffer
defer draw.restoreScale(oldScale);
var x: f32 = 0;
var y: f32 = 0;
TextRendering.shader.bind();
TextRendering.pipeline.bind(draw.getScissor());
c.glUniform2f(TextRendering.uniforms.scene, @floatFromInt(main.Window.width), @floatFromInt(main.Window.height));
c.glUniform1f(TextRendering.uniforms.ratio, draw.scale);
c.glUniform1f(TextRendering.uniforms.alpha, @as(f32, @floatFromInt(draw.color >> 24))/255.0);
@ -994,7 +1045,7 @@ pub const TextBuffer = struct { // MARK: TextBuffer
defer draw.restoreScale(oldScale);
var x: f32 = 0;
var y: f32 = 0;
TextRendering.shader.bind();
TextRendering.pipeline.bind(draw.getScissor());
c.glUniform2f(TextRendering.uniforms.scene, @floatFromInt(main.Window.width), @floatFromInt(main.Window.height));
c.glUniform1f(TextRendering.uniforms.ratio, draw.scale);
c.glUniform1f(TextRendering.uniforms.alpha, @as(f32, @floatFromInt(draw.color >> 24))/255.0);
@ -1051,7 +1102,7 @@ const TextRendering = struct { // MARK: TextRendering
bearing: Vec2i,
advance: f32,
};
var shader: Shader = undefined;
var pipeline: Pipeline = undefined;
var uniforms: struct {
texture_rect: c_int,
scene: c_int,
@ -1081,9 +1132,17 @@ const TextRendering = struct { // MARK: TextRendering
}
fn init() !void {
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Text.vs", "assets/cubyz/shaders/graphics/Text.fs", "", &uniforms);
shader.bind();
errdefer shader.deinit();
pipeline = Pipeline.init(
"assets/cubyz/shaders/graphics/Text.vs",
"assets/cubyz/shaders/graphics/Text.fs",
"",
&uniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
pipeline.bind(null);
errdefer pipeline.deinit();
c.glUniform1f(uniforms.alpha, 1.0);
c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight));
try ftError(hbft.FT_Init_FreeType(&freetypeLib));
@ -1110,7 +1169,7 @@ const TextRendering = struct { // MARK: TextRendering
}
fn deinit() void {
shader.deinit();
pipeline.deinit();
ftError(hbft.FT_Done_FreeType(freetypeLib)) catch {};
glyphMapping.deinit();
glyphData.deinit();
@ -1127,7 +1186,7 @@ const TextRendering = struct { // MARK: TextRendering
c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0]);
c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, newWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null);
c.glCopyImageSubData(glyphTexture[1], c.GL_TEXTURE_2D, 0, 0, 0, 0, glyphTexture[0], c.GL_TEXTURE_2D, 0, 0, 0, 0, textureOffset, textureHeight, 1);
shader.bind();
pipeline.bind(draw.getScissor());
c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight));
}
@ -1334,22 +1393,20 @@ pub const Shader = struct { // MARK: Shader
}
}
pub fn init(vertex: []const u8, fragment: []const u8, defines: []const u8) Shader {
fn init(vertex: []const u8, fragment: []const u8, defines: []const u8, uniformStruct: anytype) Shader {
const shader = Shader{.id = c.glCreateProgram()};
shader.addShader(vertex, defines, c.GL_VERTEX_SHADER) catch return shader;
shader.addShader(fragment, defines, c.GL_FRAGMENT_SHADER) catch return shader;
shader.link(fragment) catch return shader;
return shader;
}
pub fn initAndGetUniforms(vertex: []const u8, fragment: []const u8, defines: []const u8, ptrToUniformStruct: anytype) Shader {
const self = Shader.init(vertex, fragment, defines);
inline for(@typeInfo(@TypeOf(ptrToUniformStruct.*)).@"struct".fields) |field| {
if(field.type == c_int) {
@field(ptrToUniformStruct, field.name) = c.glGetUniformLocation(self.id, field.name[0..]);
if(@TypeOf(uniformStruct) != @TypeOf(null)) {
inline for(@typeInfo(@TypeOf(uniformStruct.*)).@"struct".fields) |field| {
if(field.type == c_int) {
@field(uniformStruct, field.name) = c.glGetUniformLocation(shader.id, field.name[0..]);
}
}
}
return self;
return shader;
}
pub fn initCompute(compute: []const u8, defines: []const u8) Shader {
@ -1378,6 +1435,345 @@ pub const Shader = struct { // MARK: Shader
}
};
pub const Pipeline = struct { // MARK: Pipeline
shader: Shader,
rasterState: RasterizationState,
multisampleState: MultisampleState = .{}, // TODO: Not implemented
depthStencilState: DepthStencilState,
blendState: ColorBlendState,
const RasterizationState = struct {
depthClamp: bool = true,
rasterizerDiscard: bool = false,
polygonMode: PolygonMode = .fill,
cullMode: CullModeFlags = .back,
frontFace: FrontFace = .counterClockwise,
depthBias: ?DepthBias = null,
lineWidth: f32 = 1,
const PolygonMode = enum(c.VkPolygonMode) {
fill = c.VK_POLYGON_MODE_FILL,
line = c.VK_POLYGON_MODE_LINE,
point = c.VK_POLYGON_MODE_POINT,
fillRectangleNV = c.VK_POLYGON_MODE_FILL_RECTANGLE_NV,
};
const CullModeFlags = enum(c.VkCullModeFlags) {
none = c.VK_CULL_MODE_NONE,
front = c.VK_CULL_MODE_FRONT_BIT,
back = c.VK_CULL_MODE_BACK_BIT,
frontAndBack = c.VK_CULL_MODE_FRONT_AND_BACK,
};
const FrontFace = enum(c.VkFrontFace) {
counterClockwise = c.VK_FRONT_FACE_COUNTER_CLOCKWISE,
clockwise = c.VK_FRONT_FACE_CLOCKWISE,
};
const DepthBias = struct {
constantFactor: f32,
clamp: f32,
slopeFactor: f32,
};
};
const MultisampleState = struct {
rasterizationSamples: Count = .@"1",
sampleShading: bool = false,
minSampleShading: f32 = undefined,
sampleMask: [*]const c.VkSampleMask = &.{0, 0},
alphaToCoverage: bool = false,
alphaToOne: bool = false,
const Count = enum(c.VkSampleCountFlags) {
@"1" = c.VK_SAMPLE_COUNT_1_BIT,
@"2" = c.VK_SAMPLE_COUNT_2_BIT,
@"4" = c.VK_SAMPLE_COUNT_4_BIT,
@"8" = c.VK_SAMPLE_COUNT_8_BIT,
@"16" = c.VK_SAMPLE_COUNT_16_BIT,
@"32" = c.VK_SAMPLE_COUNT_32_BIT,
@"64" = c.VK_SAMPLE_COUNT_64_BIT,
};
};
const DepthStencilState = struct {
depthTest: bool,
depthWrite: bool = true,
depthCompare: CompareOp = .less,
depthBoundsTest: ?DepthBoundsTest = null,
stencilTest: ?StencilTest = null,
const CompareOp = enum(c.VkCompareOp) {
never = c.VK_COMPARE_OP_NEVER,
less = c.VK_COMPARE_OP_LESS,
equal = c.VK_COMPARE_OP_EQUAL,
lessOrEqual = c.VK_COMPARE_OP_LESS_OR_EQUAL,
greater = c.VK_COMPARE_OP_GREATER,
notEqual = c.VK_COMPARE_OP_NOT_EQUAL,
greateOrEqual = c.VK_COMPARE_OP_GREATER_OR_EQUAL,
always = c.VK_COMPARE_OP_ALWAYS,
};
const StencilTest = struct {
front: StencilOpState,
back: StencilOpState,
const StencilOpState = struct {
failOp: StencilOp,
passOp: StencilOp,
depthFailOp: StencilOp,
compareOp: CompareOp,
compareMask: u32,
writeMask: u32,
reference: u32,
const StencilOp = enum(c.VkStencilOp) {
keep = c.VK_STENCIL_OP_KEEP,
zero = c.VK_STENCIL_OP_ZERO,
replace = c.VK_STENCIL_OP_REPLACE,
incrementAndClamp = c.VK_STENCIL_OP_INCREMENT_AND_CLAMP,
decrementAndClamp = c.VK_STENCIL_OP_DECREMENT_AND_CLAMP,
invert = c.VK_STENCIL_OP_INVERT,
incrementAndWrap = c.VK_STENCIL_OP_INCREMENT_AND_WRAP,
decrementAndWrap = c.VK_STENCIL_OP_DECREMENT_AND_WRAP,
};
};
};
const DepthBoundsTest = struct {
min: f32,
max: f32,
};
};
const ColorBlendAttachmentState = struct {
enabled: bool = true,
srcColorBlendFactor: BlendFactor,
dstColorBlendFactor: BlendFactor,
colorBlendOp: BlendOp,
srcAlphaBlendFactor: BlendFactor,
dstAlphaBlendFactor: BlendFactor,
alphaBlendOp: BlendOp,
colorWriteMask: ColorComponentFlags = .all,
pub const alphaBlending: ColorBlendAttachmentState = .{
.srcColorBlendFactor = .srcAlpha,
.dstColorBlendFactor = .oneMinusSrcAlpha,
.colorBlendOp = .add,
.srcAlphaBlendFactor = .srcAlpha,
.dstAlphaBlendFactor = .oneMinusSrcAlpha,
.alphaBlendOp = .add,
};
pub const noBlending: ColorBlendAttachmentState = .{
.enabled = false,
.srcColorBlendFactor = undefined,
.dstColorBlendFactor = undefined,
.colorBlendOp = undefined,
.srcAlphaBlendFactor = undefined,
.dstAlphaBlendFactor = undefined,
.alphaBlendOp = undefined,
};
const BlendFactor = enum(c.VkBlendFactor) {
zero = c.VK_BLEND_FACTOR_ZERO,
one = c.VK_BLEND_FACTOR_ONE,
srcColor = c.VK_BLEND_FACTOR_SRC_COLOR,
oneMinusSrcColor = c.VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
dstColor = c.VK_BLEND_FACTOR_DST_COLOR,
oneMinusDstColor = c.VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
srcAlpha = c.VK_BLEND_FACTOR_SRC_ALPHA,
oneMinusSrcAlpha = c.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
dstAlpha = c.VK_BLEND_FACTOR_DST_ALPHA,
oneMinusDstAlpha = c.VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
constantColor = c.VK_BLEND_FACTOR_CONSTANT_COLOR,
oneMinusConstantColor = c.VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
constantAlpha = c.VK_BLEND_FACTOR_CONSTANT_ALPHA,
oneMinusConstantAlpha = c.VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
srcAlphaSaturate = c.VK_BLEND_FACTOR_SRC_ALPHA_SATURATE,
src1Color = c.VK_BLEND_FACTOR_SRC1_COLOR,
oneMinusSrc1Color = c.VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR,
src1Alpha = c.VK_BLEND_FACTOR_SRC1_ALPHA,
oneMinusSrc1Alpha = c.VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,
fn toGl(self: BlendFactor) c.GLenum {
return switch(self) {
.zero => c.GL_ZERO,
.one => c.GL_ONE,
.srcColor => c.GL_SRC_COLOR,
.oneMinusSrcColor => c.GL_ONE_MINUS_SRC_COLOR,
.dstColor => c.GL_DST_COLOR,
.oneMinusDstColor => c.GL_ONE_MINUS_DST_COLOR,
.srcAlpha => c.GL_SRC_ALPHA,
.oneMinusSrcAlpha => c.GL_ONE_MINUS_SRC_ALPHA,
.dstAlpha => c.GL_DST_ALPHA,
.oneMinusDstAlpha => c.GL_ONE_MINUS_DST_ALPHA,
.constantColor => c.GL_CONSTANT_COLOR,
.oneMinusConstantColor => c.GL_ONE_MINUS_CONSTANT_COLOR,
.constantAlpha => c.GL_CONSTANT_ALPHA,
.oneMinusConstantAlpha => c.GL_ONE_MINUS_CONSTANT_ALPHA,
.srcAlphaSaturate => c.GL_SRC_ALPHA_SATURATE,
.src1Color => c.GL_SRC1_COLOR,
.oneMinusSrc1Color => c.GL_ONE_MINUS_SRC1_COLOR,
.src1Alpha => c.GL_SRC1_ALPHA,
.oneMinusSrc1Alpha => c.GL_ONE_MINUS_SRC1_ALPHA,
};
}
};
const BlendOp = enum(c.VkBlendOp) {
add = c.VK_BLEND_OP_ADD,
subtract = c.VK_BLEND_OP_SUBTRACT,
reverseSubtract = c.VK_BLEND_OP_REVERSE_SUBTRACT,
min = c.VK_BLEND_OP_MIN,
max = c.VK_BLEND_OP_MAX,
fn toGl(self: BlendOp) c.GLenum {
return switch(self) {
.add => c.GL_FUNC_ADD,
.subtract => c.GL_FUNC_SUBTRACT,
.reverseSubtract => c.GL_FUNC_REVERSE_SUBTRACT,
.min => c.GL_MIN,
.max => c.GL_MAX,
};
}
};
const ColorComponentFlags = packed struct {
r: bool,
g: bool,
b: bool,
a: bool,
pub const all: ColorComponentFlags = .{.r = true, .g = true, .b = true, .a = true};
pub const none: ColorComponentFlags = .{.r = false, .g = false, .b = false, .a = false};
};
};
const ColorBlendState = struct {
logicOp: ?LogicOp = null,
attachments: []const ColorBlendAttachmentState,
blendConstants: [4]f32 = .{0, 0, 0, 0},
const LogicOp = enum(c.VkLogicOp) {
clear = c.VK_LOGIC_OP_CLEAR,
@"and" = c.VK_LOGIC_OP_AND,
andReverse = c.VK_LOGIC_OP_AND_REVERSE,
copy = c.VK_LOGIC_OP_COPY,
andInverted = c.VK_LOGIC_OP_AND_INVERTED,
noOp = c.VK_LOGIC_OP_NO_OP,
xor = c.VK_LOGIC_OP_XOR,
@"or" = c.VK_LOGIC_OP_OR,
nor = c.VK_LOGIC_OP_NOR,
equivalent = c.VK_LOGIC_OP_EQUIVALENT,
invert = c.VK_LOGIC_OP_INVERT,
orReverse = c.VK_LOGIC_OP_OR_REVERSE,
copyInverted = c.VK_LOGIC_OP_COPY_INVERTED,
orInverted = c.VK_LOGIC_OP_OR_INVERTED,
nand = c.VK_LOGIC_OP_NAND,
set = c.VK_LOGIC_OP_SET,
};
};
pub fn init(vertexPath: []const u8, fragmentPath: []const u8, defines: []const u8, uniformStruct: anytype, rasterState: RasterizationState, depthStencilState: DepthStencilState, blendState: ColorBlendState) Pipeline {
std.debug.assert(depthStencilState.depthBoundsTest == null); // Only available in Vulkan 1.3
std.debug.assert(depthStencilState.stencilTest == null); // TODO: Not yet implemented
std.debug.assert(rasterState.lineWidth <= 1); // Larger values are poorly supported among drivers
std.debug.assert(blendState.logicOp == null); // TODO: Not yet implemented
return .{
.shader = .init(vertexPath, fragmentPath, defines, uniformStruct),
.rasterState = rasterState,
.multisampleState = .{}, // TODO: Not implemented
.depthStencilState = depthStencilState,
.blendState = blendState,
};
}
pub fn deinit(self: Pipeline) void {
self.shader.deinit();
}
fn conditionalEnable(typ: c.GLenum, val: bool) void {
if(val) {
c.glEnable(typ);
} else {
c.glDisable(typ);
}
}
pub fn bind(self: Pipeline, scissor: ?c.VkRect2D) void {
self.shader.bind();
if(scissor) |s| {
c.glEnable(c.GL_SCISSOR_TEST);
c.glScissor(s.offset.x, s.offset.y, @intCast(s.extent.width), @intCast(s.extent.height));
} else {
c.glDisable(c.GL_SCISSOR_TEST);
}
conditionalEnable(c.GL_DEPTH_CLAMP, self.rasterState.depthClamp);
conditionalEnable(c.GL_RASTERIZER_DISCARD, self.rasterState.rasterizerDiscard);
conditionalEnable(c.GL_RASTERIZER_DISCARD, self.rasterState.rasterizerDiscard);
c.glPolygonMode(c.GL_FRONT_AND_BACK, switch(self.rasterState.polygonMode) {
.fill => c.GL_FILL,
.line => c.GL_LINE,
.point => c.GL_POINT,
else => unreachable,
});
if(self.rasterState.cullMode != .none) {
c.glEnable(c.GL_CULL_FACE);
c.glCullFace(switch(self.rasterState.cullMode) {
.front => c.GL_FRONT,
.back => c.GL_BACK,
.frontAndBack => c.GL_FRONT_AND_BACK,
else => unreachable,
});
} else {
c.glDisable(c.GL_CULL_FACE);
}
c.glFrontFace(switch(self.rasterState.frontFace) {
.counterClockwise => c.GL_CCW,
.clockwise => c.GL_CW,
});
if(self.rasterState.depthBias) |depthBias| {
c.glEnable(c.GL_POLYGON_OFFSET_FILL);
c.glEnable(c.GL_POLYGON_OFFSET_LINE);
c.glEnable(c.GL_POLYGON_OFFSET_POINT);
c.glPolygonOffset(depthBias.slopeFactor, depthBias.constantFactor);
} else {
c.glDisable(c.GL_POLYGON_OFFSET_FILL);
c.glDisable(c.GL_POLYGON_OFFSET_LINE);
c.glDisable(c.GL_POLYGON_OFFSET_POINT);
}
c.glLineWidth(self.rasterState.lineWidth);
// TODO: Multisampling
conditionalEnable(c.GL_DEPTH_TEST, self.depthStencilState.depthTest);
c.glDepthMask(@intFromBool(self.depthStencilState.depthWrite));
c.glDepthFunc(switch(self.depthStencilState.depthCompare) {
.never => c.GL_NEVER,
.less => c.GL_LESS,
.equal => c.GL_EQUAL,
.lessOrEqual => c.GL_LEQUAL,
.greater => c.GL_GREATER,
.notEqual => c.GL_NOTEQUAL,
.greateOrEqual => c.GL_GEQUAL,
.always => c.GL_ALWAYS,
});
// TODO: stencilTest
// TODO: logicOp
for(self.blendState.attachments, 0..) |attachment, i| {
if(!attachment.enabled) {
c.glDisable(c.GL_BLEND);
continue;
}
c.glEnable(c.GL_BLEND);
c.glBlendEquationSeparatei(@intCast(i), attachment.colorBlendOp.toGl(), attachment.alphaBlendOp.toGl());
c.glBlendFuncSeparatei(@intCast(i), attachment.srcColorBlendFactor.toGl(), attachment.dstColorBlendFactor.toGl(), attachment.srcAlphaBlendFactor.toGl(), attachment.dstAlphaBlendFactor.toGl());
}
c.glBlendColor(self.blendState.blendConstants[0], self.blendState.blendConstants[1], self.blendState.blendConstants[2], self.blendState.blendConstants[3]);
}
};
pub const SSBO = struct { // MARK: SSBO
bufferID: c_uint,
pub fn init() SSBO {
@ -1653,6 +2049,9 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer
}
pub fn clear(_: FrameBuffer, clearColor: Vec4f) void {
c.glDepthFunc(c.GL_LESS);
c.glDepthMask(c.GL_TRUE);
c.glDisable(c.GL_SCISSOR_TEST);
c.glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT);
}
@ -2083,12 +2482,20 @@ const block_texture = struct { // MARK: block_texture
var uniforms: struct {
transparent: c_int,
} = undefined;
var shader: Shader = undefined;
var pipeline: Pipeline = undefined;
var depthTexture: Texture = undefined;
const textureSize = 128;
fn init() void {
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/item_texture_post.vs", "assets/cubyz/shaders/item_texture_post.fs", "", &uniforms);
pipeline = Pipeline.init(
"assets/cubyz/shaders/item_texture_post.vs",
"assets/cubyz/shaders/item_texture_post.fs",
"",
&uniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
depthTexture = .init();
depthTexture.bind();
var data: [128*128]f32 = undefined;
@ -2106,7 +2513,7 @@ const block_texture = struct { // MARK: block_texture
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_REPEAT);
}
fn deinit() void {
shader.deinit();
pipeline.deinit();
depthTexture.deinit();
}
};
@ -2117,15 +2524,6 @@ pub fn generateBlockTexture(blockType: u16) Texture {
c.glViewport(0, 0, textureSize, textureSize);
var frameBuffer: FrameBuffer = undefined;
const scissor = c.glIsEnabled(c.GL_SCISSOR_TEST);
c.glDisable(c.GL_SCISSOR_TEST);
defer if(scissor != 0) c.glEnable(c.GL_SCISSOR_TEST);
const depthTest = c.glIsEnabled(c.GL_DEPTH_TEST);
c.glDisable(c.GL_DEPTH_TEST);
defer if(depthTest != 0) c.glEnable(c.GL_DEPTH_TEST);
const cullFace = c.glIsEnabled(c.GL_CULL_FACE);
c.glEnable(c.GL_CULL_FACE);
defer if(cullFace != 0) c.glEnable(c.GL_CULL_FACE);
frameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT);
defer frameBuffer.deinit();
@ -2213,14 +2611,12 @@ pub fn generateBlockTexture(blockType: u16) Texture {
finalFrameBuffer.bind();
const texture = Texture{.textureID = finalFrameBuffer.texture};
defer c.glDeleteFramebuffers(1, &finalFrameBuffer.frameBuffer);
block_texture.shader.bind();
block_texture.pipeline.bind(null);
c.glUniform1i(block_texture.uniforms.transparent, if(block.transparent()) c.GL_TRUE else c.GL_FALSE);
frameBuffer.bindTexture(c.GL_TEXTURE3);
c.glBindVertexArray(draw.rectVAO);
c.glDisable(c.GL_BLEND);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
c.glEnable(c.GL_BLEND);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);

View File

@ -86,7 +86,7 @@ var titleTexture: Texture = undefined;
var closeTexture: Texture = undefined;
var zoomInTexture: Texture = undefined;
var zoomOutTexture: Texture = undefined;
var shader: Shader = undefined;
var pipeline: graphics.Pipeline = undefined;
var windowUniforms: struct {
screen: c_int,
start: c_int,
@ -94,7 +94,7 @@ var windowUniforms: struct {
color: c_int,
scale: c_int,
} = undefined;
pub var borderShader: Shader = undefined;
pub var borderPipeline: graphics.Pipeline = undefined;
pub var borderUniforms: struct {
screen: c_int,
start: c_int,
@ -105,10 +105,24 @@ pub var borderUniforms: struct {
} = undefined;
pub fn __init() void {
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/ui/button.vs", "assets/cubyz/shaders/ui/button.fs", "", &windowUniforms);
shader.bind();
borderShader = Shader.initAndGetUniforms("assets/cubyz/shaders/ui/window_border.vs", "assets/cubyz/shaders/ui/window_border.fs", "", &borderUniforms);
borderShader.bind();
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/ui/button.vs",
"assets/cubyz/shaders/ui/button.fs",
"",
&windowUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
borderPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/ui/window_border.vs",
"assets/cubyz/shaders/ui/window_border.fs",
"",
&borderUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
backgroundTexture = Texture.initFromFile("assets/cubyz/ui/window_background.png");
titleTexture = Texture.initFromFile("assets/cubyz/ui/window_title.png");
@ -118,7 +132,7 @@ pub fn __init() void {
}
pub fn __deinit() void {
shader.deinit();
pipeline.deinit();
backgroundTexture.deinit();
titleTexture.deinit();
}
@ -477,7 +491,7 @@ pub fn render(self: *const GuiWindow, mousePosition: Vec2f) void {
const oldScale = draw.setScale(self.scale);
if(self.hasBackground) {
draw.setColor(0xff000000);
shader.bind();
pipeline.bind(draw.getScissor());
backgroundTexture.bindTo(0);
draw.customShadedRect(windowUniforms, .{0, 0}, self.size/@as(Vec2f, @splat(self.scale)));
}
@ -486,7 +500,7 @@ pub fn render(self: *const GuiWindow, mousePosition: Vec2f) void {
component.render((mousePosition - self.pos)/@as(Vec2f, @splat(self.scale)));
}
if(self.showTitleBar or gui.reorderWindows) {
shader.bind();
pipeline.bind(draw.getScissor());
titleTexture.bindTo(0);
draw.setColor(0xff000000);
draw.customShadedRect(windowUniforms, .{0, 0}, .{self.size[0]/self.scale, titleBarHeight});

View File

@ -44,7 +44,7 @@ const Textures = struct {
var normalTextures: Textures = undefined;
var hoveredTextures: Textures = undefined;
var pressedTextures: Textures = undefined;
pub var shader: Shader = undefined;
pub var pipeline: graphics.Pipeline = undefined;
pub var buttonUniforms: struct {
screen: c_int,
start: c_int,
@ -61,15 +61,22 @@ onAction: gui.Callback,
child: GuiComponent,
pub fn __init() void {
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/ui/button.vs", "assets/cubyz/shaders/ui/button.fs", "", &buttonUniforms);
shader.bind();
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/ui/button.vs",
"assets/cubyz/shaders/ui/button.fs",
"",
&buttonUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
normalTextures = Textures.init("assets/cubyz/ui/button");
hoveredTextures = Textures.init("assets/cubyz/ui/button_hovered");
pressedTextures = Textures.init("assets/cubyz/ui/button_pressed");
}
pub fn __deinit() void {
shader.deinit();
pipeline.deinit();
normalTextures.deinit();
hoveredTextures.deinit();
pressedTextures.deinit();
@ -136,7 +143,7 @@ pub fn render(self: *Button, mousePosition: Vec2f) void {
normalTextures;
draw.setColor(0xff000000);
textures.texture.bindTo(0);
shader.bind();
pipeline.bind(draw.getScissor());
self.hovered = false;
draw.customShadedRect(buttonUniforms, self.pos + Vec2f{2, 2}, self.size - Vec2f{4, 4});
{ // Draw the outline using the 9-slice texture.

View File

@ -112,7 +112,7 @@ pub fn render(self: *CheckBox, mousePosition: Vec2f) void {
textureEmptyNormal.bindTo(0);
}
}
Button.shader.bind();
Button.pipeline.bind(draw.getScissor());
self.hovered = false;
draw.customShadedRect(Button.buttonUniforms, self.pos + Vec2f{0, self.size[1]/2 - boxSize/2}, @as(Vec2f, @splat(boxSize)));
const textPos = self.pos + Vec2f{boxSize/2, 0} + self.size/@as(Vec2f, @splat(2.0)) - self.label.size/@as(Vec2f, @splat(2.0));

View File

@ -123,7 +123,7 @@ pub fn mainButtonReleased(self: *ContinuousSlider, _: Vec2f) void {
pub fn render(self: *ContinuousSlider, mousePosition: Vec2f) void {
texture.bindTo(0);
Button.shader.bind();
Button.pipeline.bind(draw.getScissor());
draw.setColor(0xff000000);
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);

View File

@ -136,7 +136,7 @@ pub fn mainButtonReleased(self: *DiscreteSlider, _: Vec2f) void {
pub fn render(self: *DiscreteSlider, mousePosition: Vec2f) void {
texture.bindTo(0);
Button.shader.bind();
Button.pipeline.bind(draw.getScissor());
draw.setColor(0xff000000);
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);

View File

@ -95,7 +95,7 @@ pub fn mainButtonReleased(self: *ScrollBar, mousePosition: Vec2f) void {
pub fn render(self: *ScrollBar, mousePosition: Vec2f) void {
texture.bindTo(0);
Button.shader.bind();
Button.pipeline.bind(draw.getScissor());
draw.setColor(0xff000000);
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);

View File

@ -485,7 +485,7 @@ fn ensureCursorVisibility(self: *TextInput) void {
pub fn render(self: *TextInput, mousePosition: Vec2f) void {
texture.bindTo(0);
Button.shader.bind();
Button.pipeline.bind(draw.getScissor());
draw.setColor(0xff000000);
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);
const oldTranslation = draw.setTranslation(self.pos);

View File

@ -548,7 +548,7 @@ pub fn updateAndRenderGui() void {
if(!hideGui) {
if(!main.Window.grabbed) {
draw.setColor(0x80000000);
GuiWindow.borderShader.bind();
GuiWindow.borderPipeline.bind(draw.getScissor());
graphics.c.glUniform2f(GuiWindow.borderUniforms.effectLength, main.Window.getWindowSize()[0]/6, main.Window.getWindowSize()[1]/6);
draw.customShadedRect(GuiWindow.borderUniforms, .{0, 0}, main.Window.getWindowSize());
}

View File

@ -23,21 +23,44 @@ pub var window = GuiWindow{
};
var texture: Texture = undefined;
var pipeline: graphics.Pipeline = undefined;
var uniforms: struct {
screen: c_int,
start: c_int,
size: c_int,
color: c_int,
uvOffset: c_int,
uvDim: c_int,
} = undefined;
pub fn init() void {
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/graphics/Image.vs",
"assets/cubyz/shaders/graphics/Image.fs",
"",
&uniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.{
.srcColorBlendFactor = .one,
.dstColorBlendFactor = .one,
.colorBlendOp = .subtract,
.srcAlphaBlendFactor = .one,
.dstAlphaBlendFactor = .one,
.alphaBlendOp = .subtract,
}}},
);
texture = Texture.initFromFile("assets/cubyz/ui/hud/crosshair.png");
}
pub fn deinit() void {
pipeline.deinit();
texture.deinit();
}
pub fn render() void {
texture.bindTo(0);
graphics.draw.setColor(0xffffffff);
c.glBlendFunc(c.GL_ONE, c.GL_ONE);
c.glBlendEquation(c.GL_FUNC_SUBTRACT);
graphics.draw.boundImage(.{0, 0}, .{size, size});
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
c.glBlendEquation(c.GL_FUNC_ADD);
pipeline.bind(graphics.draw.getScissor());
graphics.draw.customShadedImage(&uniforms, .{0, 0}, .{size, size});
}

View File

@ -27,7 +27,7 @@ pub var window = GuiWindow{
var lastFrameTime: [2048]f32 = undefined;
var index: u31 = 0;
var ssbo: graphics.SSBO = undefined;
var shader: graphics.Shader = undefined;
var pipeline: graphics.Pipeline = undefined;
const border: f32 = 8;
var uniforms: struct {
@ -41,7 +41,15 @@ var uniforms: struct {
pub fn init() void {
ssbo = graphics.SSBO.init();
shader = graphics.Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/graph.vs", "assets/cubyz/shaders/graphics/graph.fs", "", &uniforms);
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/graphics/graph.vs",
"assets/cubyz/shaders/graphics/graph.fs",
"",
&uniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.alphaBlending}},
);
}
pub fn deinit() void {
@ -60,7 +68,7 @@ pub fn render() void {
draw.line(.{border, 40}, .{window.contentSize[0] - border, 40});
draw.line(.{border, 56}, .{window.contentSize[0] - border, 56});
draw.setColor(0xffffffff);
shader.bind();
pipeline.bind(null);
graphics.c.glUniform1i(uniforms.points, lastFrameTime.len);
graphics.c.glUniform1i(uniforms.offset, index);
graphics.c.glUniform3f(uniforms.lineColor, 1, 1, 1);

View File

@ -542,7 +542,7 @@ pub const ItemDisplayManager = struct { // MARK: ItemDisplayManager
};
pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
var itemShader: graphics.Shader = undefined;
var itemPipeline: graphics.Pipeline = undefined;
var itemUniforms: struct {
projectionMatrix: c_int,
modelMatrix: c_int,
@ -648,7 +648,15 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
};
pub fn init() void {
itemShader = graphics.Shader.initAndGetUniforms("assets/cubyz/shaders/item_drop.vs", "assets/cubyz/shaders/item_drop.fs", "", &itemUniforms);
itemPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/item_drop.vs",
"assets/cubyz/shaders/item_drop.fs",
"",
&itemUniforms,
.{},
.{.depthTest = true},
.{.attachments = &.{.alphaBlending}},
);
itemModelSSBO = .init();
itemModelSSBO.bufferData(i32, &[3]i32{1, 1, 1});
itemModelSSBO.bind(2);
@ -658,7 +666,7 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
}
pub fn deinit() void {
itemShader.deinit();
itemPipeline.deinit();
itemModelSSBO.deinit();
modelData.deinit();
voxelModels.clear();
@ -676,7 +684,7 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
}
fn bindCommonUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, ambientLight: Vec3f) void {
itemShader.bind();
itemPipeline.bind(null);
c.glUniform1f(itemUniforms.reflectionMapSize, main.renderer.reflectionCubeMapSize);
c.glUniformMatrix4fv(itemUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix));
c.glUniform3fv(itemUniforms.ambientLight, 1, @ptrCast(&ambientLight));

View File

@ -652,11 +652,6 @@ pub fn main() void { // MARK: main()
const c = Window.c;
c.glCullFace(c.GL_BACK);
c.glEnable(c.GL_BLEND);
c.glEnable(c.GL_DEPTH_CLAMP);
c.glDepthFunc(c.GL_LESS);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
Window.GLFWCallbacks.framebufferSize(undefined, Window.width, Window.height);
var lastBeginRendering = std.time.nanoTimestamp();
@ -673,6 +668,9 @@ pub fn main() void { // MARK: main()
c.glfwSwapBuffers(Window.window);
// Clear may also wait on vsync, so it's done before handling events:
gui.windowlist.gpu_performance_measuring.startQuery(.screenbuffer_clear);
c.glDepthFunc(c.GL_LESS);
c.glDepthMask(c.GL_TRUE);
c.glDisable(c.GL_SCISSOR_TEST);
c.glClearColor(0.5, 1, 1, 1);
c.glClear(c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT | c.GL_COLOR_BUFFER_BIT);
gui.windowlist.gpu_performance_measuring.stopQuery();
@ -707,13 +705,9 @@ pub fn main() void { // MARK: main()
}
if(!isHidden) {
c.glEnable(c.GL_CULL_FACE);
c.glEnable(c.GL_DEPTH_TEST);
renderer.render(game.Player.getEyePosBlocking(), deltaTime);
// Render the GUI
gui.windowlist.gpu_performance_measuring.startQuery(.gui);
c.glDisable(c.GL_CULL_FACE);
c.glDisable(c.GL_DEPTH_TEST);
gui.updateAndRenderGui();
gui.windowlist.gpu_performance_measuring.stopQuery();
}

View File

@ -33,7 +33,7 @@ const maximumMeshTime = 12;
pub const zNear = 0.1;
pub const zFar = 65536.0; // TODO: Fix z-fighting problems.
var deferredRenderPassShader: graphics.Shader = undefined;
var deferredRenderPassPipeline: graphics.Pipeline = undefined;
var deferredUniforms: struct {
@"fog.color": c_int,
@"fog.density": c_int,
@ -46,7 +46,7 @@ var deferredUniforms: struct {
playerPositionInteger: c_int,
playerPositionFraction: c_int,
} = undefined;
var fakeReflectionShader: graphics.Shader = undefined;
var fakeReflectionPipeline: graphics.Pipeline = undefined;
var fakeReflectionUniforms: struct {
normalVector: c_int,
upVector: c_int,
@ -61,8 +61,24 @@ pub const reflectionCubeMapSize = 64;
var reflectionCubeMap: graphics.CubeMapTexture = undefined;
pub fn init() void {
deferredRenderPassShader = Shader.initAndGetUniforms("assets/cubyz/shaders/deferred_render_pass.vs", "assets/cubyz/shaders/deferred_render_pass.fs", "", &deferredUniforms);
fakeReflectionShader = Shader.initAndGetUniforms("assets/cubyz/shaders/fake_reflection.vs", "assets/cubyz/shaders/fake_reflection.fs", "", &fakeReflectionUniforms);
deferredRenderPassPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/deferred_render_pass.vs",
"assets/cubyz/shaders/deferred_render_pass.fs",
"",
&deferredUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
fakeReflectionPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/fake_reflection.vs",
"assets/cubyz/shaders/fake_reflection.fs",
"",
&fakeReflectionUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
worldFrameBuffer.init(true, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE);
worldFrameBuffer.updateSize(Window.width, Window.height, c.GL_RGB16F);
Bloom.init();
@ -79,8 +95,8 @@ pub fn init() void {
}
pub fn deinit() void {
deferredRenderPassShader.deinit();
fakeReflectionShader.deinit();
deferredRenderPassPipeline.deinit();
fakeReflectionPipeline.deinit();
worldFrameBuffer.deinit();
Bloom.deinit();
MeshSelection.deinit();
@ -97,7 +113,7 @@ fn initReflectionCubeMap() void {
framebuffer.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE);
defer framebuffer.deinit();
framebuffer.bind();
fakeReflectionShader.bind();
fakeReflectionPipeline.bind(null);
c.glUniform1f(fakeReflectionUniforms.frequency, 1);
c.glUniform1f(fakeReflectionUniforms.reflectionMapSize, reflectionCubeMapSize);
for(0..6) |face| {
@ -106,8 +122,6 @@ fn initReflectionCubeMap() void {
c.glUniform3fv(fakeReflectionUniforms.rightVector, 1, @ptrCast(&graphics.CubeMapTexture.faceRight(face)));
reflectionCubeMap.bindToFramebuffer(framebuffer, @intCast(face));
c.glBindVertexArray(graphics.draw.rectVAO);
c.glDisable(c.GL_DEPTH_TEST);
c.glDisable(c.GL_CULL_FACE);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
}
@ -234,10 +248,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
gpu_performance_measuring.startQuery(.transparent_rendering_preparation);
c.glTextureBarrier();
c.glBlendEquation(c.GL_FUNC_ADD);
c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR);
c.glDepthFunc(c.GL_LEQUAL);
c.glDepthMask(c.GL_FALSE);
{
chunkList.clearRetainingCapacity();
var i: usize = meshes.len;
@ -251,9 +261,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
chunk_meshing.drawChunksIndirect(chunkList.items, game.projectionMatrix, ambientLight, playerPos, true);
}
}
c.glDepthMask(c.GL_TRUE);
c.glDepthFunc(c.GL_LESS);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
c.glDepthRange(0, 0.001);
itemdrop.ItemDropRenderer.renderDisplayItems(ambientLight, playerPos);
@ -275,7 +282,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
worldFrameBuffer.bindTexture(c.GL_TEXTURE3);
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE4);
worldFrameBuffer.unbind();
deferredRenderPassShader.bind();
deferredRenderPassPipeline.bind(null);
if(!blocks.meshes.hasFog(playerBlock)) {
c.glUniform3fv(deferredUniforms.@"fog.color", 1, @ptrCast(&game.fog.skyColor));
c.glUniform1f(deferredUniforms.@"fog.density", game.fog.density);
@ -298,8 +305,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
c.glBindFramebuffer(c.GL_FRAMEBUFFER, activeFrameBuffer);
c.glBindVertexArray(graphics.draw.rectVAO);
c.glDisable(c.GL_DEPTH_TEST);
c.glDisable(c.GL_CULL_FACE);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
@ -314,9 +319,9 @@ const Bloom = struct { // MARK: Bloom
var emptyBuffer: graphics.Texture = undefined;
var width: u31 = std.math.maxInt(u31);
var height: u31 = std.math.maxInt(u31);
var firstPassShader: graphics.Shader = undefined;
var secondPassShader: graphics.Shader = undefined;
var colorExtractAndDownsampleShader: graphics.Shader = undefined;
var firstPassPipeline: graphics.Pipeline = undefined;
var secondPassPipeline: graphics.Pipeline = undefined;
var colorExtractAndDownsamplePipeline: graphics.Pipeline = undefined;
var colorExtractUniforms: struct {
zNear: c_int,
zFar: c_int,
@ -335,20 +340,45 @@ const Bloom = struct { // MARK: Bloom
buffer2.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE);
emptyBuffer = .init();
emptyBuffer.generate(graphics.Image.emptyImage);
firstPassShader = graphics.Shader.init("assets/cubyz/shaders/bloom/first_pass.vs", "assets/cubyz/shaders/bloom/first_pass.fs", "");
secondPassShader = graphics.Shader.init("assets/cubyz/shaders/bloom/second_pass.vs", "assets/cubyz/shaders/bloom/second_pass.fs", "");
colorExtractAndDownsampleShader = graphics.Shader.initAndGetUniforms("assets/cubyz/shaders/bloom/color_extractor_downsample.vs", "assets/cubyz/shaders/bloom/color_extractor_downsample.fs", "", &colorExtractUniforms);
firstPassPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/bloom/first_pass.vs",
"assets/cubyz/shaders/bloom/first_pass.fs",
"",
null,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
secondPassPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/bloom/second_pass.vs",
"assets/cubyz/shaders/bloom/second_pass.fs",
"",
null,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
colorExtractAndDownsamplePipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/bloom/color_extractor_downsample.vs",
"assets/cubyz/shaders/bloom/color_extractor_downsample.fs",
"",
&colorExtractUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
}
pub fn deinit() void {
buffer1.deinit();
buffer2.deinit();
firstPassShader.deinit();
secondPassShader.deinit();
firstPassPipeline.deinit();
secondPassPipeline.deinit();
colorExtractAndDownsamplePipeline.deinit();
}
fn extractImageDataAndDownsample(playerBlock: blocks.Block, playerPos: Vec3d, viewMatrix: Mat4f) void {
colorExtractAndDownsampleShader.bind();
colorExtractAndDownsamplePipeline.bind(null);
worldFrameBuffer.bindTexture(c.GL_TEXTURE3);
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE4);
buffer1.bind();
@ -376,7 +406,7 @@ const Bloom = struct { // MARK: Bloom
}
fn firstPass() void {
firstPassShader.bind();
firstPassPipeline.bind(null);
buffer1.bindTexture(c.GL_TEXTURE3);
buffer2.bind();
c.glBindVertexArray(graphics.draw.rectVAO);
@ -384,7 +414,7 @@ const Bloom = struct { // MARK: Bloom
}
fn secondPass() void {
secondPassShader.bind();
secondPassPipeline.bind(null);
buffer2.bindTexture(c.GL_TEXTURE3);
buffer1.bind();
c.glBindVertexArray(graphics.draw.rectVAO);
@ -401,9 +431,6 @@ const Bloom = struct { // MARK: Bloom
std.debug.assert(buffer2.validate());
}
gpu_performance_measuring.startQuery(.bloom_extract_downsample);
c.glDisable(c.GL_DEPTH_TEST);
c.glDisable(c.GL_CULL_FACE);
c.glDepthMask(c.GL_FALSE);
c.glViewport(0, 0, width/4, height/4);
extractImageDataAndDownsample(playerBlock, playerPos, viewMatrix);
@ -417,10 +444,6 @@ const Bloom = struct { // MARK: Bloom
c.glViewport(0, 0, width, height);
buffer1.bindTexture(c.GL_TEXTURE5);
c.glDepthMask(c.GL_TRUE);
c.glEnable(c.GL_DEPTH_TEST);
c.glEnable(c.GL_CULL_FACE);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
gpu_performance_measuring.stopQuery();
}
@ -430,7 +453,7 @@ const Bloom = struct { // MARK: Bloom
};
pub const MenuBackGround = struct {
var shader: Shader = undefined;
var pipeline: graphics.Pipeline = undefined;
var uniforms: struct {
viewMatrix: c_int,
projectionMatrix: c_int,
@ -445,8 +468,15 @@ pub const MenuBackGround = struct {
fn init() !void {
lastTime = std.time.nanoTimestamp();
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/background/vertex.vs", "assets/cubyz/shaders/background/fragment.fs", "", &uniforms);
shader.bind();
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/background/vertex.vs",
"assets/cubyz/shaders/background/fragment.fs",
"",
&uniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.noBlending}},
);
// 4 sides of a simple cube with some panorama texture on it.
const rawData = [_]f32{
-1, 1, -1, 1, 1,
@ -515,7 +545,7 @@ pub const MenuBackGround = struct {
}
pub fn deinit() void {
shader.deinit();
pipeline.deinit();
c.glDeleteVertexArrays(1, &vao);
c.glDeleteBuffers(2, &vbos);
}
@ -526,14 +556,13 @@ pub const MenuBackGround = struct {
pub fn render() void {
if(texture.textureID == 0) return;
c.glDisable(c.GL_CULL_FACE); // I'm not sure if my triangles are rotated correctly, and there are no triangles facing away from the player anyways.
// Use a simple rotation around the z axis, with a steadily increasing angle.
const newTime = std.time.nanoTimestamp();
angle += @as(f32, @floatFromInt(newTime - lastTime))/2e10;
lastTime = newTime;
const viewMatrix = Mat4f.rotationZ(angle);
shader.bind();
pipeline.bind(null);
updateViewport(main.Window.width, main.Window.height, 70.0);
c.glUniformMatrix4fv(uniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix));
@ -576,8 +605,9 @@ pub const MenuBackGround = struct {
defer image.deinit(main.stackAllocator);
for(0..4) |i| {
c.glEnable(c.GL_CULL_FACE);
c.glEnable(c.GL_DEPTH_TEST);
c.glDepthFunc(c.GL_LESS);
c.glDepthMask(c.GL_TRUE);
c.glDisable(c.GL_SCISSOR_TEST);
game.camera.rotation = .{0, 0, angles[i]};
// Draw to frame buffer.
buffer.bind();
@ -595,8 +625,6 @@ pub const MenuBackGround = struct {
}
}
}
c.glDisable(c.GL_CULL_FACE);
c.glDisable(c.GL_DEPTH_TEST);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
const fileName = std.fmt.allocPrint(main.stackAllocator.allocator, "assets/backgrounds/{s}_{}.png", .{game.world.?.name, game.world.?.gameTime.load(.monotonic)}) catch unreachable;
@ -609,7 +637,7 @@ pub const MenuBackGround = struct {
};
pub const Skybox = struct {
var starShader: Shader = undefined;
var starPipeline: graphics.Pipeline = undefined;
var starUniforms: struct {
mvp: c_int,
starOpacity: c_int,
@ -653,8 +681,22 @@ pub const Skybox = struct {
};
defer starColorImage.deinit(main.stackAllocator);
starShader = Shader.initAndGetUniforms("assets/cubyz/shaders/skybox/star.vs", "assets/cubyz/shaders/skybox/star.fs", "", &starUniforms);
starShader.bind();
starPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/skybox/star.vs",
"assets/cubyz/shaders/skybox/star.fs",
"",
&starUniforms,
.{.cullMode = .none},
.{.depthTest = false, .depthWrite = false},
.{.attachments = &.{.{
.srcColorBlendFactor = .one,
.dstColorBlendFactor = .one,
.colorBlendOp = .add,
.srcAlphaBlendFactor = .one,
.dstAlphaBlendFactor = .one,
.alphaBlendOp = .add,
}}},
);
var starData: [numStars*20]f32 = undefined;
@ -719,7 +761,7 @@ pub const Skybox = struct {
}
pub fn deinit() void {
starShader.deinit();
starPipeline.deinit();
starSsbo.deinit();
c.glDeleteVertexArrays(1, &starVao);
}
@ -744,13 +786,7 @@ pub const Skybox = struct {
}
if(starOpacity != 0) {
c.glDisable(c.GL_CULL_FACE);
c.glDisable(c.GL_DEPTH_TEST);
c.glBlendFunc(c.GL_ONE, c.GL_ONE);
c.glEnable(c.GL_BLEND);
starShader.bind();
starPipeline.bind(null);
const starMatrix = game.projectionMatrix.mul(viewMatrix.mul(Mat4f.rotationX(@as(f32, @floatFromInt(time))/@as(f32, @floatFromInt(main.game.World.dayCycle)))));
@ -763,11 +799,6 @@ pub const Skybox = struct {
c.glDrawArrays(c.GL_TRIANGLES, 0, numStars*3);
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
c.glEnable(c.GL_CULL_FACE);
c.glEnable(c.GL_DEPTH_TEST);
}
}
};
@ -808,7 +839,7 @@ pub const Frustum = struct { // MARK: Frustum
};
pub const MeshSelection = struct { // MARK: MeshSelection
var shader: Shader = undefined;
var pipeline: graphics.Pipeline = undefined;
var uniforms: struct {
projectionMatrix: c_int,
viewMatrix: c_int,
@ -822,7 +853,19 @@ pub const MeshSelection = struct { // MARK: MeshSelection
var cubeIBO: c_uint = undefined;
pub fn init() void {
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/block_selection_vertex.vs", "assets/cubyz/shaders/block_selection_fragment.fs", "", &uniforms);
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/block_selection_vertex.vs",
"assets/cubyz/shaders/block_selection_fragment.fs",
"",
&uniforms,
.{.depthBias = .{
.slopeFactor = -2,
.clamp = 0,
.constantFactor = 0,
}},
.{.depthTest = true, .depthWrite = true},
.{.attachments = &.{.noBlending}},
);
const rawData = [_]f32{
0, 0, 0,
@ -862,7 +905,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
}
pub fn deinit() void {
shader.deinit();
pipeline.deinit();
c.glDeleteBuffers(1, &cubeIBO);
c.glDeleteBuffers(1, &cubeVBO);
c.glDeleteVertexArrays(1, &cubeVAO);
@ -1107,7 +1150,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
}
pub fn drawCube(projectionMatrix: Mat4f, viewMatrix: Mat4f, relativePositionToPlayer: Vec3d, min: Vec3f, max: Vec3f) void {
shader.bind();
pipeline.bind(null);
c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projectionMatrix));
c.glUniformMatrix4fv(uniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix));
@ -1122,26 +1165,19 @@ pub const MeshSelection = struct { // MARK: MeshSelection
c.glUniform3f(uniforms.upperBounds, max[0], max[1], max[2]);
c.glBindVertexArray(cubeVAO);
// c.glLineWidth(2); // TODO: Draw thicker lines so they are more visible. Maybe a simple shader + cube mesh is enough.
// TODO: Draw thicker lines so they are more visible. Maybe a simple shader + cube mesh is enough.
c.glDrawElements(c.GL_LINES, 12*2, c.GL_UNSIGNED_BYTE, null);
}
pub fn render(projectionMatrix: Mat4f, viewMatrix: Mat4f, playerPos: Vec3d) void {
if(main.gui.hideGui) return;
if(selectedBlockPos) |_selectedBlockPos| {
c.glEnable(c.GL_POLYGON_OFFSET_LINE);
defer c.glDisable(c.GL_POLYGON_OFFSET_LINE);
c.glPolygonOffset(-2, 0);
drawCube(projectionMatrix, viewMatrix, @as(Vec3d, @floatFromInt(_selectedBlockPos)) - playerPos, selectionMin, selectionMax);
}
if(game.Player.selectionPosition1) |pos1| {
if(game.Player.selectionPosition2) |pos2| {
const bottomLeft: Vec3i = @min(pos1, pos2);
const topRight: Vec3i = @max(pos1, pos2);
c.glEnable(c.GL_POLYGON_OFFSET_LINE);
defer c.glDisable(c.GL_POLYGON_OFFSET_LINE);
c.glPolygonOffset(-2, 0);
drawCube(projectionMatrix, viewMatrix, @as(Vec3d, @floatFromInt(bottomLeft)) - playerPos, .{0, 0, 0}, @floatFromInt(topRight - bottomLeft + Vec3i{1, 1, 1}));
}
}

View File

@ -25,8 +25,8 @@ const gpu_performance_measuring = main.gui.windowlist.gpu_performance_measuring;
const mesh_storage = @import("mesh_storage.zig");
var shader: Shader = undefined;
var transparentShader: Shader = undefined;
var pipeline: graphics.Pipeline = undefined;
var transparentPipeline: graphics.Pipeline = undefined;
const UniformStruct = struct {
projectionMatrix: c_int,
viewMatrix: c_int,
@ -56,7 +56,7 @@ pub var commandUniforms: struct {
onlyDrawPreviouslyInvisible: c_int,
lodDistance: c_int,
} = undefined;
pub var occlusionTestShader: Shader = undefined;
pub var occlusionTestPipeline: graphics.Pipeline = undefined;
pub var occlusionTestUniforms: struct {
projectionMatrix: c_int,
viewMatrix: c_int,
@ -75,10 +75,50 @@ pub var transparentQuadsDrawn: usize = 0;
pub fn init() void {
lighting.init();
shader = Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/chunk_fragment.fs", "", &uniforms);
transparentShader = Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/transparent_fragment.fs", "#define transparent\n", &transparentUniforms);
pipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/chunks/chunk_vertex.vs",
"assets/cubyz/shaders/chunks/chunk_fragment.fs",
"",
&uniforms,
.{},
.{.depthTest = true, .depthWrite = true},
.{.attachments = &.{.noBlending}},
);
transparentPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/chunks/chunk_vertex.vs",
"assets/cubyz/shaders/chunks/transparent_fragment.fs",
"#define transparent\n",
&transparentUniforms,
.{},
.{.depthTest = true, .depthWrite = false, .depthCompare = .lessOrEqual},
.{.attachments = &.{.{
.srcColorBlendFactor = .one,
.dstColorBlendFactor = .src1Color,
.colorBlendOp = .add,
.srcAlphaBlendFactor = .one,
.dstAlphaBlendFactor = .src1Alpha,
.alphaBlendOp = .add,
}}},
);
commandShader = Shader.initComputeAndGetUniforms("assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl", "", &commandUniforms);
occlusionTestShader = Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/occlusionTestVertex.vs", "assets/cubyz/shaders/chunks/occlusionTestFragment.fs", "", &occlusionTestUniforms);
occlusionTestPipeline = graphics.Pipeline.init(
"assets/cubyz/shaders/chunks/occlusionTestVertex.vs",
"assets/cubyz/shaders/chunks/occlusionTestFragment.fs",
"",
&occlusionTestUniforms,
.{},
.{.depthTest = true, .depthWrite = false},
.{.attachments = &.{.{
.enabled = false,
.srcColorBlendFactor = undefined,
.dstColorBlendFactor = undefined,
.colorBlendOp = undefined,
.srcAlphaBlendFactor = undefined,
.dstAlphaBlendFactor = undefined,
.alphaBlendOp = undefined,
.colorWriteMask = .none,
}}},
);
var rawData: [6*3 << (3*chunk.chunkShift)]u32 = undefined; // 6 vertices per face, maximum 3 faces/block
const lut = [_]u32{0, 2, 1, 1, 2, 3};
@ -102,8 +142,9 @@ pub fn init() void {
pub fn deinit() void {
lighting.deinit();
shader.deinit();
transparentShader.deinit();
pipeline.deinit();
transparentPipeline.deinit();
occlusionTestPipeline.deinit();
commandShader.deinit();
c.glDeleteVertexArrays(1, &vao);
c.glDeleteBuffers(1, &vbo);
@ -151,7 +192,7 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec
}
pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void {
shader.bind();
pipeline.bind(null);
bindCommonUniforms(&uniforms, projMatrix, ambient, playerPos);
@ -159,7 +200,7 @@ pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d
}
pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void {
transparentShader.bind();
transparentPipeline.bind(null);
c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.fog.skyColor));
c.glUniform1f(transparentUniforms.@"fog.density", game.fog.density);
@ -203,17 +244,13 @@ pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec
// Occlusion tests:
gpu_performance_measuring.startQuery(if(transparent) .transparent_rendering_occlusion_test else .chunk_rendering_occlusion_test);
occlusionTestShader.bind();
occlusionTestPipeline.bind(null);
c.glUniform3i(occlusionTestUniforms.playerPositionInteger, @intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2])));
c.glUniform3f(occlusionTestUniforms.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1)));
c.glUniformMatrix4fv(occlusionTestUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix));
c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix));
c.glDepthMask(c.GL_FALSE);
c.glColorMask(c.GL_FALSE, c.GL_FALSE, c.GL_FALSE, c.GL_FALSE);
c.glBindVertexArray(vao);
c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*6*chunkIDs.len), c.GL_UNSIGNED_INT, null, chunkIDAllocation.start*24);
c.glDepthMask(c.GL_TRUE);
c.glColorMask(c.GL_TRUE, c.GL_TRUE, c.GL_TRUE, c.GL_TRUE);
c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT);
gpu_performance_measuring.stopQuery();
@ -226,7 +263,6 @@ pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec
if(transparent) {
bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos);
c.glDepthMask(c.GL_FALSE);
} else {
bindShaderAndUniforms(projMatrix, ambient, playerPos);
}