CSC8503_Advanced_Game_Technologies / Plugins / VulkanRendering / VulkanMesh.cpp
VulkanMesh.cpp
Raw
#include "VulkanMesh.h"

using namespace NCL;
using namespace Rendering;

//These are both carefully arranged to match the MeshBuffer enum class!
vk::Format attributeFormats[] = {
	vk::Format::eR32G32B32Sfloat,	//Positions have this format
	vk::Format::eR32G32B32A32Sfloat,//Colours
	vk::Format::eR32G32Sfloat,		//TexCoords
	vk::Format::eR32G32B32Sfloat,	//Normals
	vk::Format::eR32G32B32Sfloat,	//Tangents
	vk::Format::eR32G32B32A32Sfloat,//Skel indices
	vk::Format::eR32G32B32A32Sfloat,//Skel Weights
};

size_t attributeSizes[] = {
	sizeof(Vector3),
	sizeof(Vector4),
	sizeof(Vector2),
	sizeof(Vector3),
	sizeof(Vector3),
	sizeof(Vector4),
	sizeof(Vector4),
};

VulkanMesh::VulkanMesh()	{

}

VulkanMesh::VulkanMesh(const std::string& filename) : MeshGeometry(filename) {

}

VulkanMesh::~VulkanMesh()	{
	if (indexBuffer) {
		sourceDevice.destroyBuffer(indexBuffer);
		sourceDevice.freeMemory(indexMemory);
	}
	if (vertexBuffer) {
		sourceDevice.destroyBuffer(vertexBuffer);
		sourceDevice.freeMemory(vertexMemory);
	}
}

void VulkanMesh::UploadToGPU(RendererBase* r)  {
	if (!ValidateMeshData()) {
		return;
	}
	VulkanRenderer* renderer = (VulkanRenderer*)r;

	sourceDevice = renderer->GetDevice();

	vector< int >			attributeTypes;
	vector< const void*>	attributePtrs;

	int strideSize		= 0; //how much data each vertex takes up - so how much we much jump over to get to the next vertex in a big buffer

	auto atrributeFunc = [&](VertexAttribute attribute, const void* data) {
		if (data) {
			attributeTypes.emplace_back(attribute);
			attributePtrs.emplace_back(data);
			strideSize += (int)attributeSizes[attribute];
		}
	};

	atrributeFunc(VertexAttribute::Positions,		GetPositionData().data());	
	atrributeFunc(VertexAttribute::Colours,			GetColourData().data());
	atrributeFunc(VertexAttribute::TextureCoords,	GetTextureCoordData().data());
	atrributeFunc(VertexAttribute::Normals,			GetNormalData().data());
	atrributeFunc(VertexAttribute::Tangents,		GetTangentData().data());
	atrributeFunc(VertexAttribute::JointWeights,	GetSkinWeightData().data());
	atrributeFunc(VertexAttribute::JointIndices,	GetSkinIndexData().data());

	attributeSpec.numAttributes = (int)attributeTypes.size();

	vertexBuffer = sourceDevice.createBuffer(
		vk::BufferCreateInfo({}, strideSize * GetVertexCount(), vk::BufferUsageFlagBits::eVertexBuffer)
	);

	vk::MemoryRequirements memReqs = sourceDevice.getBufferMemoryRequirements(vertexBuffer);

	vk::MemoryAllocateInfo memInfo = vk::MemoryAllocateInfo(memReqs.size);
	if (!renderer->MemoryTypeFromPhysicalDeviceProps(
		vk::MemoryPropertyFlagBits::eHostVisible, memReqs.memoryTypeBits, memInfo.memoryTypeIndex)) {
		return; //Oh no - Turns out we don't have the memory requirement for this!
	}

	vertexMemory = sourceDevice.allocateMemory(memInfo);
	sourceDevice.bindBufferMemory(vertexBuffer, vertexMemory, 0);
	//need to now copy vertex data to device memory
	char* dataPtr = (char*)sourceDevice.mapMemory(vertexMemory, 0, memInfo.allocationSize);
	for (unsigned int v = 0; v < GetVertexCount(); ++v) { //for every vertex
		for (int i = 0; i < attributeSpec.numAttributes; ++i) { //copy its next attribute to the GPU
			size_t copySize = attributeSizes[attributeTypes[i]];
			memcpy(dataPtr, (char*)attributePtrs[i] + (v * copySize), copySize);
			dataPtr += copySize;
		}
	}
	sourceDevice.unmapMemory(vertexMemory);

	int currentOffset = 0;
	for (int i = 0; i < attributeSpec.numAttributes; ++i) {
		int realIndex = attributeTypes[i]; //Shaders are locked to specific ids due to the binding locations, this converts to that locked ID

		vk::VertexInputAttributeDescription description;

		description.binding		= 0; //in in the same single bound buffer
		description.format		= attributeFormats[realIndex];
		description.location	= realIndex;
		description.offset		= currentOffset;

		attributeSpec.attributes.emplace_back(description);

		currentOffset += (int)attributeSizes[realIndex];
	}

	attributeSpec.binding = vk::VertexInputBindingDescription(0, strideSize, vk::VertexInputRate::eVertex);

	attributeSpec.vertexInfo = vk::PipelineVertexInputStateCreateInfo({},
		1, & attributeSpec.binding,	//How many buffers this mesh will be taking data from
		attributeSpec.numAttributes, &attributeSpec.attributes[0] //how many attributes + attribute info from above
	);

	//Make the index buffer if there are any!
	if (GetIndexCount() > 0) {
		indexBuffer = sourceDevice.createBuffer(
			vk::BufferCreateInfo({}, sizeof(int) * GetIndexCount(), vk::BufferUsageFlagBits::eIndexBuffer)
		);

		vk::MemoryRequirements memReqs = sourceDevice.getBufferMemoryRequirements(indexBuffer);

		vk::MemoryAllocateInfo memInfo = vk::MemoryAllocateInfo(memReqs.size);
		if (!renderer->MemoryTypeFromPhysicalDeviceProps(
			vk::MemoryPropertyFlagBits::eHostVisible, memReqs.memoryTypeBits, memInfo.memoryTypeIndex)) {
			return; //Oh no - Turns out we don't have the memory requirement for this!
		}

		indexMemory = sourceDevice.allocateMemory(memInfo);
		char* dataPtr = (char*)sourceDevice.mapMemory(indexMemory, 0, memInfo.allocationSize);

		memcpy(dataPtr, GetIndexData().data(), sizeof(int) * GetIndexCount());

		sourceDevice.unmapMemory(indexMemory);

		sourceDevice.bindBufferMemory(indexBuffer, indexMemory, 0);
	}

	if (!debugName.empty()) {
		renderer->SetDebugName(vk::ObjectType::eBuffer, (uint64_t)(VkBuffer)vertexBuffer, debugName + " attributes");
		if (GetIndexCount() > 0) {
			renderer->SetDebugName(vk::ObjectType::eBuffer, (uint64_t)(VkBuffer)indexBuffer, debugName + " indices");
		}
	}
}