WIP: Refactor mesh & texture
This commit is contained in:
parent
d318dfa4a2
commit
0d4478acc6
7 changed files with 431 additions and 366 deletions
|
@ -27,7 +27,8 @@ device: Device,
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
pub fn new(
|
pub fn create(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
pdev: vk.PhysicalDevice,
|
pdev: vk.PhysicalDevice,
|
||||||
device: Device,
|
device: Device,
|
||||||
|
@ -36,7 +37,6 @@ pub fn new(
|
||||||
vertices: []const Vertex,
|
vertices: []const Vertex,
|
||||||
indices: []const u32,
|
indices: []const u32,
|
||||||
tex_id: u32,
|
tex_id: u32,
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
) !Self {
|
) !Self {
|
||||||
var self: Self = undefined;
|
var self: Self = undefined;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ pub fn new(
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroyBuffers(self: Self) void {
|
pub fn destroy(self: Self) void {
|
||||||
self.device.destroyBuffer(self.vertex_buffer, null);
|
self.device.destroyBuffer(self.vertex_buffer, null);
|
||||||
self.device.freeMemory(self.vertex_buffer_memory, null);
|
self.device.freeMemory(self.vertex_buffer_memory, null);
|
||||||
|
|
||||||
|
|
|
@ -4,30 +4,92 @@ const zm = @import("zmath");
|
||||||
const ai = @import("assimp.zig").c;
|
const ai = @import("assimp.zig").c;
|
||||||
|
|
||||||
const Mesh = @import("Mesh.zig");
|
const Mesh = @import("Mesh.zig");
|
||||||
const Device = @import("Context.zig").Device;
|
const Context = @import("Context.zig");
|
||||||
|
const Device = Context.Device;
|
||||||
const Instance = @import("Context.zig").Instance;
|
const Instance = @import("Context.zig").Instance;
|
||||||
const Vertex = @import("utilities.zig").Vertex;
|
const Vertex = @import("utilities.zig").Vertex;
|
||||||
|
const StringUtils = @import("string_utils.zig");
|
||||||
|
const Texture = @import("Texture.zig");
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
mesh_list: std.ArrayList(Mesh),
|
mesh_list: std.ArrayList(Mesh),
|
||||||
|
textures: std.ArrayList(Texture),
|
||||||
model: zm.Mat,
|
model: zm.Mat,
|
||||||
|
|
||||||
pub fn new(allocator: std.mem.Allocator, mesh_list: std.ArrayList(Mesh)) Self {
|
sampler_descriptor_sets: std.ArrayList(vk.DescriptorSet),
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
ctx: Context,
|
||||||
|
graphics_command_pool: vk.CommandPool,
|
||||||
|
texture_sampler: vk.Sampler,
|
||||||
|
model_file: []const u8,
|
||||||
|
) Self {
|
||||||
var new_mesh_model: Self = undefined;
|
var new_mesh_model: Self = undefined;
|
||||||
|
|
||||||
new_mesh_model.allocator = allocator;
|
new_mesh_model.allocator = allocator;
|
||||||
new_mesh_model.mesh_list = mesh_list;
|
|
||||||
new_mesh_model.model = zm.identity();
|
new_mesh_model.model = zm.identity();
|
||||||
|
|
||||||
|
new_mesh_model.sampler_descriptor_sets = try std.ArrayList(vk.DescriptorSet)
|
||||||
|
.init(allocator);
|
||||||
|
|
||||||
|
const path = try StringUtils.concat("assets/models/", model_file, allocator);
|
||||||
|
defer allocator.free(path);
|
||||||
|
|
||||||
|
// Import model scene
|
||||||
|
const scene = ai.aiImportFile(
|
||||||
|
path.ptr,
|
||||||
|
ai.aiProcess_Triangulate | ai.aiProcess_FlipUVs | ai.aiProcess_JoinIdenticalVertices,
|
||||||
|
);
|
||||||
|
defer ai.aiReleaseImport(scene);
|
||||||
|
|
||||||
|
// Get array of all materials with 1:1 ID placement
|
||||||
|
const texture_names = try ai.loadMaterials(allocator, scene);
|
||||||
|
defer {
|
||||||
|
for (0..texture_names.items.len) |i| {
|
||||||
|
if (texture_names.items[i]) |texture_name| {
|
||||||
|
allocator.free(texture_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
texture_names.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion from the material list IDs to our descriptor array IDs
|
||||||
|
new_mesh_model.textures = try std.ArrayList(Texture).initCapacity(allocator, texture_names.items.len);
|
||||||
|
|
||||||
|
// Loop over texture names and create textures for them
|
||||||
|
for (texture_names.items) |texture_name| {
|
||||||
|
if (texture_name != null) {
|
||||||
|
// Create texture and set value to index of new texture
|
||||||
|
new_mesh_model.textures.appendAssumeCapacity(try Texture.create(texture_name.?));
|
||||||
|
} else {
|
||||||
|
// If material had no texture, set to 0 to indicate no texture. Texture 0 will be reserver for a default texture
|
||||||
|
// TODO Put the default texture somewhere else where it's shared
|
||||||
|
new_mesh_model.textures.appendAssumeCapacity(try Texture.create("giraffe.png"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load in all our meshes
|
||||||
|
new_mesh_model.mesh_list = try loadNode(
|
||||||
|
allocator,
|
||||||
|
ctx.instance,
|
||||||
|
ctx.physical_device,
|
||||||
|
ctx.device,
|
||||||
|
ctx.graphics_queue.handle,
|
||||||
|
graphics_command_pool,
|
||||||
|
scene.*.mRootNode,
|
||||||
|
scene,
|
||||||
|
);
|
||||||
|
|
||||||
return new_mesh_model;
|
return new_mesh_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *Self) void {
|
pub fn destroy(self: *Self) void {
|
||||||
for (0..self.mesh_list.items.len) |i| {
|
for (0..self.mesh_list.items.len) |i| {
|
||||||
self.mesh_list.items[i].destroyBuffers();
|
self.mesh_list.items[i].destroy();
|
||||||
}
|
}
|
||||||
self.mesh_list.deinit();
|
self.mesh_list.deinit();
|
||||||
}
|
}
|
||||||
|
@ -40,50 +102,6 @@ pub fn getMesh(self: Self, idx: usize) !Mesh {
|
||||||
return self.mesh_list.items[idx];
|
return self.mesh_list.items[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn loadMaterials(allocator: std.mem.Allocator, scene: *const ai.aiScene) !std.ArrayList(?[]const u8) {
|
|
||||||
// Create 1:1 sized list of textures
|
|
||||||
var texture_list = try std.ArrayList(?[]const u8).initCapacity(allocator, scene.mNumMaterials);
|
|
||||||
|
|
||||||
// Go through each material and copy its texture file name (if it exists)
|
|
||||||
for (0..scene.mNumMaterials) |i| {
|
|
||||||
// Get the material
|
|
||||||
const material = scene.mMaterials[i];
|
|
||||||
|
|
||||||
// Initialise the texture to empty string (will be replaced if the texture exists)
|
|
||||||
// try texture_list.append("");
|
|
||||||
|
|
||||||
// Check for diffuse texture (standard detail texture)
|
|
||||||
if (ai.aiGetMaterialTextureCount(material, ai.aiTextureType_DIFFUSE) != 0) {
|
|
||||||
// Get the path of the texture file
|
|
||||||
var path: ai.aiString = undefined;
|
|
||||||
if (ai.aiGetMaterialTexture(
|
|
||||||
material,
|
|
||||||
ai.aiTextureType_DIFFUSE,
|
|
||||||
0,
|
|
||||||
&path,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
) == ai.AI_SUCCESS) {
|
|
||||||
// Cut of any directory information already present
|
|
||||||
var it = std.mem.splitBackwardsAny(u8, &path.data, "\\/");
|
|
||||||
if (it.next()) |filename| {
|
|
||||||
texture_list.appendAssumeCapacity(try allocator.dupe(u8, filename));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
texture_list.appendAssumeCapacity(null);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
texture_list.appendAssumeCapacity(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loadNode(
|
pub fn loadNode(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
|
@ -93,7 +111,6 @@ pub fn loadNode(
|
||||||
transfer_command_pool: vk.CommandPool,
|
transfer_command_pool: vk.CommandPool,
|
||||||
node: *const ai.aiNode,
|
node: *const ai.aiNode,
|
||||||
scene: *const ai.aiScene,
|
scene: *const ai.aiScene,
|
||||||
mat_to_tex: []u32,
|
|
||||||
) !std.ArrayList(Mesh) {
|
) !std.ArrayList(Mesh) {
|
||||||
var mesh_list = std.ArrayList(Mesh).init(allocator);
|
var mesh_list = std.ArrayList(Mesh).init(allocator);
|
||||||
|
|
||||||
|
@ -108,7 +125,6 @@ pub fn loadNode(
|
||||||
transfer_queue,
|
transfer_queue,
|
||||||
transfer_command_pool,
|
transfer_command_pool,
|
||||||
scene.mMeshes[node.mMeshes[i]],
|
scene.mMeshes[node.mMeshes[i]],
|
||||||
mat_to_tex,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +139,6 @@ pub fn loadNode(
|
||||||
transfer_command_pool,
|
transfer_command_pool,
|
||||||
node.mChildren[i],
|
node.mChildren[i],
|
||||||
scene,
|
scene,
|
||||||
mat_to_tex,
|
|
||||||
);
|
);
|
||||||
defer new_list.deinit();
|
defer new_list.deinit();
|
||||||
|
|
||||||
|
@ -141,7 +156,6 @@ pub fn loadMesh(
|
||||||
transfer_queue: vk.Queue,
|
transfer_queue: vk.Queue,
|
||||||
transfer_command_pool: vk.CommandPool,
|
transfer_command_pool: vk.CommandPool,
|
||||||
mesh: *const ai.aiMesh,
|
mesh: *const ai.aiMesh,
|
||||||
mat_to_tex: []u32,
|
|
||||||
) !Mesh {
|
) !Mesh {
|
||||||
var vertices = try std.ArrayList(Vertex).initCapacity(allocator, mesh.mNumVertices);
|
var vertices = try std.ArrayList(Vertex).initCapacity(allocator, mesh.mNumVertices);
|
||||||
var indices = std.ArrayList(u32).init(allocator);
|
var indices = std.ArrayList(u32).init(allocator);
|
||||||
|
@ -180,7 +194,8 @@ pub fn loadMesh(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return try Mesh.new(
|
return try Mesh.create(
|
||||||
|
allocator,
|
||||||
instance,
|
instance,
|
||||||
pdev,
|
pdev,
|
||||||
device,
|
device,
|
||||||
|
@ -188,7 +203,6 @@ pub fn loadMesh(
|
||||||
transfer_command_pool,
|
transfer_command_pool,
|
||||||
vertices.items,
|
vertices.items,
|
||||||
indices.items,
|
indices.items,
|
||||||
mat_to_tex[mesh.mMaterialIndex],
|
mesh.mMaterialIndex,
|
||||||
allocator,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ const Context = @import("Context.zig");
|
||||||
const Instance = Context.Instance;
|
const Instance = Context.Instance;
|
||||||
const QueueUtils = @import("queue_utils.zig");
|
const QueueUtils = @import("queue_utils.zig");
|
||||||
const Utilities = @import("utilities.zig");
|
const Utilities = @import("utilities.zig");
|
||||||
|
const Image = @import("image.zig");
|
||||||
|
|
||||||
pub const SwapchainDetails = struct {
|
pub const SwapchainDetails = struct {
|
||||||
surface_capabilities: vk.SurfaceCapabilitiesKHR,
|
surface_capabilities: vk.SurfaceCapabilitiesKHR,
|
||||||
|
@ -109,83 +110,25 @@ pub fn create(allocator: std.mem.Allocator, context: Context) !Self {
|
||||||
for (images, 0..) |image, i| {
|
for (images, 0..) |image, i| {
|
||||||
self.swapchain_images[i] = .{
|
self.swapchain_images[i] = .{
|
||||||
.image = image,
|
.image = image,
|
||||||
.image_view = try self.createImageView(image, self.swapchain_image_format, .{ .color_bit = true }),
|
.image_view = try Image.createImageView(self.ctx, image, self.swapchain_image_format, .{ .color_bit = true }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createImage(
|
pub fn destroy(self: *Self) void {
|
||||||
self: *Self,
|
for (self.swapchain_framebuffers) |framebuffer| {
|
||||||
width: u32,
|
self.ctx.device.destroyFramebuffer(framebuffer, null);
|
||||||
height: u32,
|
|
||||||
format: vk.Format,
|
|
||||||
tiling: vk.ImageTiling,
|
|
||||||
use_flags: vk.ImageUsageFlags,
|
|
||||||
prop_flags: vk.MemoryPropertyFlags,
|
|
||||||
image_memory: *vk.DeviceMemory,
|
|
||||||
) !vk.Image {
|
|
||||||
// -- Create Image --
|
|
||||||
const image_create_info: vk.ImageCreateInfo = .{
|
|
||||||
.image_type = .@"2d", // Type of image (1D, 2D or 3D)
|
|
||||||
.extent = .{
|
|
||||||
.width = width, // Width of image extent
|
|
||||||
.height = height, // Height of image extent
|
|
||||||
.depth = 1, // Depth of image (just 1, no 3D aspecct)
|
|
||||||
},
|
|
||||||
.mip_levels = 1, // Number of mipmap levels
|
|
||||||
.array_layers = 1, // Number of level in image array
|
|
||||||
.format = format, // Format type of image
|
|
||||||
.tiling = tiling, // How image data should be tiled (arranged for optimal reading)
|
|
||||||
.initial_layout = .undefined, // Layout of image data on creation
|
|
||||||
.usage = use_flags, // Bit flags defining what image will be used for
|
|
||||||
.samples = .{ .@"1_bit" = true }, // Number of samples for multi-sampling
|
|
||||||
.sharing_mode = .exclusive, // Whether image can be shared between queues
|
|
||||||
};
|
|
||||||
|
|
||||||
const image = try self.ctx.device.createImage(&image_create_info, null);
|
|
||||||
|
|
||||||
// -- Create memory for image --
|
|
||||||
// Get memory requirements for a type of image
|
|
||||||
const memory_requirements = self.ctx.device.getImageMemoryRequirements(image);
|
|
||||||
|
|
||||||
// Allocate memory using image requirements and user-defined properties
|
|
||||||
const memory_alloc_info: vk.MemoryAllocateInfo = .{
|
|
||||||
.allocation_size = memory_requirements.size,
|
|
||||||
.memory_type_index = Utilities.findMemoryTypeIndex(self.ctx.physical_device, self.ctx.instance, memory_requirements.memory_type_bits, prop_flags),
|
|
||||||
};
|
|
||||||
|
|
||||||
image_memory.* = try self.ctx.device.allocateMemory(&memory_alloc_info, null);
|
|
||||||
|
|
||||||
// Connect memory to image
|
|
||||||
try self.ctx.device.bindImageMemory(image, image_memory.*, 0);
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
self.allocator.free(self.swapchain_framebuffers);
|
||||||
|
|
||||||
pub fn createImageView(self: Self, image: vk.Image, format: vk.Format, aspect_flags: vk.ImageAspectFlags) !vk.ImageView {
|
for (self.swapchain_images) |swapchain_image| {
|
||||||
const image_view_create_info: vk.ImageViewCreateInfo = .{
|
self.ctx.device.destroyImageView(swapchain_image.image_view, null);
|
||||||
.image = image,
|
}
|
||||||
.format = format,
|
self.allocator.free(self.swapchain_images);
|
||||||
.view_type = .@"2d",
|
|
||||||
.components = .{
|
|
||||||
// Used for remapping rgba values to other rgba values
|
|
||||||
.r = .identity,
|
|
||||||
.g = .identity,
|
|
||||||
.b = .identity,
|
|
||||||
.a = .identity,
|
|
||||||
},
|
|
||||||
.subresource_range = .{
|
|
||||||
.aspect_mask = aspect_flags, // Which aspect of image to view (e.g.: colour, depth, stencil, etc...)
|
|
||||||
.base_mip_level = 0, // Start mipmap level to view from
|
|
||||||
.level_count = 1, // Number of mipmap levels to view
|
|
||||||
.base_array_layer = 0, // Start array level to view from
|
|
||||||
.layer_count = 1, // Number of array levels to view
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return try self.ctx.device.createImageView(&image_view_create_info, null);
|
self.ctx.device.destroySwapchainKHR(self.handle, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getSwapchainDetails(allocator: std.mem.Allocator, instance: Instance, pdev: vk.PhysicalDevice, surface: vk.SurfaceKHR) !SwapchainDetails {
|
pub fn getSwapchainDetails(allocator: std.mem.Allocator, instance: Instance, pdev: vk.PhysicalDevice, surface: vk.SurfaceKHR) !SwapchainDetails {
|
||||||
|
|
198
src/Texture.zig
Normal file
198
src/Texture.zig
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const vk = @import("vulkan");
|
||||||
|
const img = @import("zstbi");
|
||||||
|
|
||||||
|
const Context = @import("Context.zig");
|
||||||
|
const Image = @import("image.zig");
|
||||||
|
const Utilities = @import("utilities.zig");
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
ctx: Context,
|
||||||
|
|
||||||
|
idx: u32,
|
||||||
|
texture_image: vk.Image,
|
||||||
|
texture_image_memory: vk.DeviceMemory,
|
||||||
|
texture_image_view: vk.ImageView,
|
||||||
|
|
||||||
|
sampler_descriptor_set: vk.DescriptorSet,
|
||||||
|
|
||||||
|
image_file: img.Image,
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
file_name: []const u8,
|
||||||
|
ctx: Context,
|
||||||
|
graphics_command_pool: vk.CommandPool,
|
||||||
|
texture_sampler: vk.Sampler,
|
||||||
|
sampler_set_layout: vk.DescriptorSetLayout,
|
||||||
|
sampler_descriptor_pool: vk.DescriptorPool,
|
||||||
|
) Self {
|
||||||
|
var self: Self = undefined;
|
||||||
|
|
||||||
|
self.ctx = ctx;
|
||||||
|
|
||||||
|
// Create texture image and get its location in the array
|
||||||
|
const texture_image_loc = try self.createTextureImage(file_name, graphics_command_pool);
|
||||||
|
|
||||||
|
// Create image view
|
||||||
|
self.texture_image_view = try Image.createImageView(
|
||||||
|
ctx,
|
||||||
|
self.texture_images.items[texture_image_loc],
|
||||||
|
.r8g8b8a8_srgb,
|
||||||
|
.{ .color_bit = true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create texture descriptor
|
||||||
|
try self.createTextureDescriptor(
|
||||||
|
texture_sampler,
|
||||||
|
sampler_set_layout,
|
||||||
|
sampler_descriptor_pool,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return location of set with texture
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Self) void {}
|
||||||
|
|
||||||
|
fn createTextureImage(
|
||||||
|
self: *Self,
|
||||||
|
file_name: []const u8,
|
||||||
|
graphics_command_pool: vk.CommandPool,
|
||||||
|
) !u32 {
|
||||||
|
// Load image file
|
||||||
|
var width: u32 = undefined;
|
||||||
|
var height: u32 = undefined;
|
||||||
|
var image_size: vk.DeviceSize = undefined;
|
||||||
|
const image = try self.loadTextureFile(file_name, &width, &height, &image_size);
|
||||||
|
|
||||||
|
// Create staging buffer to hold loaded data, ready to copy to device
|
||||||
|
var image_staging_buffer: vk.Buffer = undefined;
|
||||||
|
var image_staging_buffer_memory: vk.DeviceMemory = undefined;
|
||||||
|
defer self.ctx.device.destroyBuffer(image_staging_buffer, null);
|
||||||
|
defer self.ctx.device.freeMemory(image_staging_buffer_memory, null);
|
||||||
|
|
||||||
|
try Utilities.createBuffer(
|
||||||
|
self.ctx.physical_device,
|
||||||
|
self.ctx.instance,
|
||||||
|
self.ctx.device,
|
||||||
|
image_size,
|
||||||
|
.{ .transfer_src_bit = true },
|
||||||
|
.{ .host_visible_bit = true, .host_coherent_bit = true },
|
||||||
|
&image_staging_buffer,
|
||||||
|
&image_staging_buffer_memory,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copy data to staging buffer
|
||||||
|
const data = try self.ctx.device.mapMemory(image_staging_buffer_memory, 0, image_size, .{});
|
||||||
|
const image_data: [*]u8 = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
@memcpy(image_data, image[0..]);
|
||||||
|
self.ctx.device.unmapMemory(image_staging_buffer_memory);
|
||||||
|
|
||||||
|
// Create image to hold final texture
|
||||||
|
var tex_image_memory: vk.DeviceMemory = undefined;
|
||||||
|
const tex_image = try Image.createImage(
|
||||||
|
self.ctx,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
.r8g8b8a8_srgb,
|
||||||
|
.optimal,
|
||||||
|
.{ .transfer_dst_bit = true, .sampled_bit = true },
|
||||||
|
.{ .device_local_bit = true },
|
||||||
|
&tex_image_memory,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Transition image to be DST for copy operation
|
||||||
|
try Utilities.transitionImageLayout(
|
||||||
|
self.ctx.device,
|
||||||
|
self.ctx.graphics_queue.handle,
|
||||||
|
graphics_command_pool,
|
||||||
|
tex_image,
|
||||||
|
.undefined,
|
||||||
|
.transfer_dst_optimal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copy data to image
|
||||||
|
try Utilities.copyImageBuffer(
|
||||||
|
self.ctx.device,
|
||||||
|
self.ctx.graphics_queue.handle,
|
||||||
|
graphics_command_pool,
|
||||||
|
image_staging_buffer,
|
||||||
|
tex_image,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Transition image to be shader readable for shader usage
|
||||||
|
try Utilities.transitionImageLayout(
|
||||||
|
self.ctx.device,
|
||||||
|
self.ctx.graphics_queue.handle,
|
||||||
|
graphics_command_pool,
|
||||||
|
tex_image,
|
||||||
|
.transfer_dst_optimal,
|
||||||
|
.shader_read_only_optimal,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.texture_image = tex_image;
|
||||||
|
self.texture_image_memory = tex_image_memory;
|
||||||
|
|
||||||
|
// Return index of new texture image
|
||||||
|
return @intCast(self.texture_images.items.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createTextureDescriptor(
|
||||||
|
self: *Self,
|
||||||
|
texture_sampler: vk.Sampler,
|
||||||
|
sampler_set_layout: vk.DescriptorSetLayout,
|
||||||
|
sampler_descriptor_pool: vk.DescriptorPool,
|
||||||
|
) !u32 {
|
||||||
|
// Descriptor set allocation info
|
||||||
|
const set_alloc_info: vk.DescriptorSetAllocateInfo = .{
|
||||||
|
.descriptor_pool = sampler_descriptor_pool,
|
||||||
|
.descriptor_set_count = 1,
|
||||||
|
.p_set_layouts = @ptrCast(&sampler_set_layout),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate descriptor sets
|
||||||
|
try self.ctx.device.allocateDescriptorSets(&set_alloc_info, @ptrCast(&self.sampler_descriptor_set));
|
||||||
|
|
||||||
|
const image_info: vk.DescriptorImageInfo = .{
|
||||||
|
.image_layout = .shader_read_only_optimal, // Image layout when in use
|
||||||
|
.image_view = self.texture_image_view, // Image to bind to set
|
||||||
|
.sampler = texture_sampler, // Sampler to use for set
|
||||||
|
};
|
||||||
|
|
||||||
|
// Descriptor write info
|
||||||
|
const descriptor_write: vk.WriteDescriptorSet = .{
|
||||||
|
.dst_set = self.sampler_descriptor_set,
|
||||||
|
.dst_binding = 0,
|
||||||
|
.dst_array_element = 0,
|
||||||
|
.descriptor_type = .combined_image_sampler,
|
||||||
|
.descriptor_count = 1,
|
||||||
|
.p_image_info = @ptrCast(&image_info),
|
||||||
|
.p_buffer_info = undefined,
|
||||||
|
.p_texel_buffer_view = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the new descriptor set
|
||||||
|
self.ctx.device.updateDescriptorSets(1, @ptrCast(&descriptor_write), 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loadTextureFile(self: *Self, file_name: []const u8, width: *u32, height: *u32, image_size: *vk.DeviceSize) !void {
|
||||||
|
const path_concat = [2][]const u8{ "./assets/textures/", file_name };
|
||||||
|
const path = try std.mem.concatWithSentinel(self.allocator, u8, &path_concat, 0);
|
||||||
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
|
const image = try img.Image.loadFromFile(path, 0);
|
||||||
|
|
||||||
|
width.* = image.width;
|
||||||
|
height.* = image.height;
|
||||||
|
|
||||||
|
// Calculate image size using given and known data
|
||||||
|
image_size.* = width.* * height.* * 4;
|
||||||
|
|
||||||
|
self.image_file = image;
|
||||||
|
}
|
|
@ -1,9 +1,53 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
pub const c = @cImport({
|
pub const c = @cImport({
|
||||||
@cInclude("assimp/cimport.h");
|
@cInclude("assimp/cimport.h");
|
||||||
@cInclude("assimp/scene.h");
|
@cInclude("assimp/scene.h");
|
||||||
@cInclude("assimp/postprocess.h");
|
@cInclude("assimp/postprocess.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
// pub fn importFile(path: [:0]const u8, flags: c_uint) *const c.aiScene {
|
/// Load the texture material names in a scene.
|
||||||
// return c.aiImportFile(path.ptr, flags);
|
/// Don't forget to free each element after use.
|
||||||
// }
|
pub fn loadMaterials(allocator: std.mem.Allocator, scene: *const c.aiScene) !std.ArrayList(?[]const u8) {
|
||||||
|
// Create 1:1 sized list of textures
|
||||||
|
var texture_list = try std.ArrayList(?[]const u8).initCapacity(allocator, scene.mNumMaterials);
|
||||||
|
|
||||||
|
// Go through each material and copy its texture file name (if it exists)
|
||||||
|
for (0..scene.mNumMaterials) |i| {
|
||||||
|
// Get the material
|
||||||
|
const material = scene.mMaterials[i];
|
||||||
|
|
||||||
|
// Initialise the texture to empty string (will be replaced if the texture exists)
|
||||||
|
// try texture_list.append("");
|
||||||
|
|
||||||
|
// Check for diffuse texture (standard detail texture)
|
||||||
|
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_DIFFUSE) != 0) {
|
||||||
|
// Get the path of the texture file
|
||||||
|
var path: c.aiString = undefined;
|
||||||
|
if (c.aiGetMaterialTexture(
|
||||||
|
material,
|
||||||
|
c.aiTextureType_DIFFUSE,
|
||||||
|
0,
|
||||||
|
&path,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
) == c.AI_SUCCESS) {
|
||||||
|
// Cut of any directory information already present
|
||||||
|
var it = std.mem.splitBackwardsAny(u8, &path.data, "\\/");
|
||||||
|
if (it.next()) |filename| {
|
||||||
|
texture_list.appendAssumeCapacity(try allocator.dupe(u8, filename));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
texture_list.appendAssumeCapacity(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
texture_list.appendAssumeCapacity(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture_list;
|
||||||
|
}
|
||||||
|
|
82
src/image.zig
Normal file
82
src/image.zig
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const vk = @import("vulkan");
|
||||||
|
|
||||||
|
const Context = @import("Context.zig");
|
||||||
|
const Utilities = @import("utilities.zig");
|
||||||
|
|
||||||
|
pub fn createImage(
|
||||||
|
ctx: Context,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
format: vk.Format,
|
||||||
|
tiling: vk.ImageTiling,
|
||||||
|
use_flags: vk.ImageUsageFlags,
|
||||||
|
prop_flags: vk.MemoryPropertyFlags,
|
||||||
|
image_memory: *vk.DeviceMemory,
|
||||||
|
) !vk.Image {
|
||||||
|
// -- Create Image --
|
||||||
|
const image_create_info: vk.ImageCreateInfo = .{
|
||||||
|
.image_type = .@"2d", // Type of image (1D, 2D or 3D)
|
||||||
|
.extent = .{
|
||||||
|
.width = width, // Width of image extent
|
||||||
|
.height = height, // Height of image extent
|
||||||
|
.depth = 1, // Depth of image (just 1, no 3D aspecct)
|
||||||
|
},
|
||||||
|
.mip_levels = 1, // Number of mipmap levels
|
||||||
|
.array_layers = 1, // Number of level in image array
|
||||||
|
.format = format, // Format type of image
|
||||||
|
.tiling = tiling, // How image data should be tiled (arranged for optimal reading)
|
||||||
|
.initial_layout = .undefined, // Layout of image data on creation
|
||||||
|
.usage = use_flags, // Bit flags defining what image will be used for
|
||||||
|
.samples = .{ .@"1_bit" = true }, // Number of samples for multi-sampling
|
||||||
|
.sharing_mode = .exclusive, // Whether image can be shared between queues
|
||||||
|
};
|
||||||
|
|
||||||
|
const image = try ctx.device.createImage(&image_create_info, null);
|
||||||
|
|
||||||
|
// -- Create memory for image --
|
||||||
|
// Get memory requirements for a type of image
|
||||||
|
const memory_requirements = ctx.device.getImageMemoryRequirements(image);
|
||||||
|
|
||||||
|
// Allocate memory using image requirements and user-defined properties
|
||||||
|
const memory_alloc_info: vk.MemoryAllocateInfo = .{
|
||||||
|
.allocation_size = memory_requirements.size,
|
||||||
|
.memory_type_index = Utilities.findMemoryTypeIndex(ctx.physical_device, ctx.instance, memory_requirements.memory_type_bits, prop_flags),
|
||||||
|
};
|
||||||
|
|
||||||
|
image_memory.* = try ctx.device.allocateMemory(&memory_alloc_info, null);
|
||||||
|
|
||||||
|
// Connect memory to image
|
||||||
|
try ctx.device.bindImageMemory(image, image_memory.*, 0);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createImageView(
|
||||||
|
ctx: Context,
|
||||||
|
image: vk.Image,
|
||||||
|
format: vk.Format,
|
||||||
|
aspect_flags: vk.ImageAspectFlags,
|
||||||
|
) !vk.ImageView {
|
||||||
|
const image_view_create_info: vk.ImageViewCreateInfo = .{
|
||||||
|
.image = image,
|
||||||
|
.format = format,
|
||||||
|
.view_type = .@"2d",
|
||||||
|
.components = .{
|
||||||
|
// Used for remapping rgba values to other rgba values
|
||||||
|
.r = .identity,
|
||||||
|
.g = .identity,
|
||||||
|
.b = .identity,
|
||||||
|
.a = .identity,
|
||||||
|
},
|
||||||
|
.subresource_range = .{
|
||||||
|
.aspect_mask = aspect_flags, // Which aspect of image to view (e.g.: colour, depth, stencil, etc...)
|
||||||
|
.base_mip_level = 0, // Start mipmap level to view from
|
||||||
|
.level_count = 1, // Number of mipmap levels to view
|
||||||
|
.base_array_layer = 0, // Start array level to view from
|
||||||
|
.layer_count = 1, // Number of array levels to view
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return try ctx.device.createImageView(&image_view_create_info, null);
|
||||||
|
}
|
|
@ -14,14 +14,17 @@ const Vertex = Utilities.Vertex;
|
||||||
const Vector3 = Utilities.Vector3;
|
const Vector3 = Utilities.Vector3;
|
||||||
|
|
||||||
const Context = @import("Context.zig");
|
const Context = @import("Context.zig");
|
||||||
const Instance = @import("Context.zig").Instance;
|
const Instance = Context.Instance;
|
||||||
const Swapchain = @import("Swapchain.zig");
|
const Swapchain = @import("Swapchain.zig");
|
||||||
|
const Texture = @import("Texture.zig");
|
||||||
|
const Image = @import("image.zig");
|
||||||
|
|
||||||
const Mesh = @import("Mesh.zig");
|
const Mesh = @import("Mesh.zig");
|
||||||
const MeshModel = @import("MeshModel.zig");
|
const MeshModel = @import("MeshModel.zig");
|
||||||
|
|
||||||
const MAX_FRAME_DRAWS: u32 = 2;
|
const MAX_FRAME_DRAWS: u32 = 2;
|
||||||
const MAX_OBJECTS: u32 = 20;
|
const MAX_OBJECTS: u32 = 20;
|
||||||
|
|
||||||
pub const CommandBuffer = vk.CommandBufferProxy(Context.apis);
|
pub const CommandBuffer = vk.CommandBufferProxy(Context.apis);
|
||||||
|
|
||||||
const UboViewProjection = struct {
|
const UboViewProjection = struct {
|
||||||
|
@ -69,7 +72,7 @@ pub const VulkanRenderer = struct {
|
||||||
sampler_descriptor_pool: vk.DescriptorPool,
|
sampler_descriptor_pool: vk.DescriptorPool,
|
||||||
input_descriptor_pool: vk.DescriptorPool,
|
input_descriptor_pool: vk.DescriptorPool,
|
||||||
descriptor_sets: []vk.DescriptorSet,
|
descriptor_sets: []vk.DescriptorSet,
|
||||||
sampler_descriptor_sets: std.ArrayList(vk.DescriptorSet),
|
// sampler_descriptor_sets: std.ArrayList(vk.DescriptorSet),
|
||||||
input_descriptor_sets: []vk.DescriptorSet,
|
input_descriptor_sets: []vk.DescriptorSet,
|
||||||
|
|
||||||
vp_uniform_buffer: []vk.Buffer,
|
vp_uniform_buffer: []vk.Buffer,
|
||||||
|
@ -79,10 +82,7 @@ pub const VulkanRenderer = struct {
|
||||||
command_buffers: []CommandBuffer,
|
command_buffers: []CommandBuffer,
|
||||||
|
|
||||||
// Assets
|
// Assets
|
||||||
image_files: std.ArrayList(img.Image),
|
textures: std.ArrayList(Texture),
|
||||||
texture_images: std.ArrayList(vk.Image),
|
|
||||||
texture_image_memory: std.ArrayList(vk.DeviceMemory),
|
|
||||||
texture_image_views: std.ArrayList(vk.ImageView),
|
|
||||||
model_list: std.ArrayList(MeshModel),
|
model_list: std.ArrayList(MeshModel),
|
||||||
|
|
||||||
// Pipeline
|
// Pipeline
|
||||||
|
@ -133,9 +133,7 @@ pub const VulkanRenderer = struct {
|
||||||
try self.createSynchronisation();
|
try self.createSynchronisation();
|
||||||
|
|
||||||
self.image_files = std.ArrayList(img.Image).init(self.allocator);
|
self.image_files = std.ArrayList(img.Image).init(self.allocator);
|
||||||
self.texture_images = std.ArrayList(vk.Image).init(self.allocator);
|
self.textures = std.ArrayList(Texture).init(self.allocator);
|
||||||
self.texture_image_memory = std.ArrayList(vk.DeviceMemory).init(self.allocator);
|
|
||||||
self.texture_image_views = std.ArrayList(vk.ImageView).init(self.allocator);
|
|
||||||
self.model_list = std.ArrayList(MeshModel).init(allocator);
|
self.model_list = std.ArrayList(MeshModel).init(allocator);
|
||||||
|
|
||||||
const aspect: f32 = @as(f32, @floatFromInt(self.swapchain.extent.width)) / @as(f32, @floatFromInt(self.swapchain.extent.height));
|
const aspect: f32 = @as(f32, @floatFromInt(self.swapchain.extent.width)) / @as(f32, @floatFromInt(self.swapchain.extent.height));
|
||||||
|
@ -300,24 +298,13 @@ pub const VulkanRenderer = struct {
|
||||||
self.allocator.free(self.command_buffers);
|
self.allocator.free(self.command_buffers);
|
||||||
self.ctx.device.destroyCommandPool(self.graphics_command_pool, null);
|
self.ctx.device.destroyCommandPool(self.graphics_command_pool, null);
|
||||||
|
|
||||||
for (self.swapchain.swapchain_framebuffers) |framebuffer| {
|
|
||||||
self.ctx.device.destroyFramebuffer(framebuffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.allocator.free(self.swapchain.swapchain_framebuffers);
|
|
||||||
|
|
||||||
self.ctx.device.destroyPipeline(self.second_pipeline, null);
|
self.ctx.device.destroyPipeline(self.second_pipeline, null);
|
||||||
self.ctx.device.destroyPipelineLayout(self.second_pipeline_layout, null);
|
self.ctx.device.destroyPipelineLayout(self.second_pipeline_layout, null);
|
||||||
self.ctx.device.destroyPipeline(self.graphics_pipeline, null);
|
self.ctx.device.destroyPipeline(self.graphics_pipeline, null);
|
||||||
self.ctx.device.destroyPipelineLayout(self.pipeline_layout, null);
|
self.ctx.device.destroyPipelineLayout(self.pipeline_layout, null);
|
||||||
self.ctx.device.destroyRenderPass(self.render_pass, null);
|
self.ctx.device.destroyRenderPass(self.render_pass, null);
|
||||||
|
|
||||||
for (self.swapchain.swapchain_images) |swapchain_image| {
|
self.swapchain.deinit();
|
||||||
self.ctx.device.destroyImageView(swapchain_image.image_view, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.allocator.free(self.swapchain.swapchain_images);
|
|
||||||
self.ctx.device.destroySwapchainKHR(self.swapchain.handle, null);
|
|
||||||
|
|
||||||
self.ctx.deinit();
|
self.ctx.deinit();
|
||||||
}
|
}
|
||||||
|
@ -569,7 +556,8 @@ pub const VulkanRenderer = struct {
|
||||||
|
|
||||||
// Create colour buffers
|
// Create colour buffers
|
||||||
for (0..self.colour_buffer_image.len) |i| {
|
for (0..self.colour_buffer_image.len) |i| {
|
||||||
self.colour_buffer_image[i] = try self.swapchain.createImage(
|
self.colour_buffer_image[i] = try Image.createImage(
|
||||||
|
self.ctx,
|
||||||
self.swapchain.extent.width,
|
self.swapchain.extent.width,
|
||||||
self.swapchain.extent.height,
|
self.swapchain.extent.height,
|
||||||
colour_format,
|
colour_format,
|
||||||
|
@ -579,7 +567,8 @@ pub const VulkanRenderer = struct {
|
||||||
&self.colour_buffer_image_memory[i],
|
&self.colour_buffer_image_memory[i],
|
||||||
);
|
);
|
||||||
|
|
||||||
self.colour_buffer_image_view[i] = try self.swapchain.createImageView(
|
self.colour_buffer_image_view[i] = try Image.createImageView(
|
||||||
|
self.ctx,
|
||||||
self.colour_buffer_image[i],
|
self.colour_buffer_image[i],
|
||||||
colour_format,
|
colour_format,
|
||||||
.{ .color_bit = true },
|
.{ .color_bit = true },
|
||||||
|
@ -604,7 +593,8 @@ pub const VulkanRenderer = struct {
|
||||||
|
|
||||||
for (0..self.depth_buffer_image.len) |i| {
|
for (0..self.depth_buffer_image.len) |i| {
|
||||||
// Create depth buffer image
|
// Create depth buffer image
|
||||||
self.depth_buffer_image[i] = try self.swapchain.createImage(
|
self.depth_buffer_image[i] = try Image.createImage(
|
||||||
|
self.ctx,
|
||||||
self.swapchain.extent.width,
|
self.swapchain.extent.width,
|
||||||
self.swapchain.extent.height,
|
self.swapchain.extent.height,
|
||||||
self.depth_format,
|
self.depth_format,
|
||||||
|
@ -615,7 +605,7 @@ pub const VulkanRenderer = struct {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create depth buffer image view
|
// Create depth buffer image view
|
||||||
self.depth_buffer_image_view[i] = try self.swapchain.createImageView(self.depth_buffer_image[i], self.depth_format, .{ .depth_bit = true });
|
self.depth_buffer_image_view[i] = try Image.createImageView(self.ctx, self.depth_buffer_image[i], self.depth_format, .{ .depth_bit = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1306,221 +1296,15 @@ pub const VulkanRenderer = struct {
|
||||||
try command_buffer.endCommandBuffer();
|
try command_buffer.endCommandBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createTextureImage(self: *Self, file_name: []const u8) !u32 {
|
|
||||||
// Load image file
|
|
||||||
var width: u32 = undefined;
|
|
||||||
var height: u32 = undefined;
|
|
||||||
var image_size: vk.DeviceSize = undefined;
|
|
||||||
const image = try self.loadTextureFile(file_name, &width, &height, &image_size);
|
|
||||||
|
|
||||||
// Create staging buffer to hold loaded data, ready to copy to device
|
|
||||||
var image_staging_buffer: vk.Buffer = undefined;
|
|
||||||
var image_staging_buffer_memory: vk.DeviceMemory = undefined;
|
|
||||||
defer self.ctx.device.destroyBuffer(image_staging_buffer, null);
|
|
||||||
defer self.ctx.device.freeMemory(image_staging_buffer_memory, null);
|
|
||||||
|
|
||||||
try Utilities.createBuffer(
|
|
||||||
self.ctx.physical_device,
|
|
||||||
self.ctx.instance,
|
|
||||||
self.ctx.device,
|
|
||||||
image_size,
|
|
||||||
.{ .transfer_src_bit = true },
|
|
||||||
.{ .host_visible_bit = true, .host_coherent_bit = true },
|
|
||||||
&image_staging_buffer,
|
|
||||||
&image_staging_buffer_memory,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Copy data to staging buffer
|
|
||||||
const data = try self.ctx.device.mapMemory(image_staging_buffer_memory, 0, image_size, .{});
|
|
||||||
const image_data: [*]u8 = @ptrCast(@alignCast(data));
|
|
||||||
|
|
||||||
@memcpy(image_data, image[0..]);
|
|
||||||
self.ctx.device.unmapMemory(image_staging_buffer_memory);
|
|
||||||
|
|
||||||
// Create image to hold final texture
|
|
||||||
var tex_image_memory: vk.DeviceMemory = undefined;
|
|
||||||
const tex_image: vk.Image = try self.swapchain.createImage(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
.r8g8b8a8_srgb,
|
|
||||||
.optimal,
|
|
||||||
.{ .transfer_dst_bit = true, .sampled_bit = true },
|
|
||||||
.{ .device_local_bit = true },
|
|
||||||
&tex_image_memory,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transition image to be DST for copy operation
|
|
||||||
try Utilities.transitionImageLayout(
|
|
||||||
self.ctx.device,
|
|
||||||
self.ctx.graphics_queue.handle,
|
|
||||||
self.graphics_command_pool,
|
|
||||||
tex_image,
|
|
||||||
.undefined,
|
|
||||||
.transfer_dst_optimal,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Copy data to image
|
|
||||||
try Utilities.copyImageBuffer(
|
|
||||||
self.ctx.device,
|
|
||||||
self.ctx.graphics_queue.handle,
|
|
||||||
self.graphics_command_pool,
|
|
||||||
image_staging_buffer,
|
|
||||||
tex_image,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transition image to be shader readable for shader usage
|
|
||||||
try Utilities.transitionImageLayout(
|
|
||||||
self.ctx.device,
|
|
||||||
self.ctx.graphics_queue.handle,
|
|
||||||
self.graphics_command_pool,
|
|
||||||
tex_image,
|
|
||||||
.transfer_dst_optimal,
|
|
||||||
.shader_read_only_optimal,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add texture data to array for reference
|
|
||||||
try self.texture_images.append(tex_image);
|
|
||||||
try self.texture_image_memory.append(tex_image_memory);
|
|
||||||
|
|
||||||
// Return index of new texture image
|
|
||||||
return @intCast(self.texture_images.items.len - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createTexture(self: *Self, file_name: []const u8) !u32 {
|
|
||||||
// Create texture image and get its location in the array
|
|
||||||
const texture_image_loc = try self.createTextureImage(file_name);
|
|
||||||
|
|
||||||
// Create image view and add to list
|
|
||||||
const image_view = try self.swapchain.createImageView(
|
|
||||||
self.texture_images.items[texture_image_loc],
|
|
||||||
.r8g8b8a8_srgb,
|
|
||||||
.{ .color_bit = true },
|
|
||||||
);
|
|
||||||
|
|
||||||
try self.texture_image_views.append(image_view);
|
|
||||||
|
|
||||||
// Create texture descriptor
|
|
||||||
const descriptor_loc = try self.createTextureDescriptor(image_view);
|
|
||||||
|
|
||||||
// Return location of set with texture
|
|
||||||
return descriptor_loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createMeshModel(self: *Self, model_file: []const u8) !usize {
|
pub fn createMeshModel(self: *Self, model_file: []const u8) !usize {
|
||||||
const path = try StringUtils.concat("assets/models/", model_file, self.allocator);
|
// Pass tex smapler
|
||||||
defer self.allocator.free(path);
|
MeshModel.new(
|
||||||
|
|
||||||
// Import model scene
|
|
||||||
const scene = ai.aiImportFile(
|
|
||||||
path.ptr,
|
|
||||||
ai.aiProcess_Triangulate | ai.aiProcess_FlipUVs | ai.aiProcess_JoinIdenticalVertices,
|
|
||||||
);
|
|
||||||
defer ai.aiReleaseImport(scene);
|
|
||||||
|
|
||||||
// Get array of all materials with 1:1 ID placement
|
|
||||||
const texture_names = try MeshModel.loadMaterials(self.allocator, scene);
|
|
||||||
defer {
|
|
||||||
for (0..texture_names.items.len) |i| {
|
|
||||||
if (texture_names.items[i]) |texture_name| {
|
|
||||||
self.allocator.free(texture_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
texture_names.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversion from the material list IDs to our descriptor array IDs
|
|
||||||
var mat_to_tex = try std.ArrayList(u32).initCapacity(self.allocator, texture_names.items.len);
|
|
||||||
defer mat_to_tex.deinit();
|
|
||||||
|
|
||||||
// Loop over texture names and create textures for them
|
|
||||||
for (texture_names.items) |texture_name| {
|
|
||||||
if (texture_name != null) {
|
|
||||||
// Create texture and set value to index of new texture
|
|
||||||
mat_to_tex.appendAssumeCapacity(try self.createTexture(texture_name.?));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// If material had no texture, set to 0 to indicate no texture. Texture 0 will be reserver for a default texture
|
|
||||||
mat_to_tex.appendAssumeCapacity(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load in all our meshes
|
|
||||||
const model_meshes = try MeshModel.loadNode(
|
|
||||||
self.allocator,
|
self.allocator,
|
||||||
self.ctx.instance,
|
self.ctx,
|
||||||
self.ctx.physical_device,
|
|
||||||
self.ctx.device,
|
|
||||||
self.ctx.graphics_queue.handle,
|
|
||||||
self.graphics_command_pool,
|
self.graphics_command_pool,
|
||||||
scene.*.mRootNode,
|
self.texture_sampler,
|
||||||
scene,
|
model_file,
|
||||||
mat_to_tex.items,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a mesh model and add to our list
|
|
||||||
const mesh_model = MeshModel.new(self.allocator, model_meshes);
|
|
||||||
try self.model_list.append(mesh_model);
|
|
||||||
|
|
||||||
return self.model_list.items.len - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createTextureDescriptor(self: *Self, texture_image: vk.ImageView) !u32 {
|
|
||||||
var descriptor_set: vk.DescriptorSet = undefined;
|
|
||||||
|
|
||||||
// Descriptor set allocation info
|
|
||||||
const set_alloc_info: vk.DescriptorSetAllocateInfo = .{
|
|
||||||
.descriptor_pool = self.sampler_descriptor_pool,
|
|
||||||
.descriptor_set_count = 1,
|
|
||||||
.p_set_layouts = @ptrCast(&self.sampler_set_layout),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allocate descriptor sets
|
|
||||||
try self.ctx.device.allocateDescriptorSets(&set_alloc_info, @ptrCast(&descriptor_set));
|
|
||||||
|
|
||||||
const image_info: vk.DescriptorImageInfo = .{
|
|
||||||
.image_layout = .shader_read_only_optimal, // Image layout when in use
|
|
||||||
.image_view = texture_image, // Image to bind to set
|
|
||||||
.sampler = self.texture_sampler, // Sampler to use for set
|
|
||||||
};
|
|
||||||
|
|
||||||
// Descriptor write info
|
|
||||||
const descriptor_write: vk.WriteDescriptorSet = .{
|
|
||||||
.dst_set = descriptor_set,
|
|
||||||
.dst_binding = 0,
|
|
||||||
.dst_array_element = 0,
|
|
||||||
.descriptor_type = .combined_image_sampler,
|
|
||||||
.descriptor_count = 1,
|
|
||||||
.p_image_info = @ptrCast(&image_info),
|
|
||||||
.p_buffer_info = undefined,
|
|
||||||
.p_texel_buffer_view = undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update the new descriptor set
|
|
||||||
self.ctx.device.updateDescriptorSets(1, @ptrCast(&descriptor_write), 0, null);
|
|
||||||
|
|
||||||
try self.sampler_descriptor_sets.append(descriptor_set);
|
|
||||||
|
|
||||||
// Return descriptor set location
|
|
||||||
return @intCast(self.sampler_descriptor_sets.items.len - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loadTextureFile(self: *Self, file_name: []const u8, width: *u32, height: *u32, image_size: *vk.DeviceSize) ![]const u8 {
|
|
||||||
const path_concat = [2][]const u8{ "./assets/textures/", file_name };
|
|
||||||
const path = try std.mem.concatWithSentinel(self.allocator, u8, &path_concat, 0);
|
|
||||||
defer self.allocator.free(path);
|
|
||||||
|
|
||||||
const image = try img.Image.loadFromFile(path, 0);
|
|
||||||
try self.image_files.append(image);
|
|
||||||
|
|
||||||
width.* = image.width;
|
|
||||||
height.* = image.height;
|
|
||||||
|
|
||||||
// Calculate image size using given and known data
|
|
||||||
image_size.* = width.* * height.* * 4;
|
|
||||||
|
|
||||||
return image.data;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue