Made Renderer a separate module
This commit is contained in:
parent
09691ec4d9
commit
1730f1e298
14 changed files with 292 additions and 260 deletions
21
src/renderer/Camera.zig
Normal file
21
src/renderer/Camera.zig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const std = @import("std");
|
||||
const ecs = @import("ecs");
|
||||
const math = @import("../math.zig");
|
||||
const Camera = @This();
|
||||
const UP = @Vector(3, f32){ 0.0, 1.0, 0.0 };
|
||||
|
||||
pub const Uniform = struct {
|
||||
proj: math.Matrix,
|
||||
view: math.Matrix,
|
||||
model: math.Matrix,
|
||||
};
|
||||
|
||||
uniform: Uniform,
|
||||
position: @Vector(3, f32),
|
||||
target: @Vector(3, f32),
|
||||
direction: @Vector(3, f32),
|
||||
right: @Vector(3, f32),
|
||||
up: @Vector(3, f32),
|
||||
|
||||
fn input(pool: *ecs.Pool) void {
|
||||
}
|
||||
119
src/renderer/Mesh.zig
Normal file
119
src/renderer/Mesh.zig
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
const c = @import("c.zig");
|
||||
const std = @import("std");
|
||||
const vk = @import("vulkan.zig");
|
||||
const gltf = @import("gltf.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Mesh = @This();
|
||||
|
||||
pub const Vertex = struct {
|
||||
position: [3]f32,
|
||||
|
||||
pub fn create(x: f32, y: f32, z: f32) Vertex {
|
||||
return Vertex{
|
||||
.position = .{ x, y, z },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bindingDescription() c.VkVertexInputBindingDescription {
|
||||
const binding_description: c.VkVertexInputBindingDescription = .{
|
||||
.binding = 0,
|
||||
.stride = @sizeOf(Vertex),
|
||||
.inputRate = c.VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
};
|
||||
|
||||
return binding_description;
|
||||
}
|
||||
|
||||
pub fn attributeDescription() c.VkVertexInputAttributeDescription {
|
||||
const attribute_description: c.VkVertexInputAttributeDescription = .{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = c.VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = 0,
|
||||
};
|
||||
|
||||
return attribute_description;
|
||||
}
|
||||
};
|
||||
|
||||
vertex_buffer: vk.Buffer,
|
||||
index_buffer: vk.Buffer,
|
||||
|
||||
pub fn createVertexBuffer(allocator: Allocator, device: anytype) !vk.Buffer {
|
||||
const gltf_data = try gltf.parseFile(allocator, "assets/models/block.glb");
|
||||
|
||||
const vertices = gltf_data.vertices;
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.createBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf(Vertex) * vertices.len);
|
||||
|
||||
try vk.mapError(c.vkMapMemory(
|
||||
device.handle,
|
||||
buffer.memory,
|
||||
0,
|
||||
buffer.size,
|
||||
0,
|
||||
@ptrCast(&data),
|
||||
));
|
||||
|
||||
if (data) |ptr| {
|
||||
const gpu_vertices: [*]Vertex = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@memcpy(gpu_vertices, @as([]Vertex, @ptrCast(vertices[0..])));
|
||||
}
|
||||
|
||||
c.vkUnmapMemory(device.handle, buffer.memory);
|
||||
|
||||
const vertex_buffer = try device.createBuffer(vk.BufferUsage{ .vertex_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(Vertex) * vertices.len);
|
||||
|
||||
try buffer.copyTo(device, vertex_buffer);
|
||||
buffer.destroy(device.handle);
|
||||
|
||||
return vertex_buffer;
|
||||
}
|
||||
|
||||
pub fn createIndexBuffer(allocator: Allocator, device: anytype) !vk.Buffer {
|
||||
const gltf_data = try gltf.parseFile(allocator, "assets/models/block.glb");
|
||||
const indices = gltf_data.indices;
|
||||
//const indices = [_]u16{ 0, 1, 2, 3, 0, 2 };
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.createBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf(u16) * indices.len);
|
||||
|
||||
try vk.mapError(c.vkMapMemory(
|
||||
device.handle,
|
||||
buffer.memory,
|
||||
0,
|
||||
buffer.size,
|
||||
0,
|
||||
@ptrCast(&data),
|
||||
));
|
||||
|
||||
if (data) |ptr| {
|
||||
const gpu_indices: [*]u16 = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@memcpy(gpu_indices, indices[0..]);
|
||||
}
|
||||
|
||||
c.vkUnmapMemory(device.handle, buffer.memory);
|
||||
|
||||
const index_buffer = try device.createBuffer(vk.BufferUsage{ .index_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(u16) * indices.len);
|
||||
|
||||
try buffer.copyTo(device, index_buffer);
|
||||
buffer.destroy(device.handle);
|
||||
|
||||
return index_buffer;
|
||||
}
|
||||
|
||||
pub fn create(allocator: Allocator, device: anytype) !Mesh {
|
||||
const vertex_buffer = try Mesh.createVertexBuffer(allocator, device);
|
||||
const index_buffer = try Mesh.createIndexBuffer(allocator, device);
|
||||
|
||||
return Mesh{
|
||||
.vertex_buffer = vertex_buffer,
|
||||
.index_buffer = index_buffer,
|
||||
};
|
||||
}
|
||||
94
src/renderer/Renderer.zig
Normal file
94
src/renderer/Renderer.zig
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
const c = @import("c.zig");
|
||||
const std = @import("std");
|
||||
const vk = @import("vulkan.zig");
|
||||
pub const Window = @import("Window.zig");
|
||||
pub const Mesh = @import("Mesh.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Renderer = @This();
|
||||
|
||||
instance: vk.Instance,
|
||||
surface: vk.Surface,
|
||||
physical_device: vk.PhysicalDevice,
|
||||
device: vk.Device(2),
|
||||
render_pass: vk.RenderPass(2),
|
||||
swapchain: vk.Swapchain(2),
|
||||
graphics_pipeline: vk.GraphicsPipeline(2),
|
||||
current_frame: u32,
|
||||
vertex_buffer: vk.Buffer,
|
||||
index_buffer: vk.Buffer,
|
||||
|
||||
pub fn create(allocator: Allocator, w: Window) !Renderer {
|
||||
const instance = try vk.Instance.create(allocator);
|
||||
|
||||
const surface = try vk.Surface.create(instance, w);
|
||||
|
||||
var physical_device = try vk.PhysicalDevice.pick(allocator, instance);
|
||||
const device = try physical_device.create_device(surface, allocator, 2);
|
||||
|
||||
const vertex_shader = try device.createShader("shader_vert");
|
||||
defer device.destroyShader(vertex_shader);
|
||||
const fragment_shader = try device.createShader("shader_frag");
|
||||
defer device.destroyShader(fragment_shader);
|
||||
|
||||
const render_pass = try vk.RenderPass(2).create(allocator, device, surface, physical_device);
|
||||
|
||||
const swapchain = try vk.Swapchain(2).create(allocator, surface, device, physical_device, w, render_pass);
|
||||
|
||||
const graphics_pipeline = try vk.GraphicsPipeline(2).create(device, swapchain, render_pass, vertex_shader, fragment_shader);
|
||||
|
||||
// TODO: I think the renderer shouldn't have to interact with buffers. I think the API should change to
|
||||
// something along the lines of
|
||||
// renderer.begin()
|
||||
// renderer.render(triangle);
|
||||
// renderer.render(some_other_thing);
|
||||
// ...
|
||||
// renderer.submit()
|
||||
const triangle = try Mesh.create(allocator, device);
|
||||
|
||||
return Renderer{
|
||||
.instance = instance,
|
||||
.surface = surface,
|
||||
.physical_device = physical_device,
|
||||
.device = device,
|
||||
.render_pass = render_pass,
|
||||
.swapchain = swapchain,
|
||||
.graphics_pipeline = graphics_pipeline,
|
||||
.current_frame = 0,
|
||||
// TODO: Why are we storing the buffer and not the Mesh?
|
||||
.vertex_buffer = triangle.vertex_buffer,
|
||||
.index_buffer = triangle.index_buffer,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn destroy(self: Renderer) void {
|
||||
self.device.waitIdle();
|
||||
self.index_buffer.destroy(self.device.handle);
|
||||
self.vertex_buffer.destroy(self.device.handle);
|
||||
self.graphics_pipeline.destroy(self.device);
|
||||
self.swapchain.destroy(self.device);
|
||||
self.render_pass.destroy(self.device);
|
||||
self.device.destroy();
|
||||
self.surface.destroy(self.instance);
|
||||
self.instance.destroy();
|
||||
}
|
||||
|
||||
// TODO: tick is maybe a bad name? something like present() or submit() is better?
|
||||
pub fn tick(self: *Renderer) !void {
|
||||
try self.device.waitFence(self.current_frame);
|
||||
const image = try self.swapchain.nextImage(self.device, self.current_frame);
|
||||
try self.device.resetCommand(self.current_frame);
|
||||
try self.device.beginCommand(self.current_frame);
|
||||
self.render_pass.begin(self.swapchain, self.device, image, self.current_frame);
|
||||
self.graphics_pipeline.bind(self.device, self.current_frame);
|
||||
self.device.bindVertexBuffer(self.vertex_buffer, self.current_frame);
|
||||
self.device.bindIndexBuffer(self.index_buffer, self.current_frame);
|
||||
self.device.bindDescriptorSets(self.graphics_pipeline, self.current_frame);
|
||||
self.device.draw(@intCast(self.index_buffer.size / @sizeOf(u16)), self.current_frame);
|
||||
self.render_pass.end(self.device, self.current_frame);
|
||||
try self.device.endCommand(self.current_frame);
|
||||
|
||||
try self.device.submit(self.swapchain, image, self.current_frame);
|
||||
|
||||
self.current_frame = (self.current_frame + 1) % 2;
|
||||
}
|
||||
64
src/renderer/Window.zig
Normal file
64
src/renderer/Window.zig
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
const c = @import("c.zig");
|
||||
const std = @import("std");
|
||||
|
||||
const Window = @This();
|
||||
|
||||
pub const Error = error{
|
||||
platform_unavailable,
|
||||
platform_error,
|
||||
};
|
||||
|
||||
pub fn getExtensions() [][*c]const u8 {
|
||||
var extension_count: u32 = undefined;
|
||||
const raw: [*c][*c]const u8 = c.glfwGetRequiredInstanceExtensions(&extension_count);
|
||||
const extensions = raw[0..extension_count];
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
title: []const u8,
|
||||
width: usize,
|
||||
height: usize,
|
||||
raw: *c.GLFWwindow,
|
||||
|
||||
pub fn create(width: usize, height: usize, title: []const u8) !Window {
|
||||
if (c.glfwInit() != c.GLFW_TRUE) {
|
||||
const status = c.glfwGetError(null);
|
||||
|
||||
return switch (status) {
|
||||
c.GLFW_PLATFORM_UNAVAILABLE => Error.platform_unavailable,
|
||||
c.GLFW_PLATFORM_ERROR => Error.platform_error,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
c.glfwWindowHint(c.GLFW_RESIZABLE, c.GLFW_FALSE);
|
||||
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
||||
const raw = c.glfwCreateWindow(@intCast(width), @intCast(height), title.ptr, null, null);
|
||||
c.glfwShowWindow(raw);
|
||||
|
||||
return Window{
|
||||
.title = title,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.raw = raw.?,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn shouldClose(self: Window) bool {
|
||||
return c.glfwWindowShouldClose(self.raw) == c.GLFW_TRUE;
|
||||
}
|
||||
|
||||
pub fn size(self: Window) struct { usize, usize } {
|
||||
var width: u32 = undefined;
|
||||
var height: u32 = undefined;
|
||||
|
||||
c.glfwGetFramebufferSize(self.raw, @ptrCast(&width), @ptrCast(&height));
|
||||
|
||||
return .{ @intCast(width), @intCast(height) };
|
||||
}
|
||||
|
||||
pub fn destroy(self: Window) void {
|
||||
c.glfwDestroyWindow(self.raw);
|
||||
c.glfwTerminate();
|
||||
}
|
||||
5
src/renderer/c.zig
Normal file
5
src/renderer/c.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub usingnamespace @cImport({
|
||||
@cDefine("GLFW_INCLUDE_NONE", {});
|
||||
@cInclude("vulkan/vulkan.h");
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
});
|
||||
164
src/renderer/gltf.zig
Normal file
164
src/renderer/gltf.zig
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
const std = @import("std");
|
||||
const Mesh = @import("Mesh.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const Model = struct {
|
||||
const Asset = struct {
|
||||
version: []u8,
|
||||
generator: ?[]u8 = null,
|
||||
copyright: ?[]u8 = null,
|
||||
};
|
||||
const Buffer = struct {
|
||||
byteLength: usize,
|
||||
uri: ?[]u8 = null,
|
||||
};
|
||||
const BufferView = struct {
|
||||
buffer: usize,
|
||||
byteLength: usize,
|
||||
byteOffset: usize,
|
||||
byteStride: ?usize = null,
|
||||
target: ?usize = null,
|
||||
};
|
||||
const Node = struct {
|
||||
name: []u8,
|
||||
mesh: ?usize = null,
|
||||
weights: ?[]f64 = null,
|
||||
children: ?[]usize = null,
|
||||
rotation: ?[4]f64 = null,
|
||||
scale: ?[3]f64 = null,
|
||||
translation: ?[3]f64 = null,
|
||||
camera: ?usize = null,
|
||||
matrix: ?[16]usize = null,
|
||||
};
|
||||
const Accessor = struct {
|
||||
bufferView: usize,
|
||||
byteOffset: ?usize = null,
|
||||
componentType: usize,
|
||||
count: usize,
|
||||
type: []u8,
|
||||
max: ?[]f64 = null,
|
||||
min: ?[]f64 = null,
|
||||
};
|
||||
const Primitive = struct {
|
||||
const Attributes = struct {
|
||||
NORMAL: ?usize = null,
|
||||
POSITION: ?usize = null,
|
||||
TANGENT: ?usize = null,
|
||||
TEXCOORD_0: ?usize = null,
|
||||
TEXCOORD_1: ?usize = null,
|
||||
COLOR_0: ?usize = null,
|
||||
JOINTS_0: ?usize = null,
|
||||
WEIGHTS_0: ?usize = null,
|
||||
};
|
||||
|
||||
attributes: ?Attributes = null,
|
||||
indices: ?usize = null,
|
||||
material: ?usize = null,
|
||||
mode: ?usize = null,
|
||||
};
|
||||
const Mesh = struct {
|
||||
name: ?[]u8 = null,
|
||||
primitives: ?[]Primitive = null,
|
||||
weights: ?[]f64 = null,
|
||||
};
|
||||
const Skin = struct {
|
||||
inverseBindMatrices: usize,
|
||||
joints: []usize,
|
||||
skeleton: usize,
|
||||
};
|
||||
const Texture = struct {
|
||||
sampler: usize,
|
||||
source: usize,
|
||||
};
|
||||
const Image = struct {
|
||||
uri: ?[]u8 = null,
|
||||
bufferView: ?usize = null,
|
||||
mimeType: ?[]u8 = null,
|
||||
};
|
||||
const Material = struct {
|
||||
const Pbr = struct {
|
||||
baseColorFactor: ?[4]f64 = null,
|
||||
baseColorTexture: ?struct {
|
||||
index: usize,
|
||||
texCoord: usize,
|
||||
} = null,
|
||||
metallicFactor: ?f64 = null,
|
||||
roughnessFactor: ?f64 = null,
|
||||
};
|
||||
name: ?[]u8 = null,
|
||||
pbrMetallicRoughness: Pbr,
|
||||
doubleSided: bool,
|
||||
};
|
||||
const Scene = struct {
|
||||
nodes: ?[]usize = null,
|
||||
name: ?[]u8 = null,
|
||||
};
|
||||
|
||||
const Chunk = packed struct {
|
||||
const offset = Header.offset + 8;
|
||||
length: u32,
|
||||
type: u32,
|
||||
};
|
||||
|
||||
const JsonChunk = struct {
|
||||
asset: Asset,
|
||||
scene: usize,
|
||||
scenes: ?[]Scene = null,
|
||||
nodes: ?[]Node = null,
|
||||
materials: ?[]Material = null,
|
||||
meshes: ?[]Model.Mesh = null,
|
||||
accessors: ?[]Accessor = null,
|
||||
bufferViews: ?[]BufferView = null,
|
||||
buffers: ?[]Buffer = null,
|
||||
};
|
||||
|
||||
const Header = packed struct {
|
||||
const offset = 12;
|
||||
magic: u32,
|
||||
version: u32,
|
||||
length: u32,
|
||||
};
|
||||
|
||||
const Binary = struct {
|
||||
data: []u8,
|
||||
const Vec3 = [3]f32;
|
||||
|
||||
pub fn readU16(self: Binary, allocator: Allocator, view: BufferView, count: usize) ![]u16 {
|
||||
const data = self.data[view.byteOffset .. view.byteOffset + view.byteLength];
|
||||
const scalars = try allocator.alloc(u16, count);
|
||||
|
||||
var j: usize = 0;
|
||||
for (0..data.len / 2) |i| {
|
||||
scalars[i] = std.mem.bytesAsValue(u16, data[j .. j + 1]).*;
|
||||
j += 2;
|
||||
}
|
||||
|
||||
return scalars;
|
||||
}
|
||||
|
||||
pub fn readVec3(self: Binary, allocator: Allocator, view: BufferView, count: usize) ![]Vec3 {
|
||||
const data = self.data[view.byteOffset .. view.byteOffset + view.byteLength];
|
||||
const vectors = try allocator.alloc(Vec3, count);
|
||||
|
||||
for (0..count) |i| {
|
||||
vectors[i] = std.mem.bytesAsValue(Vec3, data[(@sizeOf(Vec3) * i) .. (@sizeOf(Vec3) * i) + @sizeOf(Vec3)]).*;
|
||||
}
|
||||
|
||||
return vectors;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub fn parseFile(allocator: Allocator, name: []const u8) !struct { vertices: [][3]f32, indices: []u16 } {
|
||||
const file = try std.fs.cwd().openFile(name, .{});
|
||||
const all = try file.readToEndAlloc(allocator, 1_000_000);
|
||||
const json_chunk = std.mem.bytesAsValue(Model.Chunk, all[Model.Header.offset..]);
|
||||
|
||||
const data = (try std.json.parseFromSlice(Model.JsonChunk, allocator, @constCast(all[Model.Chunk.offset .. Model.Chunk.offset + json_chunk.length]), .{ .ignore_unknown_fields = true })).value;
|
||||
const binary = Model.Binary{ .data = all[Model.Chunk.offset + json_chunk.length + 8 ..] };
|
||||
|
||||
const vertices = try binary.readVec3(allocator, data.bufferViews.?[data.meshes.?[0].primitives.?[0].attributes.?.POSITION.?], data.accessors.?[data.meshes.?[0].primitives.?[0].attributes.?.POSITION.?].count);
|
||||
const indices = try binary.readU16(allocator, data.bufferViews.?[data.meshes.?[0].primitives.?[0].indices.?], data.accessors.?[data.meshes.?[0].primitives.?[0].indices.?].count);
|
||||
|
||||
return .{ .vertices = vertices, .indices = indices };
|
||||
}
|
||||
1153
src/renderer/vulkan.zig
Normal file
1153
src/renderer/vulkan.zig
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue