mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-25 06:04:57 +00:00
multi object support
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
local vertices = {
|
||||
local cube_vertices = {
|
||||
{ position = {-1.0, -1.0, -1.0}, color = {1.0, 0.0, 0.0} },
|
||||
{ position = {1.0, -1.0, -1.0}, color = {0.0, 1.0, 0.0} },
|
||||
{ position = {1.0, 1.0, -1.0}, color = {0.0, 0.0, 1.0} },
|
||||
@@ -9,7 +9,7 @@ local vertices = {
|
||||
{ position = {-1.0, 1.0, 1.0}, color = {0.2, 0.2, 0.2} },
|
||||
}
|
||||
|
||||
local indices = {
|
||||
local cube_indices = {
|
||||
1, 2, 3, 3, 4, 1, -- back
|
||||
5, 6, 7, 7, 8, 5, -- front
|
||||
1, 5, 8, 8, 4, 1, -- left
|
||||
@@ -18,10 +18,38 @@ local indices = {
|
||||
1, 2, 6, 6, 5, 1, -- bottom
|
||||
}
|
||||
|
||||
local pyramid_vertices = {
|
||||
{ position = {0.0, 1.0, 0.0}, color = {1.0, 0.5, 0.0} },
|
||||
{ position = {-1.0, -1.0, -1.0}, color = {0.0, 1.0, 1.0} },
|
||||
{ position = {1.0, -1.0, -1.0}, color = {1.0, 0.0, 1.0} },
|
||||
{ position = {1.0, -1.0, 1.0}, color = {1.0, 1.0, 0.0} },
|
||||
{ position = {-1.0, -1.0, 1.0}, color = {0.0, 0.0, 1.0} },
|
||||
}
|
||||
|
||||
local pyramid_indices = {
|
||||
1, 2, 3,
|
||||
1, 3, 4,
|
||||
1, 4, 5,
|
||||
1, 5, 2,
|
||||
2, 3, 4,
|
||||
4, 5, 2,
|
||||
}
|
||||
|
||||
local rotation_speeds = {x = 0.5, y = 0.7}
|
||||
local shader_paths = {
|
||||
vertex = "shaders/cube.vert.spv",
|
||||
fragment = "shaders/cube.frag.spv",
|
||||
|
||||
local shader_variants = {
|
||||
default = {
|
||||
vertex = "shaders/cube.vert.spv",
|
||||
fragment = "shaders/cube.frag.spv",
|
||||
},
|
||||
cube = {
|
||||
vertex = "shaders/cube.vert.spv",
|
||||
fragment = "shaders/cube.frag.spv",
|
||||
},
|
||||
pyramid = {
|
||||
vertex = "shaders/cube.vert.spv",
|
||||
fragment = "shaders/cube.frag.spv",
|
||||
},
|
||||
}
|
||||
|
||||
local function rotation_y(radians)
|
||||
@@ -60,24 +88,60 @@ local function multiply_matrices(a, b)
|
||||
return result
|
||||
end
|
||||
|
||||
local function translation(x, y, z)
|
||||
return {
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
x, y, z, 1.0,
|
||||
}
|
||||
end
|
||||
|
||||
local function build_model(time)
|
||||
local y = rotation_y(time * rotation_speeds.y)
|
||||
local x = rotation_x(time * rotation_speeds.x)
|
||||
return multiply_matrices(y, x)
|
||||
end
|
||||
|
||||
function get_cube_vertices()
|
||||
return vertices
|
||||
local function create_cube(position, speed_scale, shader_key)
|
||||
local function compute_model_matrix(time)
|
||||
local base = build_model(time * speed_scale)
|
||||
local offset = translation(position[1], position[2], position[3])
|
||||
return multiply_matrices(offset, base)
|
||||
end
|
||||
|
||||
return {
|
||||
vertices = cube_vertices,
|
||||
indices = cube_indices,
|
||||
compute_model_matrix = compute_model_matrix,
|
||||
shader_key = shader_key or "cube",
|
||||
}
|
||||
end
|
||||
|
||||
function get_cube_indices()
|
||||
return indices
|
||||
local function create_pyramid(position, shader_key)
|
||||
local function compute_model_matrix(time)
|
||||
local base = build_model(time * 0.6)
|
||||
local offset = translation(position[1], position[2], position[3])
|
||||
return multiply_matrices(offset, base)
|
||||
end
|
||||
|
||||
return {
|
||||
vertices = pyramid_vertices,
|
||||
indices = pyramid_indices,
|
||||
compute_model_matrix = compute_model_matrix,
|
||||
shader_key = shader_key or "pyramid",
|
||||
}
|
||||
end
|
||||
|
||||
function compute_model_matrix(time)
|
||||
return build_model(time)
|
||||
function get_scene_objects()
|
||||
return {
|
||||
create_cube({0.0, 0.0, 0.0}, 1.0, "cube"),
|
||||
create_cube({3.0, 0.0, 0.0}, 0.8, "cube"),
|
||||
create_cube({-3.0, 0.0, 0.0}, 1.2, "cube"),
|
||||
create_pyramid({0.0, -0.5, -4.0}, "pyramid"),
|
||||
}
|
||||
end
|
||||
|
||||
function get_shader_paths()
|
||||
return shader_paths
|
||||
return shader_variants
|
||||
end
|
||||
|
||||
414
src/main.cpp
414
src/main.cpp
@@ -15,6 +15,7 @@
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -154,16 +155,24 @@ public:
|
||||
std::string fragment;
|
||||
};
|
||||
|
||||
std::vector<Vertex> LoadVertices();
|
||||
std::vector<uint16_t> LoadIndices();
|
||||
std::array<float, 16> ComputeModelMatrix(float time);
|
||||
ShaderPaths LoadShaderPaths();
|
||||
struct SceneObject {
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<uint16_t> indices;
|
||||
int computeModelMatrixRef = LUA_REFNIL;
|
||||
std::string shaderKey = "default";
|
||||
};
|
||||
|
||||
std::vector<SceneObject> LoadSceneObjects();
|
||||
std::array<float, 16> ComputeModelMatrix(int functionRef, float time);
|
||||
std::unordered_map<std::string, ShaderPaths> LoadShaderPathsMap();
|
||||
|
||||
private:
|
||||
static std::array<float, 3> ReadVector3(lua_State* L, int index);
|
||||
static std::array<float, 16> ReadMatrix(lua_State* L, int index);
|
||||
static std::vector<Vertex> ReadVertexArray(lua_State* L, int index);
|
||||
static std::vector<uint16_t> ReadIndexArray(lua_State* L, int index);
|
||||
static std::string LuaErrorMessage(lua_State* L);
|
||||
static ShaderPaths DefaultShaderPaths();
|
||||
static ShaderPaths ReadShaderPathsTable(lua_State* L, int index);
|
||||
|
||||
lua_State* L_ = nullptr;
|
||||
};
|
||||
@@ -188,96 +197,84 @@ CubeScript::~CubeScript() {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vertex> CubeScript::LoadVertices() {
|
||||
lua_getglobal(L_, "get_cube_vertices");
|
||||
|
||||
std::vector<CubeScript::SceneObject> CubeScript::LoadSceneObjects() {
|
||||
lua_getglobal(L_, "get_scene_objects");
|
||||
if (!lua_isfunction(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Lua function 'get_cube_vertices' is missing");
|
||||
throw std::runtime_error("Lua function 'get_scene_objects' is missing");
|
||||
}
|
||||
if (lua_pcall(L_, 0, 1, 0) != LUA_OK) {
|
||||
std::string message = LuaErrorMessage(L_);
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Lua get_cube_vertices failed: " + message);
|
||||
throw std::runtime_error("Lua get_scene_objects failed: " + message);
|
||||
}
|
||||
if (!lua_istable(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("'get_cube_vertices' did not return a table");
|
||||
throw std::runtime_error("'get_scene_objects' did not return a table");
|
||||
}
|
||||
|
||||
size_t count = lua_rawlen(L_, -1);
|
||||
std::vector<Vertex> vertices;
|
||||
vertices.reserve(count);
|
||||
std::vector<SceneObject> objects;
|
||||
objects.reserve(count);
|
||||
|
||||
for (size_t i = 1; i <= count; ++i) {
|
||||
lua_rawgeti(L_, -1, static_cast<int>(i));
|
||||
if (!lua_istable(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Vertex entry at index " + std::to_string(i) + " is not a table");
|
||||
throw std::runtime_error("Scene object at index " + std::to_string(i) + " is not a table");
|
||||
}
|
||||
|
||||
int vertexIndex = lua_gettop(L_);
|
||||
Vertex vertex{};
|
||||
|
||||
lua_getfield(L_, vertexIndex, "position");
|
||||
vertex.position = ReadVector3(L_, -1);
|
||||
SceneObject object;
|
||||
lua_getfield(L_, -1, "vertices");
|
||||
object.vertices = ReadVertexArray(L_, -1);
|
||||
lua_pop(L_, 1);
|
||||
|
||||
lua_getfield(L_, vertexIndex, "color");
|
||||
vertex.color = ReadVector3(L_, -1);
|
||||
lua_pop(L_, 1);
|
||||
|
||||
vertices.push_back(vertex);
|
||||
lua_pop(L_, 1);
|
||||
}
|
||||
|
||||
lua_pop(L_, 1);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> CubeScript::LoadIndices() {
|
||||
lua_getglobal(L_, "get_cube_indices");
|
||||
if (!lua_isfunction(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Lua function 'get_cube_indices' is missing");
|
||||
}
|
||||
if (lua_pcall(L_, 0, 1, 0) != LUA_OK) {
|
||||
std::string message = LuaErrorMessage(L_);
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Lua get_cube_indices failed: " + message);
|
||||
}
|
||||
if (!lua_istable(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("'get_cube_indices' did not return a table");
|
||||
}
|
||||
|
||||
size_t count = lua_rawlen(L_, -1);
|
||||
std::vector<uint16_t> indices;
|
||||
indices.reserve(count);
|
||||
|
||||
for (size_t i = 1; i <= count; ++i) {
|
||||
lua_rawgeti(L_, -1, static_cast<int>(i));
|
||||
if (!lua_isinteger(L_, -1)) {
|
||||
if (object.vertices.empty()) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Index entry at position " + std::to_string(i) + " is not an integer");
|
||||
throw std::runtime_error("Scene object " + std::to_string(i) + " must supply at least one vertex");
|
||||
}
|
||||
lua_Integer value = lua_tointeger(L_, -1);
|
||||
|
||||
lua_getfield(L_, -1, "indices");
|
||||
object.indices = ReadIndexArray(L_, -1);
|
||||
lua_pop(L_, 1);
|
||||
if (value < 1) {
|
||||
throw std::runtime_error("Index values must be 1 or greater");
|
||||
if (object.indices.empty()) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Scene object " + std::to_string(i) + " must supply indices");
|
||||
}
|
||||
indices.push_back(static_cast<uint16_t>(value - 1));
|
||||
|
||||
lua_getfield(L_, -1, "compute_model_matrix");
|
||||
if (lua_isfunction(L_, -1)) {
|
||||
object.computeModelMatrixRef = luaL_ref(L_, LUA_REGISTRYINDEX);
|
||||
} else {
|
||||
lua_pop(L_, 1);
|
||||
object.computeModelMatrixRef = LUA_REFNIL;
|
||||
}
|
||||
|
||||
lua_getfield(L_, -1, "shader_key");
|
||||
if (lua_isstring(L_, -1)) {
|
||||
object.shaderKey = lua_tostring(L_, -1);
|
||||
}
|
||||
lua_pop(L_, 1);
|
||||
|
||||
objects.push_back(std::move(object));
|
||||
lua_pop(L_, 1);
|
||||
}
|
||||
|
||||
lua_pop(L_, 1);
|
||||
return indices;
|
||||
return objects;
|
||||
}
|
||||
|
||||
std::array<float, 16> CubeScript::ComputeModelMatrix(float time) {
|
||||
lua_getglobal(L_, "compute_model_matrix");
|
||||
if (!lua_isfunction(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
throw std::runtime_error("Lua function 'compute_model_matrix' is missing");
|
||||
std::array<float, 16> CubeScript::ComputeModelMatrix(int functionRef, float time) {
|
||||
if (functionRef == LUA_REFNIL) {
|
||||
lua_getglobal(L_, "compute_model_matrix");
|
||||
if (!lua_isfunction(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
return IdentityMatrix();
|
||||
}
|
||||
} else {
|
||||
lua_rawgeti(L_, LUA_REGISTRYINDEX, functionRef);
|
||||
}
|
||||
|
||||
lua_pushnumber(L_, time);
|
||||
if (lua_pcall(L_, 1, 1, 0) != LUA_OK) {
|
||||
std::string message = LuaErrorMessage(L_);
|
||||
@@ -294,11 +291,73 @@ std::array<float, 16> CubeScript::ComputeModelMatrix(float time) {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
CubeScript::ShaderPaths CubeScript::LoadShaderPaths() {
|
||||
std::vector<Vertex> CubeScript::ReadVertexArray(lua_State* L, int index) {
|
||||
int absIndex = lua_absindex(L, index);
|
||||
if (!lua_istable(L, absIndex)) {
|
||||
throw std::runtime_error("Expected table for vertex data");
|
||||
}
|
||||
|
||||
size_t count = lua_rawlen(L, absIndex);
|
||||
std::vector<Vertex> vertices;
|
||||
vertices.reserve(count);
|
||||
|
||||
for (size_t i = 1; i <= count; ++i) {
|
||||
lua_rawgeti(L, absIndex, static_cast<int>(i));
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
throw std::runtime_error("Vertex entry at index " + std::to_string(i) + " is not a table");
|
||||
}
|
||||
|
||||
int vertexIndex = lua_gettop(L);
|
||||
Vertex vertex{};
|
||||
|
||||
lua_getfield(L, vertexIndex, "position");
|
||||
vertex.position = ReadVector3(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, vertexIndex, "color");
|
||||
vertex.color = ReadVector3(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
vertices.push_back(vertex);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> CubeScript::ReadIndexArray(lua_State* L, int index) {
|
||||
int absIndex = lua_absindex(L, index);
|
||||
if (!lua_istable(L, absIndex)) {
|
||||
throw std::runtime_error("Expected table for index data");
|
||||
}
|
||||
|
||||
size_t count = lua_rawlen(L, absIndex);
|
||||
std::vector<uint16_t> indices;
|
||||
indices.reserve(count);
|
||||
|
||||
for (size_t i = 1; i <= count; ++i) {
|
||||
lua_rawgeti(L, absIndex, static_cast<int>(i));
|
||||
if (!lua_isinteger(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
throw std::runtime_error("Index entry at position " + std::to_string(i) + " is not an integer");
|
||||
}
|
||||
lua_Integer value = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (value < 1) {
|
||||
throw std::runtime_error("Index values must be 1 or greater");
|
||||
}
|
||||
indices.push_back(static_cast<uint16_t>(value - 1));
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, CubeScript::ShaderPaths> CubeScript::LoadShaderPathsMap() {
|
||||
lua_getglobal(L_, "get_shader_paths");
|
||||
if (!lua_isfunction(L_, -1)) {
|
||||
lua_pop(L_, 1);
|
||||
return DefaultShaderPaths();
|
||||
throw std::runtime_error("Lua function 'get_shader_paths' is missing");
|
||||
}
|
||||
if (lua_pcall(L_, 0, 1, 0) != LUA_OK) {
|
||||
std::string message = LuaErrorMessage(L_);
|
||||
@@ -310,24 +369,43 @@ CubeScript::ShaderPaths CubeScript::LoadShaderPaths() {
|
||||
throw std::runtime_error("'get_shader_paths' did not return a table");
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, ShaderPaths> shaderMap;
|
||||
lua_pushnil(L_);
|
||||
while (lua_next(L_, -2) != 0) {
|
||||
if (lua_isstring(L_, -2) && lua_istable(L_, -1)) {
|
||||
std::string key = lua_tostring(L_, -2);
|
||||
shaderMap.emplace(key, ReadShaderPathsTable(L_, -1));
|
||||
}
|
||||
lua_pop(L_, 1);
|
||||
}
|
||||
|
||||
lua_pop(L_, 1);
|
||||
if (shaderMap.empty()) {
|
||||
throw std::runtime_error("'get_shader_paths' did not return any shader variants");
|
||||
}
|
||||
return shaderMap;
|
||||
}
|
||||
|
||||
CubeScript::ShaderPaths CubeScript::ReadShaderPathsTable(lua_State* L, int index) {
|
||||
ShaderPaths paths;
|
||||
lua_getfield(L_, -1, "vertex");
|
||||
if (!lua_isstring(L_, -1)) {
|
||||
lua_pop(L_, 2);
|
||||
int absIndex = lua_absindex(L, index);
|
||||
|
||||
lua_getfield(L, absIndex, "vertex");
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
throw std::runtime_error("Shader path 'vertex' must be a string");
|
||||
}
|
||||
paths.vertex = lua_tostring(L_, -1);
|
||||
lua_pop(L_, 1);
|
||||
paths.vertex = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L_, -1, "fragment");
|
||||
if (!lua_isstring(L_, -1)) {
|
||||
lua_pop(L_, 2);
|
||||
lua_getfield(L, absIndex, "fragment");
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
throw std::runtime_error("Shader path 'fragment' must be a string");
|
||||
}
|
||||
paths.fragment = lua_tostring(L_, -1);
|
||||
lua_pop(L_, 1);
|
||||
paths.fragment = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pop(L_, 1);
|
||||
return paths;
|
||||
}
|
||||
|
||||
@@ -374,10 +452,6 @@ std::string CubeScript::LuaErrorMessage(lua_State* L) {
|
||||
return message ? message : "unknown lua error";
|
||||
}
|
||||
|
||||
CubeScript::ShaderPaths CubeScript::DefaultShaderPaths() {
|
||||
return {"shaders/cube.vert.spv", "shaders/cube.frag.spv"};
|
||||
}
|
||||
|
||||
class VulkanCubeApp {
|
||||
public:
|
||||
explicit VulkanCubeApp(const std::filesystem::path& scriptPath);
|
||||
@@ -389,6 +463,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
struct RenderObject {
|
||||
uint32_t indexOffset = 0;
|
||||
uint32_t indexCount = 0;
|
||||
int32_t vertexOffset = 0;
|
||||
int computeModelMatrixRef = LUA_REFNIL;
|
||||
std::string shaderKey = "default";
|
||||
};
|
||||
|
||||
void InitSDL() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
throw std::runtime_error(std::string("SDL_Init failed: ") + SDL_GetError());
|
||||
@@ -409,10 +491,10 @@ private:
|
||||
CreateSwapChain();
|
||||
CreateImageViews();
|
||||
CreateRenderPass();
|
||||
LoadCubeData();
|
||||
CreateGraphicsPipeline();
|
||||
CreateFramebuffers();
|
||||
CreateCommandPool();
|
||||
LoadCubeData();
|
||||
CreateVertexBuffer();
|
||||
CreateIndexBuffer();
|
||||
CreateCommandBuffers();
|
||||
@@ -447,8 +529,14 @@ private:
|
||||
}
|
||||
vkFreeCommandBuffers(device_, commandPool_,
|
||||
static_cast<uint32_t>(commandBuffers_.size()), commandBuffers_.data());
|
||||
vkDestroyPipeline(device_, graphicsPipeline_, nullptr);
|
||||
vkDestroyPipelineLayout(device_, pipelineLayout_, nullptr);
|
||||
for (auto& entry : graphicsPipelines_) {
|
||||
vkDestroyPipeline(device_, entry.second, nullptr);
|
||||
}
|
||||
graphicsPipelines_.clear();
|
||||
if (pipelineLayout_ != VK_NULL_HANDLE) {
|
||||
vkDestroyPipelineLayout(device_, pipelineLayout_, nullptr);
|
||||
pipelineLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
vkDestroyRenderPass(device_, renderPass_, nullptr);
|
||||
for (auto imageView : swapChainImageViews_) {
|
||||
vkDestroyImageView(device_, imageView, nullptr);
|
||||
@@ -716,27 +804,16 @@ private:
|
||||
}
|
||||
|
||||
void CreateGraphicsPipeline() {
|
||||
auto shaderPaths = cubeScript_.LoadShaderPaths();
|
||||
auto vertShaderCode = ReadFile(shaderPaths.vertex);
|
||||
auto fragShaderCode = ReadFile(shaderPaths.fragment);
|
||||
if (shaderPathMap_.empty()) {
|
||||
throw std::runtime_error("No shader paths were loaded before pipeline creation");
|
||||
}
|
||||
|
||||
VkShaderModule vertShaderModule = CreateShaderModule(vertShaderCode);
|
||||
VkShaderModule fragShaderModule = CreateShaderModule(fragShaderCode);
|
||||
|
||||
VkPipelineShaderStageCreateInfo vertStageInfo{};
|
||||
vertStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertStageInfo.module = vertShaderModule;
|
||||
vertStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo fragStageInfo{};
|
||||
fragStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragStageInfo.module = fragShaderModule;
|
||||
fragStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[] = {vertStageInfo, fragStageInfo};
|
||||
for (auto& entry : graphicsPipelines_) {
|
||||
vkDestroyPipeline(device_, entry.second, nullptr);
|
||||
}
|
||||
graphicsPipelines_.clear();
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
|
||||
VkVertexInputBindingDescription bindingDescription{};
|
||||
bindingDescription.binding = 0;
|
||||
bindingDescription.stride = sizeof(Vertex);
|
||||
@@ -753,12 +830,10 @@ private:
|
||||
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
attributeDescriptions[1].offset = offsetof(Vertex, color);
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
|
||||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertexInputInfo.vertexBindingDescriptionCount = 1;
|
||||
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
|
||||
vertexInputInfo.vertexAttributeDescriptionCount =
|
||||
static_cast<uint32_t>(attributeDescriptions.size());
|
||||
vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
|
||||
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
|
||||
@@ -821,14 +896,17 @@ private:
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushRange;
|
||||
|
||||
if (pipelineLayout_ != VK_NULL_HANDLE) {
|
||||
vkDestroyPipelineLayout(device_, pipelineLayout_, nullptr);
|
||||
pipelineLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (vkCreatePipelineLayout(device_, &pipelineLayoutInfo, nullptr, &pipelineLayout_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create pipeline layout");
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineInfo.stageCount = 2;
|
||||
pipelineInfo.pStages = shaderStages;
|
||||
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
||||
pipelineInfo.pInputAssemblyState = &inputAssembly;
|
||||
pipelineInfo.pViewportState = &viewportState;
|
||||
@@ -839,13 +917,43 @@ private:
|
||||
pipelineInfo.renderPass = renderPass_;
|
||||
pipelineInfo.subpass = 0;
|
||||
|
||||
if (vkCreateGraphicsPipelines(device_, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr,
|
||||
&graphicsPipeline_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create graphics pipeline");
|
||||
}
|
||||
for (const auto& [key, paths] : shaderPathMap_) {
|
||||
auto vertShaderCode = ReadFile(paths.vertex);
|
||||
auto fragShaderCode = ReadFile(paths.fragment);
|
||||
|
||||
vkDestroyShaderModule(device_, fragShaderModule, nullptr);
|
||||
vkDestroyShaderModule(device_, vertShaderModule, nullptr);
|
||||
VkShaderModule vertShaderModule = CreateShaderModule(vertShaderCode);
|
||||
VkShaderModule fragShaderModule = CreateShaderModule(fragShaderCode);
|
||||
|
||||
VkPipelineShaderStageCreateInfo vertStageInfo{};
|
||||
vertStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertStageInfo.module = vertShaderModule;
|
||||
vertStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo fragStageInfo{};
|
||||
fragStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragStageInfo.module = fragShaderModule;
|
||||
fragStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[] = {vertStageInfo, fragStageInfo};
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = pipelineInfo;
|
||||
pipelineCreateInfo.stageCount = 2;
|
||||
pipelineCreateInfo.pStages = shaderStages;
|
||||
|
||||
VkPipeline pipeline;
|
||||
if (vkCreateGraphicsPipelines(device_, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr,
|
||||
&pipeline) != VK_SUCCESS) {
|
||||
vkDestroyShaderModule(device_, fragShaderModule, nullptr);
|
||||
vkDestroyShaderModule(device_, vertShaderModule, nullptr);
|
||||
throw std::runtime_error("Failed to create graphics pipeline");
|
||||
}
|
||||
graphicsPipelines_.emplace(key, pipeline);
|
||||
|
||||
vkDestroyShaderModule(device_, fragShaderModule, nullptr);
|
||||
vkDestroyShaderModule(device_, vertShaderModule, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateFramebuffers() {
|
||||
@@ -883,10 +991,46 @@ private:
|
||||
}
|
||||
|
||||
void LoadCubeData() {
|
||||
vertices_ = cubeScript_.LoadVertices();
|
||||
indices_ = cubeScript_.LoadIndices();
|
||||
shaderPathMap_ = cubeScript_.LoadShaderPathsMap();
|
||||
if (shaderPathMap_.empty()) {
|
||||
throw std::runtime_error("Lua script did not provide shader paths");
|
||||
}
|
||||
defaultShaderKey_ = shaderPathMap_.count("default") ? "default" : shaderPathMap_.begin()->first;
|
||||
|
||||
auto sceneObjects = cubeScript_.LoadSceneObjects();
|
||||
if (sceneObjects.empty()) {
|
||||
throw std::runtime_error("Lua script did not provide any scene objects");
|
||||
}
|
||||
|
||||
vertices_.clear();
|
||||
indices_.clear();
|
||||
renderObjects_.clear();
|
||||
|
||||
size_t vertexOffset = 0;
|
||||
size_t indexOffset = 0;
|
||||
for (const auto& sceneObject : sceneObjects) {
|
||||
RenderObject renderObject{};
|
||||
renderObject.vertexOffset = static_cast<int32_t>(vertexOffset);
|
||||
renderObject.indexOffset = static_cast<uint32_t>(indexOffset);
|
||||
renderObject.indexCount = static_cast<uint32_t>(sceneObject.indices.size());
|
||||
renderObject.computeModelMatrixRef = sceneObject.computeModelMatrixRef;
|
||||
renderObject.shaderKey = sceneObject.shaderKey;
|
||||
if (shaderPathMap_.find(renderObject.shaderKey) == shaderPathMap_.end()) {
|
||||
renderObject.shaderKey = defaultShaderKey_;
|
||||
}
|
||||
renderObjects_.push_back(renderObject);
|
||||
|
||||
vertices_.insert(vertices_.end(), sceneObject.vertices.begin(), sceneObject.vertices.end());
|
||||
for (uint16_t index : sceneObject.indices) {
|
||||
indices_.push_back(static_cast<uint16_t>(index + vertexOffset));
|
||||
}
|
||||
|
||||
vertexOffset += sceneObject.vertices.size();
|
||||
indexOffset += sceneObject.indices.size();
|
||||
}
|
||||
|
||||
if (vertices_.empty() || indices_.empty()) {
|
||||
throw std::runtime_error("Lua script did not provide cube geometry");
|
||||
throw std::runtime_error("Aggregated scene geometry is empty");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -928,7 +1072,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex, const PushConstants& pushConstants) {
|
||||
void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex, float time,
|
||||
const std::array<float, 16>& viewProj) {
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
|
||||
@@ -946,15 +1091,27 @@ private:
|
||||
renderPassInfo.pClearValues = &clearColor;
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline_);
|
||||
|
||||
VkBuffer vertexBuffers[] = {vertexBuffer_};
|
||||
VkDeviceSize offsets[] = {0};
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(commandBuffer, indexBuffer_, 0, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants),
|
||||
&pushConstants);
|
||||
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(indices_.size()), 1, 0, 0, 0);
|
||||
PushConstants pushConstants{};
|
||||
pushConstants.viewProj = viewProj;
|
||||
for (const auto& object : renderObjects_) {
|
||||
auto pipelineIt = graphicsPipelines_.find(object.shaderKey);
|
||||
if (pipelineIt == graphicsPipelines_.end()) {
|
||||
pipelineIt = graphicsPipelines_.find(defaultShaderKey_);
|
||||
if (pipelineIt == graphicsPipelines_.end()) {
|
||||
throw std::runtime_error("Missing pipeline for shader key");
|
||||
}
|
||||
}
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineIt->second);
|
||||
pushConstants.model = cubeScript_.ComputeModelMatrix(object.computeModelMatrixRef, time);
|
||||
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants),
|
||||
&pushConstants);
|
||||
vkCmdDrawIndexed(commandBuffer, object.indexCount, 1, object.indexOffset, object.vertexOffset, 0);
|
||||
}
|
||||
vkCmdEndRenderPass(commandBuffer);
|
||||
vkEndCommandBuffer(commandBuffer);
|
||||
}
|
||||
@@ -989,16 +1146,14 @@ private:
|
||||
throw std::runtime_error("Failed to acquire swap chain image");
|
||||
}
|
||||
|
||||
PushConstants pushConstants{};
|
||||
pushConstants.model = cubeScript_.ComputeModelMatrix(time);
|
||||
auto view = LookAt({2.0f, 2.0f, 2.5f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f});
|
||||
auto projection = Perspective(0.78f, static_cast<float>(swapChainExtent_.width) /
|
||||
static_cast<float>(swapChainExtent_.height),
|
||||
0.1f, 10.0f);
|
||||
pushConstants.viewProj = MultiplyMatrix(projection, view);
|
||||
auto viewProj = MultiplyMatrix(projection, view);
|
||||
|
||||
vkResetCommandBuffer(commandBuffers_[imageIndex], 0);
|
||||
RecordCommandBuffer(commandBuffers_[imageIndex], imageIndex, pushConstants);
|
||||
RecordCommandBuffer(commandBuffers_[imageIndex], imageIndex, time, viewProj);
|
||||
|
||||
VkSemaphore waitSemaphores[] = {imageAvailableSemaphore_};
|
||||
VkSemaphore signalSemaphores[] = {renderFinishedSemaphore_};
|
||||
@@ -1199,7 +1354,6 @@ private:
|
||||
std::vector<VkImageView> swapChainImageViews_;
|
||||
VkRenderPass renderPass_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkPipeline graphicsPipeline_ = VK_NULL_HANDLE;
|
||||
std::vector<VkFramebuffer> swapChainFramebuffers_;
|
||||
VkCommandPool commandPool_ = VK_NULL_HANDLE;
|
||||
std::vector<VkCommandBuffer> commandBuffers_;
|
||||
@@ -1212,8 +1366,12 @@ private:
|
||||
CubeScript cubeScript_;
|
||||
std::vector<Vertex> vertices_;
|
||||
std::vector<uint16_t> indices_;
|
||||
std::unordered_map<std::string, CubeScript::ShaderPaths> shaderPathMap_;
|
||||
std::unordered_map<std::string, VkPipeline> graphicsPipelines_;
|
||||
std::string defaultShaderKey_;
|
||||
VkFence inFlightFence_ = VK_NULL_HANDLE;
|
||||
bool framebufferResized_ = false;
|
||||
std::vector<RenderObject> renderObjects_;
|
||||
};
|
||||
|
||||
VulkanCubeApp::VulkanCubeApp(const std::filesystem::path& scriptPath)
|
||||
|
||||
Reference in New Issue
Block a user