Cleaned up rendering module
This commit is contained in:
parent
473c4aeffb
commit
b1d092b6e3
10 changed files with 21 additions and 16 deletions
43
src/rendering/Camera.zig
Normal file
43
src/rendering/Camera.zig
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
const std = @import("std");
|
||||
const ecs = @import("ecs");
|
||||
const math = @import("math");
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
position: @Vector(3, f32),
|
||||
target: @Vector(3, f32) = .{ 0.0, 0.0, 0.0 },
|
||||
front: @Vector(3, f32) = .{ 0.0, 0.0, 1.0 },
|
||||
up: @Vector(3, f32) = .{ 0.0, 1.0, 0.0 },
|
||||
speed: f32 = 2.5,
|
||||
|
||||
pub fn getProjection(width: usize, height: usize) math.Matrix {
|
||||
return math.Matrix.perspective(math.rad(45.0), (@as(f32, @floatFromInt(width)) / @as(f32, @floatFromInt(height))), 0.1, 100.0);
|
||||
}
|
||||
|
||||
pub fn getView(self: Camera) math.Matrix {
|
||||
return math.Matrix.lookAt(self.position, self.target, self.up);
|
||||
}
|
||||
|
||||
pub fn moveCamera(pool: *ecs.Pool) void {
|
||||
const input = pool.resources.input;
|
||||
const camera = pool.resources.camera;
|
||||
if (input.isKeyDown(.w)) {
|
||||
camera.position += (camera.front * (camera.speed * pool.resources.delta_time));
|
||||
}
|
||||
if (input.isKeyDown(.s)) {
|
||||
camera.position -= (camera.front * (camera.speed * pool.resources.delta_time));
|
||||
}
|
||||
if (input.isKeyDown(.a)) {
|
||||
camera.position -= math.normalize(math.cross(camera.front, camera.up)) * (camera.speed * pool.resources.delta_time);
|
||||
}
|
||||
if (input.isKeyDown(.d)) {
|
||||
camera.position += math.normalize(math.cross(camera.front, camera.up)) * (camera.speed * pool.resources.delta_time);
|
||||
}
|
||||
}
|
||||
162
src/rendering/Mesh.zig
Normal file
162
src/rendering/Mesh.zig
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan.zig");
|
||||
const gltf = @import("gltf.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const c = vk.c;
|
||||
|
||||
const Mesh = @This();
|
||||
|
||||
pub const Vertex = struct {
|
||||
position: [3]f32,
|
||||
normal: [3]f32,
|
||||
uv: [2]f32,
|
||||
|
||||
pub fn create(x: f32, y: f32, z: f32, normal_x: f32, normal_y: f32, normal_z: f32, u: f32, v: f32) Vertex {
|
||||
return Vertex{
|
||||
.position = .{ x, y, z },
|
||||
.normal = .{ normal_x, normal_y, normal_z },
|
||||
.uv = .{u, v},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bindingDescription() vk.c.VkVertexInputBindingDescription {
|
||||
const binding_description: vk.c.VkVertexInputBindingDescription = .{
|
||||
.binding = 0,
|
||||
.stride = @sizeOf(Vertex),
|
||||
.inputRate = vk.c.VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
};
|
||||
|
||||
return binding_description;
|
||||
}
|
||||
|
||||
pub fn attributeDescriptions() []const c.VkVertexInputAttributeDescription {
|
||||
const attributes: []const c.VkVertexInputAttributeDescription = &.{
|
||||
.{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = c.VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
.{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = c.VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = 12,
|
||||
},
|
||||
.{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = c.VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = 24,
|
||||
},
|
||||
};
|
||||
|
||||
return attributes;
|
||||
}
|
||||
};
|
||||
|
||||
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/cube.glb");
|
||||
|
||||
const vertices = gltf_data.vertices;
|
||||
const normals = gltf_data.normals;
|
||||
const uvs = gltf_data.uvs;
|
||||
defer allocator.free(uvs);
|
||||
defer allocator.free(normals);
|
||||
defer allocator.free(vertices);
|
||||
defer allocator.free(gltf_data.indices);
|
||||
|
||||
const final_array = try allocator.alloc([8]f32, vertices.len);
|
||||
defer allocator.free(final_array);
|
||||
|
||||
for (vertices, normals, uvs, final_array) |vertex, normal, uv, *final| {
|
||||
final[0] = vertex[0];
|
||||
final[1] = vertex[1];
|
||||
final[2] = vertex[2];
|
||||
|
||||
final[3] = normal[0];
|
||||
final[4] = normal[1];
|
||||
final[5] = normal[2];
|
||||
|
||||
final[6] = uv[0];
|
||||
final[7] = uv[1];
|
||||
}
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.createBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf([8]f32) * vertices.len);
|
||||
|
||||
try vk.mapError(vk.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(final_array[0..])));
|
||||
}
|
||||
|
||||
vk.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/cube.glb");
|
||||
const indices = gltf_data.indices;
|
||||
defer allocator.free(indices);
|
||||
defer allocator.free(gltf_data.vertices);
|
||||
defer allocator.free(gltf_data.normals);
|
||||
defer allocator.free(gltf_data.uvs);
|
||||
|
||||
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(vk.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..]);
|
||||
}
|
||||
|
||||
vk.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,
|
||||
};
|
||||
}
|
||||
128
src/rendering/Renderer.zig
Normal file
128
src/rendering/Renderer.zig
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
const math = @import("math");
|
||||
const ecs = @import("ecs");
|
||||
const std = @import("std");
|
||||
const vk = @import("vulkan.zig");
|
||||
const Mesh = @import("Mesh.zig");
|
||||
const Texture = vk.Texture;
|
||||
const Camera = @import("Camera.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,
|
||||
transform: math.Transform,
|
||||
previous_time: std.time.Instant,
|
||||
|
||||
pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_handle: vk.c.VkSurfaceKHR) !Renderer {
|
||||
const instance: vk.Instance = .{ .handle = instance_handle };
|
||||
const surface: vk.Surface = .{ .handle = surface_handle };
|
||||
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, render_pass);
|
||||
|
||||
var graphics_pipeline = try vk.GraphicsPipeline(2).create(allocator, 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);
|
||||
|
||||
const texture = try Texture.init("assets/textures/container.png", device);
|
||||
const diffuse = try Texture.init("assets/textures/container_specular.png", device);
|
||||
|
||||
_ = try graphics_pipeline.addTexture(device, texture, diffuse);
|
||||
|
||||
graphics_pipeline.light_pos[0] = -10.0;
|
||||
graphics_pipeline.light_pos[1] = 0.0;
|
||||
graphics_pipeline.light_pos[2] = 0.0;
|
||||
|
||||
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,
|
||||
.transform = math.Transform.init(.{0.0, 0.0, 0.0}, .{1.0, 1.0, 1.0}, .{0.0, 0.0, 0.0}),
|
||||
.previous_time = try std.time.Instant.now(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(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();
|
||||
}
|
||||
|
||||
// TODO: render is maybe a bad name? something like present() or submit() is better?
|
||||
pub fn render(pool: *ecs.Pool) anyerror!void {
|
||||
var renderer = pool.resources.renderer;
|
||||
var camera = pool.resources.camera;
|
||||
|
||||
const now = try std.time.Instant.now();
|
||||
const delta_time: f32 = @as(f32, @floatFromInt(now.since(renderer.previous_time))) / @as(f32, 1_000_000_000.0);
|
||||
renderer.previous_time = now;
|
||||
|
||||
const view_memory = renderer.graphics_pipeline.view_memory;
|
||||
@memcpy(view_memory[0..@sizeOf(math.Matrix)], std.mem.asBytes(&camera.getView()));
|
||||
|
||||
const view_pos_memory = renderer.graphics_pipeline.view_pos_memory;
|
||||
const view_pos: [*]f32 = @alignCast(@ptrCast(view_pos_memory));
|
||||
view_pos[0] = camera.position[0];
|
||||
view_pos[1] = camera.position[1];
|
||||
view_pos[2] = camera.position[2];
|
||||
|
||||
renderer.transform.rotate(math.rad(10) * delta_time, .{0.0, 1.0, 0.0});
|
||||
|
||||
const transform_memory = renderer.graphics_pipeline.transform_memory;
|
||||
@memcpy(transform_memory[0..(@sizeOf(math.Transform)-@sizeOf(math.Quaternion))], std.mem.asBytes(&renderer.transform)[0..(@sizeOf(math.Transform)-@sizeOf(math.Quaternion))]);
|
||||
|
||||
try renderer.device.waitFence(renderer.current_frame);
|
||||
const image = try renderer.swapchain.nextImage(renderer.device, renderer.current_frame);
|
||||
try renderer.device.resetCommand(renderer.current_frame);
|
||||
try renderer.device.beginCommand(renderer.current_frame);
|
||||
renderer.render_pass.begin(renderer.swapchain, renderer.device, image, renderer.current_frame);
|
||||
renderer.graphics_pipeline.bind(renderer.device, renderer.current_frame);
|
||||
renderer.device.bindVertexBuffer(renderer.vertex_buffer, renderer.current_frame);
|
||||
renderer.device.bindIndexBuffer(renderer.index_buffer, renderer.current_frame);
|
||||
renderer.device.bindDescriptorSets(renderer.graphics_pipeline, renderer.current_frame, 0);
|
||||
renderer.device.draw(@intCast(renderer.index_buffer.size / @sizeOf(u16)), renderer.current_frame);
|
||||
renderer.render_pass.end(renderer.device, renderer.current_frame);
|
||||
try renderer.device.endCommand(renderer.current_frame);
|
||||
|
||||
try renderer.device.submit(renderer.swapchain, image, renderer.current_frame);
|
||||
|
||||
renderer.current_frame = (renderer.current_frame + 1) % 2;
|
||||
|
||||
renderer.device.waitIdle();
|
||||
}
|
||||
118
src/rendering/Texture.zig
Normal file
118
src/rendering/Texture.zig
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
const Texture = @This();
|
||||
const vk = @import("vulkan.zig");
|
||||
const c = vk.c;
|
||||
pub const stb = @cImport({
|
||||
@cInclude("stb_image.h");
|
||||
});
|
||||
|
||||
image: c.VkImage,
|
||||
image_memory: c.VkDeviceMemory,
|
||||
image_view: c.VkImageView,
|
||||
|
||||
pub fn init(path: [:0]const u8, device: anytype) !Texture {
|
||||
var width: i32 = 0;
|
||||
var height: i32 = 0;
|
||||
var channels: i32 = 0;
|
||||
|
||||
const pixels = stb.stbi_load(path, &width, &height, &channels, stb.STBI_rgb_alpha);
|
||||
defer stb.stbi_image_free(pixels);
|
||||
|
||||
const size: c.VkDeviceSize = @as(u64, @intCast(width)) * @as(u64, @intCast(height)) * 4;
|
||||
const image_buffer = try device.createBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, size);
|
||||
|
||||
const pixel_bytes: [*]u8 = @ptrCast(pixels);
|
||||
var image_data: [*c]u8 = undefined;
|
||||
|
||||
try vk.mapError(c.vkMapMemory(
|
||||
device.handle,
|
||||
image_buffer.memory,
|
||||
0,
|
||||
image_buffer.size,
|
||||
0,
|
||||
@ptrCast(&image_data),
|
||||
));
|
||||
|
||||
@memcpy(image_data[0..size], pixel_bytes[0..size]);
|
||||
|
||||
c.vkUnmapMemory(
|
||||
device.handle,
|
||||
image_buffer.memory,
|
||||
);
|
||||
|
||||
const create_info: c.VkImageCreateInfo = .{
|
||||
.sType = c.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = c.VK_IMAGE_TYPE_2D,
|
||||
.extent = .{
|
||||
.width = @intCast(width),
|
||||
.height = @intCast(height),
|
||||
.depth = 1,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.format = c.VK_FORMAT_R8G8B8A8_SRGB,
|
||||
.tiling = c.VK_IMAGE_TILING_OPTIMAL,
|
||||
.initialLayout = c.VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.usage = c.VK_IMAGE_USAGE_TRANSFER_DST_BIT | c.VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.sharingMode = c.VK_SHARING_MODE_EXCLUSIVE,
|
||||
.samples = c.VK_SAMPLE_COUNT_1_BIT,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
var image: c.VkImage = undefined;
|
||||
var image_memory: c.VkDeviceMemory = undefined;
|
||||
try vk.mapError(c.vkCreateImage(device.handle, &create_info, null, &image));
|
||||
|
||||
var memory_requirements: c.VkMemoryRequirements = undefined;
|
||||
c.vkGetImageMemoryRequirements(device.handle, image, &memory_requirements);
|
||||
|
||||
const alloc_info: c.VkMemoryAllocateInfo = .{
|
||||
.sType = c.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memory_requirements.size,
|
||||
.memoryTypeIndex = try device.findMemoryType(memory_requirements.memoryTypeBits, c.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
|
||||
};
|
||||
|
||||
try vk.mapError(c.vkAllocateMemory(device.handle, &alloc_info, null, &image_memory));
|
||||
try vk.mapError(c.vkBindImageMemory(device.handle, image, image_memory, 0));
|
||||
|
||||
try device.transitionImageLayout(image, c.VK_FORMAT_R8G8B8A8_SRGB, c.VK_IMAGE_LAYOUT_UNDEFINED, c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
try device.copyBufferToImage(image_buffer, image, @intCast(width), @intCast(height));
|
||||
try device.transitionImageLayout(image, c.VK_FORMAT_R8G8B8A8_SRGB, c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
image_buffer.destroy(device.handle);
|
||||
|
||||
const image_view = try createImageView(device, image, c.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
|
||||
return .{
|
||||
.image = image,
|
||||
.image_memory = image_memory,
|
||||
.image_view = image_view,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn destroy(self: Texture, device: vk.Device) void {
|
||||
c.vkDestroyImageView(device.handle, self.image_view, null);
|
||||
c.vkDestroyImage(device.handle, self.image, null);
|
||||
c.vkFreeMemory(device.handle, self.image_memory, null);
|
||||
}
|
||||
|
||||
fn createImageView(device: anytype, image: c.VkImage, format: c.VkFormat) !c.VkImageView {
|
||||
const create_info: c.VkImageViewCreateInfo = .{
|
||||
.sType = c.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image,
|
||||
.viewType = c.VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.subresourceRange = .{
|
||||
.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
var image_view: c.VkImageView = undefined;
|
||||
|
||||
try vk.mapError(c.vkCreateImageView(device.handle, &create_info, null, &image_view));
|
||||
|
||||
return image_view;
|
||||
}
|
||||
185
src/rendering/gltf.zig
Normal file
185
src/rendering/gltf.zig
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
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;
|
||||
const Vec2 = [2]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 readVec2(self: Binary, allocator: Allocator, view: BufferView, count: usize) ![]Vec2 {
|
||||
const data = self.data[view.byteOffset .. view.byteOffset + view.byteLength];
|
||||
const vectors = try allocator.alloc(Vec2, count);
|
||||
|
||||
for (0..count) |i| {
|
||||
vectors[i] = std.mem.bytesAsValue(Vec2, data[(@sizeOf(Vec2) * i) .. (@sizeOf(Vec2) * i) + @sizeOf(Vec2)]).*;
|
||||
}
|
||||
|
||||
return vectors;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub fn parseFile(allocator: Allocator, name: []const u8) !struct { vertices: [][3]f32, normals: [][3]f32, uvs: [][2]f32, indices: []u16 } {
|
||||
const file = try std.fs.cwd().openFile(name, .{});
|
||||
const all = try file.readToEndAlloc(allocator, 1_000_000);
|
||||
defer allocator.free(all);
|
||||
const json_chunk = std.mem.bytesAsValue(Model.Chunk, all[Model.Header.offset..]);
|
||||
|
||||
const parsed = try std.json.parseFromSlice(Model.JsonChunk, allocator, @constCast(all[Model.Chunk.offset .. Model.Chunk.offset + json_chunk.length]), .{ .ignore_unknown_fields = true });
|
||||
defer parsed.deinit();
|
||||
|
||||
const data = parsed.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 normals = try binary.readVec3(allocator, data.bufferViews.?[data.meshes.?[0].primitives.?[0].attributes.?.NORMAL.?], data.accessors.?[data.meshes.?[0].primitives.?[0].attributes.?.NORMAL.?].count);
|
||||
|
||||
const uvs = try binary.readVec2(allocator, data.bufferViews.?[data.meshes.?[0].primitives.?[0].attributes.?.TEXCOORD_0.?], data.accessors.?[data.meshes.?[0].primitives.?[0].attributes.?.TEXCOORD_0.?].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, .normals = normals, .uvs = uvs, .indices = indices };
|
||||
}
|
||||
1471
src/rendering/vulkan.zig
Normal file
1471
src/rendering/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