Revert "commit for revert"

This reverts commit 3c7ad199a0.
pull/2/head
ink-soul 2023-05-26 14:12:02 +08:00
parent 3c7ad199a0
commit a4590dc8fb
7 changed files with 1960 additions and 717 deletions

View File

@ -148,6 +148,6 @@ ENDIF(WIN32)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/")
add_subdirectory(base)
add_subdirectory(littleRender)
add_subdirectory(homework)
# add_subdirectory(examples)

View File

@ -1,37 +1,36 @@
# Function for building single homework
# Function for building single homework
function(buildHomework HOMEWORK_NAME)
SET(HOMEWORK_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
SET(HOMEWORK_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/${HOMEWORK_NAME})
message(STATUS "Generating project file for homework in ${HOMEWORK_FOLDER}")
# Main
file(GLOB SOURCE *.cpp ${BASE_HEADERS} ${HOMEWORK_FOLDER}/*.cpp)
SET(MAIN_CPP ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.cpp)
SET(MAIN_CPP ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.cpp "homework1/homework1.h")
if(EXISTS ${HOMEWORK_FOLDER}/main.cpp)
SET(MAIN_CPP ${HOMEWORK_FOLDER}/main.cpp)
SET(MAIN_CPP ${HOMEWORK_FOLDER}/main.cpp "homework1/homework1.h")
ENDIF()
if(EXISTS ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h)
SET(MAIN_HEADER ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h)
SET(MAIN_HEADER ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h "homework1/homework1.h")
ENDIF()
# imgui homework requires additional source files
IF(${HOMEWORK_NAME} STREQUAL "imgui")
file(GLOB ADD_SOURCE "../external/imgui/*.cpp")
SET(SOURCE ${SOURCE} ${ADD_SOURCE})
SET(SOURCE ${SOURCE} ${ADD_SOURCE} "homework1/homework1.h")
ENDIF()
# wayland requires additional source files
IF(USE_WAYLAND_WSI)
SET(SOURCE ${SOURCE} ${CMAKE_BINARY_DIR}/xdg-shell-client-protocol.h ${CMAKE_BINARY_DIR}/xdg-shell-protocol.c)
SET(SOURCE ${SOURCE} ${CMAKE_BINARY_DIR}/xdg-shell-client-protocol.h ${CMAKE_BINARY_DIR}/xdg-shell-protocol.c "homework1/homework1.h")
ENDIF()
# Add shaders
set(SHADER_DIR_GLSL "../data/buster_drone/shaders/glsl")
set(SHADER_DIR_GLSL "../data/homework/shaders/glsl/${HOMEWORK_NAME}")
file(GLOB SHADERS_GLSL "${SHADER_DIR_GLSL}/*.vert" "${SHADER_DIR_GLSL}/*.frag" "${SHADER_DIR_GLSL}/*.comp" "${SHADER_DIR_GLSL}/*.geom" "${SHADER_DIR_GLSL}/*.tesc" "${SHADER_DIR_GLSL}/*.tese" "${SHADER_DIR_GLSL}/*.mesh" "${SHADER_DIR_GLSL}/*.task" "${SHADER_DIR_GLSL}/*.rgen" "${SHADER_DIR_GLSL}/*.rchit" "${SHADER_DIR_GLSL}/*.rmiss" "${SHADER_DIR_GLSL}/*.rcall")
set(SHADER_DIR_HLSL "../data/buster_drone/shaders/glsl")
set(SHADER_DIR_HLSL "../data/homework/shaders/hlsl/${HOMEWORK_NAME}")
file(GLOB SHADERS_HLSL "${SHADER_DIR_HLSL}/*.vert" "${SHADER_DIR_HLSL}/*.frag" "${SHADER_DIR_HLSL}/*.comp" "${SHADER_DIR_HLSL}/*.geom" "${SHADER_DIR_HLSL}/*.tesc" "${SHADER_DIR_HLSL}/*.tese" "${SHADER_DIR_HLSL}/*.mesh" "${SHADER_DIR_HLSL}/*.task" "${SHADER_DIR_HLSL}/*.rgen" "${SHADER_DIR_HLSL}/*.rchit" "${SHADER_DIR_HLSL}/*.rmiss" "${SHADER_DIR_HLSL}/*.rcall")
source_group("Shaders\\GLSL" FILES ${SHADERS_GLSL})
source_group("Shaders\\HLSL" FILES ${SHADERS_HLSL})
# Add optional readme / tutorial
file(GLOB README_FILES "${HOMEWORK_FOLDER}/*.md")
if(WIN32)
add_executable(${HOMEWORK_NAME} WIN32 ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
add_executable(${HOMEWORK_NAME} WIN32 ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES} "homework1/homework1.h")
target_link_libraries(${HOMEWORK_NAME} base ${Vulkan_LIBRARY} ${WINLIBS})
else(WIN32)
add_executable(${HOMEWORK_NAME} ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
@ -79,7 +78,8 @@ function(buildHomeworks)
endfunction(buildHomeworks)
set(HOMEWORKS
render
homework0
homework1
)
buildHomeworks()

File diff suppressed because it is too large Load Diff

View File

@ -16,8 +16,496 @@
*
* If you are looking for a complete glTF implementation, check out https://github.com/SaschaWillems/Vulkan-glTF-PBR/
*/
#include "render.h"
#include "glTFModel.h"
#include "homework1.h"
/*
glTF loading functions
The following functions take a glTF input model loaded via tinyglTF and convert all required data into our own structure
*/
void VulkanglTFModel::loadImages(tinygltf::Model& input)
{
// Images can be stored inside the glTF (which is the case for the sample model), so instead of directly
// loading them from disk, we fetch them from the glTF loader and upload the buffers
images.resize(input.images.size());
for (size_t i = 0; i < input.images.size(); i++) {
tinygltf::Image& glTFImage = input.images[i];
// Get the image data from the glTF loader
unsigned char* buffer = nullptr;
VkDeviceSize bufferSize = 0;
bool deleteBuffer = false;
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
if (glTFImage.component == 3) {
bufferSize = glTFImage.width * glTFImage.height * 4;
buffer = new unsigned char[bufferSize];
unsigned char* rgba = buffer;
unsigned char* rgb = &glTFImage.image[0];
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i) {
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
rgba += 4;
rgb += 3;
}
deleteBuffer = true;
}
else {
buffer = &glTFImage.image[0];
bufferSize = glTFImage.image.size();
}
// Load texture from image buffer
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
if (deleteBuffer) {
delete[] buffer;
}
}
}
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
{
textures.resize(input.textures.size());
for (size_t i = 0; i < input.textures.size(); i++) {
textures[i].imageIndex = input.textures[i].source;
}
}
void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
{
animations.resize(input.animations.size());
for (size_t i = 0; i < input.animations.size(); ++i)
{
auto glTFAnimation = input.animations[i];
animations[i].name = glTFAnimation.name;
//Samplers
animations[i].samplers.resize(glTFAnimation.samplers.size());
for (size_t j = 0; j < glTFAnimation.samplers.size(); ++j)
{
auto glTFSampler = glTFAnimation.samplers[j];
auto& dstSampler = animations[i].samplers[j];
dstSampler.interpolation = glTFSampler.interpolation;
// Read sampler keyframe input time values
{
const auto& accessor = input.accessors[glTFSampler.input];
const auto& bufferView = input.bufferViews[accessor.bufferView];
const auto& buffer = input.buffers[bufferView.buffer];
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const float* buf = static_cast<const float*>(dataPtr);
for (size_t index = 0; index < accessor.count; ++index)
{
dstSampler.inputs.push_back(buf[index]);
}
// Adjust animation's start and end times
for (auto input : animations[i].samplers[j].inputs)
{
if (input < animations[i].start)
{
animations[i].start = input;
};
if (input > animations[i].end)
{
animations[i].end = input;
}
}
}
// Read sampler keyframe output translate/rotate/scale values
{
const auto& accessor = input.accessors[glTFSampler.output];
const auto& bufferView = input.bufferViews[accessor.bufferView];
const auto& buffer = input.buffers[bufferView.buffer];
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
switch (accessor.type)
{
case TINYGLTF_TYPE_VEC3:
{
const glm::vec3* buf = static_cast<const glm::vec3*>(dataPtr);
for (size_t index = 0; index < accessor.count; index++)
{
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
}
break;
}
case TINYGLTF_TYPE_VEC4:
{
const glm::vec4* buf = static_cast<const glm::vec4*>(dataPtr);
for (size_t index = 0; index < accessor.count; index++)
{
dstSampler.outputsVec4.push_back(buf[index]);
}
break;
}
default:
{
std::cout << "unknown type" << std::endl;
break;
}
}
}
}
animations[i].channels.resize(glTFAnimation.channels.size());
for (size_t j = 0; j < glTFAnimation.channels.size(); ++j)
{
auto glTFChannel = glTFAnimation.channels[j];
auto& dstChannel = animations[i].channels[j];
dstChannel.path = glTFChannel.target_path;
dstChannel.samplerIndex = glTFChannel.sampler;
dstChannel.node = nodeFromIndex(glTFChannel.target_node);
}
}
}
void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
{
materials.resize(input.materials.size());
for (size_t i = 0; i < input.materials.size(); i++) {
// We only read the most basic properties required for our sample
tinygltf::Material glTFMaterial = input.materials[i];
// Get the base color factor
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end()) {
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
}
// Get base color texture index
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end()) {
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
}
if (glTFMaterial.values.find("metallicRoughnessTexture") != glTFMaterial.values.end()) {
materials[i].matalicRoughTextureIndex = glTFMaterial.values["metallicRoughnessTexture"].TextureIndex();
}
if (glTFMaterial.additionalValues.find("normalTexture") != glTFMaterial.additionalValues.end())
{
materials[i].normalMapTextureIndex = glTFMaterial.additionalValues["normalTexture"].TextureIndex();
}
if (glTFMaterial.emissiveTexture.index != -1)
{
materials[i].emissiveTextureIndex = glTFMaterial.emissiveTexture.index;
}
if (glTFMaterial.emissiveFactor.size() == 3)
{
materials[i].materialData.values.emissiveFactor = glm::make_vec3(glTFMaterial.emissiveFactor.data());
}
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end())
{
materials[i].materialData.values.baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
}
}
}
void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, uint32_t nodeIndex,std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer)
{
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
node->matrix = glm::mat4(1.0f);
node->parent = parent;
node->index = nodeIndex;
// Get the local node matrix
// It's either made up from translation, rotation, scale or a 4x4 matrix
if (inputNode.translation.size() == 3) {
node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
}
if (inputNode.rotation.size() == 4) {
glm::quat q = glm::make_quat(inputNode.rotation.data());
node->matrix *= glm::mat4(q);
}
if (inputNode.scale.size() == 3) {
node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
}
if (inputNode.matrix.size() == 16) {
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
};
// Load node's children
if (inputNode.children.size() > 0) {
for (size_t i = 0; i < inputNode.children.size(); i++) {
loadNode(input.nodes[inputNode.children[i]], input , node, inputNode.children[i],indexBuffer, vertexBuffer);
}
}
// If the node contains mesh data, we load vertices and indices from the buffers
// In glTF this is done via accessors and buffer views
if (inputNode.mesh > -1) {
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
// Iterate through all primitives of this node's mesh
for (size_t i = 0; i < mesh.primitives.size(); i++) {
const tinygltf::Primitive& glTFPrimitive = mesh.primitives[i];
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
uint32_t indexCount = 0;
// Vertices
{
const float* positionBuffer = nullptr;
const float* normalsBuffer = nullptr;
const float* texCoordsBuffer = nullptr;
const float* tangentsBuffer = nullptr;
size_t vertexCount = 0;
// Get buffer data for vertex positions
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) {
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
positionBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
vertexCount = accessor.count;
}
// Get buffer data for vertex normals
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) {
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
normalsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
}
// Get buffer data for vertex texture coordinates
// glTF supports multiple sets, we only load the first one
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) {
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
texCoordsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
}
if (glTFPrimitive.attributes.find("TANGENT") != glTFPrimitive.attributes.end())
{
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TANGENT")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
tangentsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
}
// Append data to model's vertex buffer
for (size_t v = 0; v < vertexCount; v++) {
Vertex vert{};
vert.pos = glm::vec4(glm::make_vec3(&positionBuffer[v * 3]), 1.0f);
vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f)));
vert.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f);
vert.tangent = tangentsBuffer ? glm::normalize(glm::make_vec3(&tangentsBuffer[v * 4])) : glm::vec3(0.0f);
vert.color = glm::vec3(1.0f, 1.0f, nodeIndex);//Temp set index in color attribute
vertexBuffer.push_back(vert);
}
}
// Indices
{
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.indices];
const tinygltf::BufferView& bufferView = input.bufferViews[accessor.bufferView];
const tinygltf::Buffer& buffer = input.buffers[bufferView.buffer];
indexCount += static_cast<uint32_t>(accessor.count);
// glTF supports different component types of indices
switch (accessor.componentType) {
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
const uint32_t* buf = reinterpret_cast<const uint32_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
for (size_t index = 0; index < accessor.count; index++) {
indexBuffer.push_back(buf[index] + vertexStart);
}
break;
}
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
const uint16_t* buf = reinterpret_cast<const uint16_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
for (size_t index = 0; index < accessor.count; index++) {
indexBuffer.push_back(buf[index] + vertexStart);
}
break;
}
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
const uint8_t* buf = reinterpret_cast<const uint8_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
for (size_t index = 0; index < accessor.count; index++) {
indexBuffer.push_back(buf[index] + vertexStart);
}
break;
}
default:
std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl;
return;
}
}
Primitive primitive{};
primitive.firstIndex = firstIndex;
primitive.indexCount = indexCount;
primitive.materialIndex = glTFPrimitive.material;
node->mesh.primitives.push_back(primitive);
}
}
if (parent) {
parent->children.push_back(node);
}
else {
nodes.push_back(node);
}
}
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
{
Node* nodeFound = nullptr;
if (parent->index == index)
{
return parent;
}
for (auto& child : parent->children)
{
nodeFound = findNode(child, index);
if (nodeFound)
{
break;
}
}
return nodeFound;
}
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
{
Node* nodeFound = nullptr;
for (auto& node : nodes)
{
nodeFound = findNode(node, index);
if (nodeFound)
{
break;
}
}
return nodeFound;
}
void VulkanglTFModel::updateAnimation(float deltaTime, vks::Buffer& buffer)
{
constexpr uint32_t activeAnimation = 0;
Animation& animation = animations[activeAnimation];
animation.currentTime += deltaTime;
if (animation.currentTime > animation.end)
{
animation.currentTime -= animation.end;
}
for (auto& channel : animation.channels)
{
auto& sampler = animation.samplers[channel.samplerIndex];
for (size_t i = 0; i < sampler.inputs.size() - 1; ++i)
{
if (sampler.interpolation != "LINEAR")
{
std::cout << "This sample only supports linear interpolations\n";
continue;
}
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1]))
{
float ratio = (animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
if (channel.path == "translation")
{
channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
channel.node->bAnimateNode = true;
}
if (channel.path == "rotation")
{
glm::quat q1;
q1.x = sampler.outputsVec4[i].x;
q1.y = sampler.outputsVec4[i].y;
q1.z = sampler.outputsVec4[i].z;
q1.w = sampler.outputsVec4[i].w;
glm::quat q2;
q2.x = sampler.outputsVec4[i + 1].x;
q2.y = sampler.outputsVec4[i + 1].y;
q2.z = sampler.outputsVec4[i + 1].z;
q2.w = sampler.outputsVec4[i + 1].w;
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, ratio));
channel.node->bAnimateNode = true;
}
if (channel.path == "scale")
{
channel.node->scale = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
channel.node->bAnimateNode = true;
}
}
}
}
std::vector<glm::mat4> nodeMatrics(nodeCount);
for (auto& node : nodes)
{
updateNodeMatrix(node, nodeMatrics);
}
buffer.copyTo(nodeMatrics.data(), nodeCount * sizeof(glm::mat4));
}
void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics)
{
nodeMatrics[node->index] = getNodeMatrix(node);
for (auto& child : node->children)
{
updateNodeMatrix(child, nodeMatrics);
}
}
glm::mat4 VulkanglTFModel::getNodeMatrix(Node* node)
{
glm::mat4 nodeMatrix = node->getLocalMatrix();
Node* currentParent = node->parent;
while (currentParent)
{
nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;
currentParent = currentParent->parent;
}
return nodeMatrix;
}
/*
glTF rendering functions
*/
// Draw a single node including child nodes (if present)
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants)
{
if (node->mesh.primitives.size() > 0) {
// Pass the node's matrix via push constants
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
glm::mat4 nodeMatrix = node->matrix;
VulkanglTFModel::Node* currentParent = node->parent;
while (currentParent) {
nodeMatrix = currentParent->matrix * nodeMatrix;
currentParent = currentParent->parent;
}
for (VulkanglTFModel::Primitive& primitive : node->mesh.primitives) {
if (primitive.indexCount > 0) {
// Get the texture index for this primitive
if (textures.size() > 0)
{
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
auto normalMap = textures[materials[primitive.materialIndex].normalMapTextureIndex];
auto roughMetalMap = textures[materials[primitive.materialIndex].matalicRoughTextureIndex];
if (materials[primitive.materialIndex].emissiveTextureIndex >= 0)
{
auto emissiveMap = textures[materials[primitive.materialIndex].emissiveTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 4, 1, &images[emissiveMap.imageIndex].descriptorSet, 0, nullptr);
}
// Bind the descriptor for the current primitive's texture
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &images[normalMap.imageIndex].descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 3, 1, &images[roughMetalMap.imageIndex].descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 5, 1, &materials[primitive.materialIndex].materialData.descriptorSet, 0, nullptr);
}
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
}
}
}
for (auto& child : node->children) {
drawNode(commandBuffer, pipelineLayout, child, bPushConstants);
}
}
// Draw the glTF scene starting at the top-level-nodes
void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag = true)
{
// All vertices and indices are stored in single buffers, so we only need to bind once
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
// Render all nodes at top-level
for (auto& node : nodes) {
drawNode(commandBuffer, pipelineLayout, node, flag);
}
}
@ -184,7 +672,7 @@
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 6, 1, &skinDescriptorSet, 0, nullptr);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid);
glTFModel.draw(drawCmdBuffers[i], pipelineLayouts.pbrLayout,true);
glTFModel.draw(drawCmdBuffers[i], pipelineLayouts.pbrLayout);
vkCmdEndRenderPass(drawCmdBuffers[i]);
{

View File

@ -1,8 +1,9 @@
#pragma once
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define TINYGLTF_NO_STB_IMAGE_WRITE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
@ -10,10 +11,20 @@
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define TINYGLTF_NO_STB_IMAGE_WRITE
#ifdef VK_USE_PLATFORM_ANDROID_KHR
#define TINYGLTF_ANDROID_LOAD_FROM_ASSETS
#endif
#include "tiny_gltf.h"
#include "vulkanexamplebase.h"
#define ENABLE_VALIDATION false
// Contains everything required to render a glTF model in Vulkan
// This class is heavily simplified (compared to glTF's feature set) but retains the basic glTF structure
class VulkanglTFModel
@ -215,3 +226,179 @@ public:
void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants);
void draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag);
};
class VulkanExample : public VulkanExampleBase
{
public:
bool wireframe = false;
bool normalMapping = true;
bool ToneMapping = true;
bool pbrEnabled = true;
VulkanglTFModel glTFModel;
struct ShaderData {
vks::Buffer buffer;
struct Values {
glm::mat4 projection;
glm::mat4 model;
glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f);
glm::vec4 viewPos;
glm::vec4 bFlagSet = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
} values;
vks::Buffer skinSSBO;
} shaderData;
struct StagingBuffer {
VkBuffer buffer;
VkDeviceMemory memory;
} vertexStaging, indexStaging;
struct Pipelines {
VkPipeline solid;
VkPipeline wireframe = VK_NULL_HANDLE;
VkPipeline toneMapping = VK_NULL_HANDLE;
} pipelines;
struct PipelineLayouts
{
VkPipelineLayout pbrLayout;
VkPipelineLayout tonemappingLayout;
} pipelineLayouts;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSet skinDescriptorSet;
VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
struct FrameBufferAttachment
{
VkImage image;
VkDeviceMemory deviceMemory;
VkImageView imageView;
VkFormat format;
void destroy(VkDevice device)
{
vkDestroyImage(device, image, nullptr);
vkDestroyImageView(device, imageView,nullptr);
vkFreeMemory(device, deviceMemory, nullptr);
}
};
struct FrameBuffer
{
int32_t width, height;
VkFramebuffer frameBuffer;
VkRenderPass renderPass;
void setSize(int32_t w, int32_t h)
{
this->width = w;
this->height = h;
}
void destroy(VkDevice device)
{
vkDestroyFramebuffer(device, frameBuffer, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
}
};
struct PBRFrameBuffer {
FrameBufferAttachment color, depth;
FrameBuffer fbo;
bool bCreate = false;
} pbrFrameBuffer;
VkSampler colorSampler;
struct DescriptorSetLayouts {
VkDescriptorSetLayout matrices;
VkDescriptorSetLayout textures;
VkDescriptorSetLayout materialUniform;
VkDescriptorSetLayout ssbo;
VkDescriptorSetLayout jointMatrices;
} descriptorSetLayouts;
struct IBLTextures
{
vks::TextureCubeMap skyboxCube;
vks::TextureCubeMap irradianceCube;
vks::TextureCubeMap prefilteredCube;
vks::Texture2D lutBrdf;
} ibltextures;
struct OffScreen
{
VkImage image;
VkImageView view;
VkDeviceMemory memory;
VkFramebuffer framebuffer;
} offscreen;
struct IrradiancePushBlock
{
glm::mat4 mvp;
// Sampling deltas
float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
} irradiancePushBlock;
struct PrefilterPushBlock {
glm::mat4 mvp;
float roughness;
uint32_t numSamples = 32u;
} prefilterPushBlock;
VulkanglTFModel skyboxModel;
VulkanExample();
~VulkanExample()
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
vkDestroyPipeline(device, pipelines.solid, nullptr);
vkDestroyPipeline(device, pipelines.toneMapping, nullptr);
if (pipelines.wireframe != VK_NULL_HANDLE) {
vkDestroyPipeline(device, pipelines.wireframe, nullptr);
}
vkDestroyPipelineLayout(device, pipelineLayouts.pbrLayout, nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.tonemappingLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.matrices, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.materialUniform, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.ssbo, nullptr);
ibltextures.irradianceCube.destroy();
ibltextures.skyboxCube.destroy();
ibltextures.prefilteredCube.destroy();
ibltextures.lutBrdf.destroy();
pbrFrameBuffer.color.destroy(device);
pbrFrameBuffer.depth.destroy(device);
pbrFrameBuffer.fbo.destroy(device);
vkDestroySampler(device, colorSampler, nullptr);
shaderData.buffer.destroy();
shaderData.skinSSBO.destroy();
}
void loadglTFFile(std::string filename, VulkanglTFModel& model, bool bSkyboxFlag);
virtual void getEnabledFeatures();
void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment* attachment, uint32_t width, uint32_t height);
virtual void setupFrameBuffer();
void buildCommandBuffers();
void loadAssets();
void setupDescriptors();
void preparePipelines();
void CreateToneMappingPipeline();
void GenerateIrradianceCubemap();
void GeneratePrefilteredCubemap();
void GenerateBRDFLUT();
void prepareUniformBuffers();
void updateUniformBuffers();
void prepare();
virtual void render();
virtual void viewChanged();
virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
};

View File

@ -1,490 +0,0 @@
#include "glTFModel.h"
/*
glTF loading functions
The following functions take a glTF input model loaded via tinyglTF and convert all required data into our own structure
*/
void VulkanglTFModel::loadImages(tinygltf::Model& input)
{
// Images can be stored inside the glTF (which is the case for the sample model), so instead of directly
// loading them from disk, we fetch them from the glTF loader and upload the buffers
images.resize(input.images.size());
for (size_t i = 0; i < input.images.size(); i++) {
tinygltf::Image& glTFImage = input.images[i];
// Get the image data from the glTF loader
unsigned char* buffer = nullptr;
VkDeviceSize bufferSize = 0;
bool deleteBuffer = false;
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
if (glTFImage.component == 3) {
bufferSize = glTFImage.width * glTFImage.height * 4;
buffer = new unsigned char[bufferSize];
unsigned char* rgba = buffer;
unsigned char* rgb = &glTFImage.image[0];
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i) {
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
rgba += 4;
rgb += 3;
}
deleteBuffer = true;
}
else {
buffer = &glTFImage.image[0];
bufferSize = glTFImage.image.size();
}
// Load texture from image buffer
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
if (deleteBuffer) {
delete[] buffer;
}
}
}
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
{
textures.resize(input.textures.size());
for (size_t i = 0; i < input.textures.size(); i++) {
textures[i].imageIndex = input.textures[i].source;
}
}
void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
{
animations.resize(input.animations.size());
for (size_t i = 0; i < input.animations.size(); ++i)
{
auto glTFAnimation = input.animations[i];
animations[i].name = glTFAnimation.name;
//Samplers
animations[i].samplers.resize(glTFAnimation.samplers.size());
for (size_t j = 0; j < glTFAnimation.samplers.size(); ++j)
{
auto glTFSampler = glTFAnimation.samplers[j];
auto& dstSampler = animations[i].samplers[j];
dstSampler.interpolation = glTFSampler.interpolation;
// Read sampler keyframe input time values
{
const auto& accessor = input.accessors[glTFSampler.input];
const auto& bufferView = input.bufferViews[accessor.bufferView];
const auto& buffer = input.buffers[bufferView.buffer];
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const float* buf = static_cast<const float*>(dataPtr);
for (size_t index = 0; index < accessor.count; ++index)
{
dstSampler.inputs.push_back(buf[index]);
}
// Adjust animation's start and end times
for (auto input : animations[i].samplers[j].inputs)
{
if (input < animations[i].start)
{
animations[i].start = input;
};
if (input > animations[i].end)
{
animations[i].end = input;
}
}
}
// Read sampler keyframe output translate/rotate/scale values
{
const auto& accessor = input.accessors[glTFSampler.output];
const auto& bufferView = input.bufferViews[accessor.bufferView];
const auto& buffer = input.buffers[bufferView.buffer];
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
switch (accessor.type)
{
case TINYGLTF_TYPE_VEC3:
{
const glm::vec3* buf = static_cast<const glm::vec3*>(dataPtr);
for (size_t index = 0; index < accessor.count; index++)
{
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
}
break;
}
case TINYGLTF_TYPE_VEC4:
{
const glm::vec4* buf = static_cast<const glm::vec4*>(dataPtr);
for (size_t index = 0; index < accessor.count; index++)
{
dstSampler.outputsVec4.push_back(buf[index]);
}
break;
}
default:
{
std::cout << "unknown type" << std::endl;
break;
}
}
}
}
animations[i].channels.resize(glTFAnimation.channels.size());
for (size_t j = 0; j < glTFAnimation.channels.size(); ++j)
{
auto glTFChannel = glTFAnimation.channels[j];
auto& dstChannel = animations[i].channels[j];
dstChannel.path = glTFChannel.target_path;
dstChannel.samplerIndex = glTFChannel.sampler;
dstChannel.node = nodeFromIndex(glTFChannel.target_node);
}
}
}
void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
{
materials.resize(input.materials.size());
for (size_t i = 0; i < input.materials.size(); i++) {
// We only read the most basic properties required for our sample
tinygltf::Material glTFMaterial = input.materials[i];
// Get the base color factor
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end()) {
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
}
// Get base color texture index
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end()) {
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
}
if (glTFMaterial.values.find("metallicRoughnessTexture") != glTFMaterial.values.end()) {
materials[i].matalicRoughTextureIndex = glTFMaterial.values["metallicRoughnessTexture"].TextureIndex();
}
if (glTFMaterial.additionalValues.find("normalTexture") != glTFMaterial.additionalValues.end())
{
materials[i].normalMapTextureIndex = glTFMaterial.additionalValues["normalTexture"].TextureIndex();
}
if (glTFMaterial.emissiveTexture.index != -1)
{
materials[i].emissiveTextureIndex = glTFMaterial.emissiveTexture.index;
}
if (glTFMaterial.emissiveFactor.size() == 3)
{
materials[i].materialData.values.emissiveFactor = glm::make_vec3(glTFMaterial.emissiveFactor.data());
}
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end())
{
materials[i].materialData.values.baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
}
}
}
void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, uint32_t nodeIndex, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer)
{
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
node->matrix = glm::mat4(1.0f);
node->parent = parent;
node->index = nodeIndex;
// Get the local node matrix
// It's either made up from translation, rotation, scale or a 4x4 matrix
if (inputNode.translation.size() == 3) {
node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
}
if (inputNode.rotation.size() == 4) {
glm::quat q = glm::make_quat(inputNode.rotation.data());
node->matrix *= glm::mat4(q);
}
if (inputNode.scale.size() == 3) {
node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
}
if (inputNode.matrix.size() == 16) {
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
};
// Load node's children
if (inputNode.children.size() > 0) {
for (size_t i = 0; i < inputNode.children.size(); i++) {
loadNode(input.nodes[inputNode.children[i]], input, node, inputNode.children[i], indexBuffer, vertexBuffer);
}
}
// If the node contains mesh data, we load vertices and indices from the buffers
// In glTF this is done via accessors and buffer views
if (inputNode.mesh > -1) {
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
// Iterate through all primitives of this node's mesh
for (size_t i = 0; i < mesh.primitives.size(); i++) {
const tinygltf::Primitive& glTFPrimitive = mesh.primitives[i];
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
uint32_t indexCount = 0;
// Vertices
{
const float* positionBuffer = nullptr;
const float* normalsBuffer = nullptr;
const float* texCoordsBuffer = nullptr;
const float* tangentsBuffer = nullptr;
size_t vertexCount = 0;
// Get buffer data for vertex positions
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) {
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
positionBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
vertexCount = accessor.count;
}
// Get buffer data for vertex normals
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) {
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
normalsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
}
// Get buffer data for vertex texture coordinates
// glTF supports multiple sets, we only load the first one
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) {
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
texCoordsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
}
if (glTFPrimitive.attributes.find("TANGENT") != glTFPrimitive.attributes.end())
{
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TANGENT")->second];
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
tangentsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
}
// Append data to model's vertex buffer
for (size_t v = 0; v < vertexCount; v++) {
Vertex vert{};
vert.pos = glm::vec4(glm::make_vec3(&positionBuffer[v * 3]), 1.0f);
vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f)));
vert.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f);
vert.tangent = tangentsBuffer ? glm::normalize(glm::make_vec3(&tangentsBuffer[v * 4])) : glm::vec3(0.0f);
vert.color = glm::vec3(1.0f, 1.0f, nodeIndex);//Temp set index in color attribute
vertexBuffer.push_back(vert);
}
}
// Indices
{
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.indices];
const tinygltf::BufferView& bufferView = input.bufferViews[accessor.bufferView];
const tinygltf::Buffer& buffer = input.buffers[bufferView.buffer];
indexCount += static_cast<uint32_t>(accessor.count);
// glTF supports different component types of indices
switch (accessor.componentType) {
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
const uint32_t* buf = reinterpret_cast<const uint32_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
for (size_t index = 0; index < accessor.count; index++) {
indexBuffer.push_back(buf[index] + vertexStart);
}
break;
}
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
const uint16_t* buf = reinterpret_cast<const uint16_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
for (size_t index = 0; index < accessor.count; index++) {
indexBuffer.push_back(buf[index] + vertexStart);
}
break;
}
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
const uint8_t* buf = reinterpret_cast<const uint8_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
for (size_t index = 0; index < accessor.count; index++) {
indexBuffer.push_back(buf[index] + vertexStart);
}
break;
}
default:
std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl;
return;
}
}
Primitive primitive{};
primitive.firstIndex = firstIndex;
primitive.indexCount = indexCount;
primitive.materialIndex = glTFPrimitive.material;
node->mesh.primitives.push_back(primitive);
}
}
if (parent) {
parent->children.push_back(node);
}
else {
nodes.push_back(node);
}
}
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
{
Node* nodeFound = nullptr;
if (parent->index == index)
{
return parent;
}
for (auto& child : parent->children)
{
nodeFound = findNode(child, index);
if (nodeFound)
{
break;
}
}
return nodeFound;
}
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
{
Node* nodeFound = nullptr;
for (auto& node : nodes)
{
nodeFound = findNode(node, index);
if (nodeFound)
{
break;
}
}
return nodeFound;
}
void VulkanglTFModel::updateAnimation(float deltaTime, vks::Buffer& buffer)
{
constexpr uint32_t activeAnimation = 0;
Animation& animation = animations[activeAnimation];
animation.currentTime += deltaTime;
if (animation.currentTime > animation.end)
{
animation.currentTime -= animation.end;
}
for (auto& channel : animation.channels)
{
auto& sampler = animation.samplers[channel.samplerIndex];
for (size_t i = 0; i < sampler.inputs.size() - 1; ++i)
{
if (sampler.interpolation != "LINEAR")
{
std::cout << "This sample only supports linear interpolations\n";
continue;
}
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1]))
{
float ratio = (animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
if (channel.path == "translation")
{
channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
channel.node->bAnimateNode = true;
}
if (channel.path == "rotation")
{
glm::quat q1;
q1.x = sampler.outputsVec4[i].x;
q1.y = sampler.outputsVec4[i].y;
q1.z = sampler.outputsVec4[i].z;
q1.w = sampler.outputsVec4[i].w;
glm::quat q2;
q2.x = sampler.outputsVec4[i + 1].x;
q2.y = sampler.outputsVec4[i + 1].y;
q2.z = sampler.outputsVec4[i + 1].z;
q2.w = sampler.outputsVec4[i + 1].w;
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, ratio));
channel.node->bAnimateNode = true;
}
if (channel.path == "scale")
{
channel.node->scale = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
channel.node->bAnimateNode = true;
}
}
}
}
std::vector<glm::mat4> nodeMatrics(nodeCount);
for (auto& node : nodes)
{
updateNodeMatrix(node, nodeMatrics);
}
buffer.copyTo(nodeMatrics.data(), nodeCount * sizeof(glm::mat4));
}
void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics)
{
nodeMatrics[node->index] = getNodeMatrix(node);
for (auto& child : node->children)
{
updateNodeMatrix(child, nodeMatrics);
}
}
glm::mat4 VulkanglTFModel::getNodeMatrix(Node* node)
{
glm::mat4 nodeMatrix = node->getLocalMatrix();
Node* currentParent = node->parent;
while (currentParent)
{
nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;
currentParent = currentParent->parent;
}
return nodeMatrix;
}
/*
glTF rendering functions
*/
// Draw a single node including child nodes (if present)
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants)
{
if (node->mesh.primitives.size() > 0) {
// Pass the node's matrix via push constants
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
glm::mat4 nodeMatrix = node->matrix;
VulkanglTFModel::Node* currentParent = node->parent;
while (currentParent) {
nodeMatrix = currentParent->matrix * nodeMatrix;
currentParent = currentParent->parent;
}
for (VulkanglTFModel::Primitive& primitive : node->mesh.primitives) {
if (primitive.indexCount > 0) {
// Get the texture index for this primitive
if (textures.size() > 0)
{
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
auto normalMap = textures[materials[primitive.materialIndex].normalMapTextureIndex];
auto roughMetalMap = textures[materials[primitive.materialIndex].matalicRoughTextureIndex];
if (materials[primitive.materialIndex].emissiveTextureIndex >= 0)
{
auto emissiveMap = textures[materials[primitive.materialIndex].emissiveTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 4, 1, &images[emissiveMap.imageIndex].descriptorSet, 0, nullptr);
}
// Bind the descriptor for the current primitive's texture
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &images[normalMap.imageIndex].descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 3, 1, &images[roughMetalMap.imageIndex].descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 5, 1, &materials[primitive.materialIndex].materialData.descriptorSet, 0, nullptr);
}
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
}
}
}
for (auto& child : node->children) {
drawNode(commandBuffer, pipelineLayout, child, bPushConstants);
}
}
// Draw the glTF scene starting at the top-level-nodes
void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag = true)
{
// All vertices and indices are stored in single buffers, so we only need to bind once
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
// Render all nodes at top-level
for (auto& node : nodes) {
drawNode(commandBuffer, pipelineLayout, node, flag);
}
}

View File

@ -1,202 +0,0 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#ifdef VK_USE_PLATFORM_ANDROID_KHR
#define TINYGLTF_ANDROID_LOAD_FROM_ASSETS
#endif
#include "tiny_gltf.h"
#include "glTFModel.h"
#include "vulkanexamplebase.h"
#define ENABLE_VALIDATION false
class VulkanExample : public VulkanExampleBase
{
public:
bool wireframe = false;
bool normalMapping = true;
bool ToneMapping = true;
bool pbrEnabled = true;
VulkanglTFModel glTFModel;
struct ShaderData {
vks::Buffer buffer;
struct Values {
glm::mat4 projection;
glm::mat4 model;
glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f);
glm::vec4 viewPos;
glm::vec4 bFlagSet = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
} values;
vks::Buffer skinSSBO;
} shaderData;
struct StagingBuffer {
VkBuffer buffer;
VkDeviceMemory memory;
} vertexStaging, indexStaging;
struct Pipelines {
VkPipeline solid;
VkPipeline wireframe = VK_NULL_HANDLE;
VkPipeline toneMapping = VK_NULL_HANDLE;
} pipelines;
struct PipelineLayouts
{
VkPipelineLayout pbrLayout;
VkPipelineLayout tonemappingLayout;
} pipelineLayouts;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSet skinDescriptorSet;
VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
struct FrameBufferAttachment
{
VkImage image;
VkDeviceMemory deviceMemory;
VkImageView imageView;
VkFormat format;
void destroy(VkDevice device)
{
vkDestroyImage(device, image, nullptr);
vkDestroyImageView(device, imageView,nullptr);
vkFreeMemory(device, deviceMemory, nullptr);
}
};
struct FrameBuffer
{
int32_t width, height;
VkFramebuffer frameBuffer;
VkRenderPass renderPass;
void setSize(int32_t w, int32_t h)
{
this->width = w;
this->height = h;
}
void destroy(VkDevice device)
{
vkDestroyFramebuffer(device, frameBuffer, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
}
};
struct PBRFrameBuffer {
FrameBufferAttachment color, depth;
FrameBuffer fbo;
bool bCreate = false;
} pbrFrameBuffer;
VkSampler colorSampler;
struct DescriptorSetLayouts {
VkDescriptorSetLayout matrices;
VkDescriptorSetLayout textures;
VkDescriptorSetLayout materialUniform;
VkDescriptorSetLayout ssbo;
VkDescriptorSetLayout jointMatrices;
} descriptorSetLayouts;
struct IBLTextures
{
vks::TextureCubeMap skyboxCube;
vks::TextureCubeMap irradianceCube;
vks::TextureCubeMap prefilteredCube;
vks::Texture2D lutBrdf;
} ibltextures;
struct OffScreen
{
VkImage image;
VkImageView view;
VkDeviceMemory memory;
VkFramebuffer framebuffer;
} offscreen;
struct IrradiancePushBlock
{
glm::mat4 mvp;
// Sampling deltas
float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
} irradiancePushBlock;
struct PrefilterPushBlock {
glm::mat4 mvp;
float roughness;
uint32_t numSamples = 32u;
} prefilterPushBlock;
VulkanglTFModel skyboxModel;
VulkanExample();
~VulkanExample()
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
vkDestroyPipeline(device, pipelines.solid, nullptr);
vkDestroyPipeline(device, pipelines.toneMapping, nullptr);
if (pipelines.wireframe != VK_NULL_HANDLE) {
vkDestroyPipeline(device, pipelines.wireframe, nullptr);
}
vkDestroyPipelineLayout(device, pipelineLayouts.pbrLayout, nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.tonemappingLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.matrices, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.materialUniform, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.ssbo, nullptr);
ibltextures.irradianceCube.destroy();
ibltextures.skyboxCube.destroy();
ibltextures.prefilteredCube.destroy();
ibltextures.lutBrdf.destroy();
pbrFrameBuffer.color.destroy(device);
pbrFrameBuffer.depth.destroy(device);
pbrFrameBuffer.fbo.destroy(device);
vkDestroySampler(device, colorSampler, nullptr);
shaderData.buffer.destroy();
shaderData.skinSSBO.destroy();
}
void loadglTFFile(std::string filename, VulkanglTFModel& model, bool bSkyboxFlag);
virtual void getEnabledFeatures();
void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment* attachment, uint32_t width, uint32_t height);
virtual void setupFrameBuffer();
void buildCommandBuffers();
void loadAssets();
void setupDescriptors();
void preparePipelines();
void CreateToneMappingPipeline();
void GenerateIrradianceCubemap();
void GeneratePrefilteredCubemap();
void GenerateBRDFLUT();
void prepareUniformBuffers();
void updateUniformBuffers();
void prepare();
virtual void render();
virtual void viewChanged();
virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
};