From d534e57ad2209c4ef58b0629bebc904ac754819b Mon Sep 17 00:00:00 2001 From: Bram Verhulst Date: Sun, 21 Jun 2026 18:00:22 +0200 Subject: [PATCH] Fix some stuff --- destrum/assets_src/char2.fbx | Bin 61868 -> 84092 bytes destrum/assets_src/textures/T_T.png | Bin 0 -> 77400 bytes destrum/include/destrum/App.h | 4 + destrum/include/destrum/Graphics/GfxDevice.h | 2 + .../destrum/Graphics/Pipelines/MeshPipeline.h | 8 + destrum/include/destrum/Graphics/Renderer.h | 8 + destrum/include/destrum/Util/ModelLoader.h | 353 +++++++++++++----- destrum/src/App.cpp | 40 +- destrum/src/Graphics/GfxDevice.cpp | 5 +- destrum/src/Graphics/Pipeline.cpp | 2 +- .../src/Graphics/Pipelines/MeshPipeline.cpp | 2 + .../src/Graphics/Pipelines/SkyboxPipeline.cpp | 2 + destrum/src/Graphics/Swapchain.cpp | 74 +++- lightkeeper/src/Lightkeeper.cpp | 256 ++++++------- 14 files changed, 518 insertions(+), 238 deletions(-) create mode 100644 destrum/assets_src/textures/T_T.png diff --git a/destrum/assets_src/char2.fbx b/destrum/assets_src/char2.fbx index 5647086ced1c2c9ca28a2f4828646c737b507fd0..c9f094d347b9e323f01c03db74c02b4e89914ebb 100644 GIT binary patch delta 31537 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 delta 19862 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

XMkvwGMh(s?9-iN;lO=RE-IK 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 literal 0 HcmV?d00001 diff --git a/destrum/include/destrum/App.h b/destrum/include/destrum/App.h index 389c547..4e4f843 100644 --- a/destrum/include/destrum/App.h +++ b/destrum/include/destrum/App.h @@ -52,6 +52,10 @@ protected: bool frameLimit{false}; float frameTime{0.f}; float avgFPS{0.f}; + + bool resizePending = false; + std::chrono::steady_clock::time_point lastResizeTime{}; + }; diff --git a/destrum/include/destrum/Graphics/GfxDevice.h b/destrum/include/destrum/Graphics/GfxDevice.h index b2996f4..852efa3 100644 --- a/destrum/include/destrum/Graphics/GfxDevice.h +++ b/destrum/include/destrum/Graphics/GfxDevice.h @@ -91,6 +91,8 @@ public: VmaAllocator getAllocator() const { return allocator; } + vkb::Device getVkbDevice() const { return device; } + ImageID createImage(const vkutil::CreateImageInfo& createInfo, const std::string& debugName = "", void* pixelData = nullptr, ImageID imageId = NULL_IMAGE_ID); ImageID createDrawImage(VkFormat format, glm::ivec2 size, const std::string& debugName = "", ImageID imageId = NULL_IMAGE_ID); ImageID loadImageFromFile(const std::filesystem::path& path, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT, bool mipMap = false, TextureIntent intent = TextureIntent::ColorSrgb); diff --git a/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h b/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h index b7e1011..f00b300 100644 --- a/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h +++ b/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h @@ -29,6 +29,12 @@ public: void cleanup(VkDevice device); + void setRenderWireframe(bool wireframe) { + m_renderWireframe = wireframe; + } + + bool getRenderWireframe(){ return m_renderWireframe; } + private: VkPipelineLayout m_pipelineLayout; @@ -43,6 +49,8 @@ private: std::uint32_t padding; }; + bool m_renderWireframe{false}; + }; #endif //MESHPIPELINE_H diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index 5aff843..5a7ba85 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -61,6 +61,14 @@ public: return *skinningPipeline; } + void setRenderWireframe(bool wireframe) { + meshPipeline->setRenderWireframe(wireframe); + } + + bool getRenderWireframe() { + return meshPipeline->getRenderWireframe(); + } + private: void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate); diff --git a/destrum/include/destrum/Util/ModelLoader.h b/destrum/include/destrum/Util/ModelLoader.h index bdab3cc..b959747 100644 --- a/destrum/include/destrum/Util/ModelLoader.h +++ b/destrum/include/destrum/Util/ModelLoader.h @@ -34,11 +34,13 @@ #include #include +#include #include #include #include #include #include +#include #include namespace ModelLoader { @@ -50,6 +52,21 @@ static glm::mat4 ToGLM(const aiMatrix4x4& m) { return glm::transpose(glm::make_mat4(&m.a1)); } +static glm::vec3 ToGLM(const aiVector3D& v) { + return glm::vec3(v.x, v.y, v.z); +} + +static glm::quat ToGLM(const aiQuaternion& q) { + // glm::quat constructor order is (w, x, y, z). + return glm::normalize(glm::quat(q.w, q.x, q.y, q.z)); +} + +static glm::vec3 SafeNormalize(const glm::vec3& v, const glm::vec3& fallback) { + const float len2 = glm::dot(v, v); + if (len2 <= std::numeric_limits::epsilon()) return fallback; + return v * (1.0f / std::sqrt(len2)); +} + static void UpdateBounds(glm::vec3& mn, glm::vec3& mx, const glm::vec3& p) { mn.x = std::min(mn.x, p.x); mn.y = std::min(mn.y, p.y); @@ -59,6 +76,47 @@ static void UpdateBounds(glm::vec3& mn, glm::vec3& mx, const glm::vec3& p) { mx.z = std::max(mx.z, p.z); } +struct NodeTRS { + glm::vec3 translation{0.0f, 0.0f, 0.0f}; + glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f}; + glm::vec3 scale{1.0f, 1.0f, 1.0f}; +}; + +static NodeTRS DecomposeNodeTransform(const aiNode* node) { + NodeTRS out{}; + + aiVector3D scaling; + aiVector3D translation; + aiQuaternion rotation; + node->mTransformation.Decompose(scaling, rotation, translation); + + out.translation = ToGLM(translation); + out.rotation = ToGLM(rotation); + out.scale = ToGLM(scaling); + return out; +} + +static void CollectNodeDefaults(const aiNode* node, + std::unordered_map& defaults) { + if (!node) return; + + defaults[std::string(node->mName.C_Str())] = DecomposeNodeTransform(node); + + for (unsigned int c = 0; c < node->mNumChildren; ++c) + CollectNodeDefaults(node->mChildren[c], defaults); +} + +static std::unordered_map +BuildJointIndexMap(const Skeleton& skeleton) { + std::unordered_map map; + map.reserve(skeleton.jointNames.size()); + + for (std::size_t i = 0; i < skeleton.jointNames.size(); ++i) + map[skeleton.jointNames[i]] = static_cast(i); + + return map; +} + // ─── Post-process flags ─────────────────────────────────────────────────────── static constexpr unsigned int kImportFlags = @@ -82,16 +140,17 @@ static const aiScene* LoadScene(Assimp::Importer& importer, const std::string& p // For skinned meshes, pass glm::mat4{1.f} so vertices stay in bind-pose space. // Skinning data is NOT populated here — call LoadSkinningData afterwards. static CPUMesh LoadAiMeshIntoCPUMesh(const aiMesh* mesh, - const std::string& name, - const glm::mat4& world) { + const std::string& name, + const glm::mat4& world) { CPUMesh out{}; out.name = name; const size_t vertexCount = mesh->mNumVertices; out.vertices.resize(vertexCount); - const glm::mat3 nrmMat = glm::transpose(glm::inverse(glm::mat3(world))); - const glm::mat3 tanMat = glm::mat3(world); + const glm::mat3 world3 = glm::mat3(world); + const glm::mat3 nrmMat = glm::transpose(glm::inverse(world3)); + const glm::mat3 tanMat = world3; glm::vec3 mn{ std::numeric_limits::infinity()}; glm::vec3 mx{-std::numeric_limits::infinity()}; @@ -107,7 +166,8 @@ static CPUMesh LoadAiMeshIntoCPUMesh(const aiMesh* mesh, // Normal if (mesh->HasNormals()) { const aiVector3D& an = mesh->mNormals[i]; - v.normal = glm::normalize(nrmMat * glm::vec3(an.x, an.y, an.z)); + v.normal = SafeNormalize(nrmMat * glm::vec3(an.x, an.y, an.z), + glm::vec3(0.0f, 1.0f, 0.0f)); } else { v.normal = glm::vec3(0.0f, 1.0f, 0.0f); } @@ -125,8 +185,10 @@ static CPUMesh LoadAiMeshIntoCPUMesh(const aiMesh* mesh, if (mesh->HasTangentsAndBitangents()) { const aiVector3D& at = mesh->mTangents[i]; const aiVector3D& ab = mesh->mBitangents[i]; - glm::vec3 t3 = glm::normalize(tanMat * glm::vec3(at.x, at.y, at.z)); - glm::vec3 b3 = glm::normalize(tanMat * glm::vec3(ab.x, ab.y, ab.z)); + glm::vec3 t3 = SafeNormalize(tanMat * glm::vec3(at.x, at.y, at.z), + glm::vec3(1.0f, 0.0f, 0.0f)); + glm::vec3 b3 = SafeNormalize(tanMat * glm::vec3(ab.x, ab.y, ab.z), + glm::vec3(0.0f, 1.0f, 0.0f)); glm::vec3 n3 = v.normal; float sign = (glm::dot(glm::cross(n3, t3), b3) < 0.0f) ? -1.0f : 1.0f; v.tangent = glm::vec4(t3, sign); @@ -182,6 +244,7 @@ static void AppendMesh(CPUMesh& dst, const CPUMesh& src) { static Skeleton LoadSkeleton(const aiScene* scene) { Skeleton skeleton; + skeleton.rootPreTransform = glm::mat4{1.0f}; // Pass 1: collect all bone names and their inverse bind matrices // from every mesh in the scene. @@ -223,7 +286,7 @@ static Skeleton LoadSkeleton(const aiScene* scene) { stack.pop_back(); std::string name(node->mName.C_Str()); - int myIdx = parentIdx; + int myIdx = parentIdx; glm::mat4 myAccWorld = accWorld; // only used while still outside the bone hierarchy if (boneOffsets.count(name)) { @@ -237,7 +300,7 @@ static Skeleton LoadSkeleton(const aiScene* scene) { skeleton.jointNames.push_back(name); skeleton.inverseBindMatrices.push_back(boneOffsets[name]); - // Grow hierarchy arrays to accommodate this id + // Grow hierarchy arrays to accommodate this id. while (skeleton.hierarchy.size() <= id) skeleton.hierarchy.push_back({}); skeleton.hierarchy[id].id = id; @@ -251,10 +314,11 @@ static Skeleton LoadSkeleton(const aiScene* scene) { skeleton.rootPreTransform = accWorld; glm::mat4& m = skeleton.rootPreTransform; - // Normalize each basis vector to remove scale - m[0] = glm::vec4(glm::normalize(glm::vec3(m[0])), 0.f); - m[1] = glm::vec4(glm::normalize(glm::vec3(m[1])), 0.f); - m[2] = glm::vec4(glm::normalize(glm::vec3(m[2])), 0.f); + // Normalize each basis vector to remove scale without creating + // NaNs if an imported file contains a degenerate transform. + m[0] = glm::vec4(SafeNormalize(glm::vec3(m[0]), glm::vec3(1.0f, 0.0f, 0.0f)), 0.f); + m[1] = glm::vec4(SafeNormalize(glm::vec3(m[1]), glm::vec3(0.0f, 1.0f, 0.0f)), 0.f); + m[2] = glm::vec4(SafeNormalize(glm::vec3(m[2]), glm::vec3(0.0f, 0.0f, 1.0f)), 0.f); } // Once we are inside the bone hierarchy the pre-transform is fully @@ -265,7 +329,7 @@ static Skeleton LoadSkeleton(const aiScene* scene) { myAccWorld = accWorld * ToGLM(node->mTransformation); } - // Push children in reverse so left-most child is processed first + // Push children in reverse so left-most child is processed first. for (int c = static_cast(node->mNumChildren) - 1; c >= 0; --c) stack.push_back({ node->mChildren[c], myIdx, myAccWorld }); } @@ -279,119 +343,240 @@ static Skeleton LoadSkeleton(const aiScene* scene) { // Populate cpuMesh.skinningData from the matching aiMesh. // Must be called after LoadAiMeshIntoCPUMesh so vertices are already sized. static void LoadSkinningData(CPUMesh& cpuMesh, - const aiMesh* aiMesh, - const Skeleton& skeleton) { + const aiMesh* aiMesh, + const Skeleton& skeleton) { if (!aiMesh->HasBones() || skeleton.joints.empty()) return; cpuMesh.skinningData.assign(cpuMesh.vertices.size(), CPUMesh::SkinningData{ {0, 0, 0, 0}, {0.f, 0.f, 0.f, 0.f} }); + const auto jointIndexByName = BuildJointIndexMap(skeleton); std::vector weightCount(cpuMesh.vertices.size(), 0); for (unsigned int b = 0; b < aiMesh->mNumBones; ++b) { const aiBone* bone = aiMesh->mBones[b]; std::string boneName(bone->mName.C_Str()); - // Find which joint index this bone maps to - std::uint32_t jointIdx = 0; - bool found = false; - for (std::size_t j = 0; j < skeleton.jointNames.size(); ++j) { - if (skeleton.jointNames[j] == boneName) { - jointIdx = static_cast(j); - found = true; - break; - } - } - if (!found) continue; + const auto jointIt = jointIndexByName.find(boneName); + if (jointIt == jointIndexByName.end()) continue; + + const std::uint32_t jointIdx = jointIt->second; for (unsigned int w = 0; w < bone->mNumWeights; ++w) { const unsigned int vertIdx = bone->mWeights[w].mVertexId; - const float weight = bone->mWeights[w].mWeight; - const int slot = weightCount[vertIdx]; + if (vertIdx >= cpuMesh.skinningData.size()) continue; - if (slot >= 4) continue; // aiProcess_LimitBoneWeights should prevent this + const float weight = bone->mWeights[w].mWeight; + const int slot = weightCount[vertIdx]; + + if (slot >= 4) continue; // aiProcess_LimitBoneWeights should prevent this. cpuMesh.skinningData[vertIdx].jointIds[slot] = jointIdx; cpuMesh.skinningData[vertIdx].weights[slot] = weight; ++weightCount[vertIdx]; } } + + // Normalize weights so the shader does not accidentally shrink/explode + // vertices if the importer returned imperfect totals. + for (std::size_t i = 0; i < cpuMesh.skinningData.size(); ++i) { + auto& skin = cpuMesh.skinningData[i]; + const float sum = skin.weights[0] + skin.weights[1] + skin.weights[2] + skin.weights[3]; + + if (sum > std::numeric_limits::epsilon()) { + skin.weights[0] /= sum; + skin.weights[1] /= sum; + skin.weights[2] /= sum; + skin.weights[3] /= sum; + } else { + // Defensive fallback for malformed meshes with unweighted vertices. + skin.jointIds = {0, 0, 0, 0}; + skin.weights = {1.f, 0.f, 0.f, 0.f}; + } + } } // ─── Animation loading ──────────────────────────────────────────────────────── +static glm::vec3 SampleVectorKeys(const aiVectorKey* keys, + unsigned int count, + double tick, + const glm::vec3& fallback) { + if (!keys || count == 0) return fallback; + if (count == 1 || tick <= keys[0].mTime) return ToGLM(keys[0].mValue); + + for (unsigned int i = 0; i + 1 < count; ++i) { + const double t0 = keys[i].mTime; + const double t1 = keys[i + 1].mTime; + + if (tick <= t1) { + const double denom = t1 - t0; + const float alpha = denom > 0.0 + ? static_cast((tick - t0) / denom) + : 0.0f; + + return glm::mix(ToGLM(keys[i].mValue), ToGLM(keys[i + 1].mValue), alpha); + } + } + + return ToGLM(keys[count - 1].mValue); +} + +static glm::quat SampleQuatKeys(const aiQuatKey* keys, + unsigned int count, + double tick, + const glm::quat& fallback) { + if (!keys || count == 0) return fallback; + if (count == 1 || tick <= keys[0].mTime) return ToGLM(keys[0].mValue); + + for (unsigned int i = 0; i + 1 < count; ++i) { + const double t0 = keys[i].mTime; + const double t1 = keys[i + 1].mTime; + + if (tick <= t1) { + const double denom = t1 - t0; + const float alpha = denom > 0.0 + ? static_cast((tick - t0) / denom) + : 0.0f; + + return glm::normalize(glm::slerp(ToGLM(keys[i].mValue), + ToGLM(keys[i + 1].mValue), + alpha)); + } + } + + return ToGLM(keys[count - 1].mValue); +} + +static void AppendUniqueKeyTimes(std::vector& times, + const aiVectorKey* keys, + unsigned int count) { + if (!keys) return; + for (unsigned int i = 0; i < count; ++i) + times.push_back(keys[i].mTime); +} + +static void AppendUniqueKeyTimes(std::vector& times, + const aiQuatKey* keys, + unsigned int count) { + if (!keys) return; + for (unsigned int i = 0; i < count; ++i) + times.push_back(keys[i].mTime); +} + +static void SortAndUniqueKeyTimes(std::vector& times) { + std::sort(times.begin(), times.end()); + + constexpr double epsilon = 1e-6; + times.erase(std::unique(times.begin(), times.end(), + [](double a, double b) { return std::abs(a - b) <= epsilon; }), + times.end()); +} + +static double GetMaxAnimationKeyTick(const aiAnimation* aiAnim) { + double maxTick = 0.0; + + for (unsigned int c = 0; c < aiAnim->mNumChannels; ++c) { + const aiNodeAnim* ch = aiAnim->mChannels[c]; + + if (ch->mNumPositionKeys > 0) + maxTick = std::max(maxTick, ch->mPositionKeys[ch->mNumPositionKeys - 1].mTime); + if (ch->mNumRotationKeys > 0) + maxTick = std::max(maxTick, ch->mRotationKeys[ch->mNumRotationKeys - 1].mTime); + if (ch->mNumScalingKeys > 0) + maxTick = std::max(maxTick, ch->mScalingKeys[ch->mNumScalingKeys - 1].mTime); + } + + return maxTick; +} + static std::vector LoadAnimations(const aiScene* scene, - const Skeleton& skeleton) { + const Skeleton& skeleton) { std::vector result; - if (!scene->HasAnimations()) return result; + if (!scene->HasAnimations() || skeleton.joints.empty()) return result; + + const auto jointIndexByName = BuildJointIndexMap(skeleton); + + std::unordered_map nodeDefaults; + CollectNodeDefaults(scene->mRootNode, nodeDefaults); + + result.reserve(scene->mNumAnimations); for (unsigned int a = 0; a < scene->mNumAnimations; ++a) { const aiAnimation* aiAnim = scene->mAnimations[a]; const double tps = aiAnim->mTicksPerSecond > 0.0 ? aiAnim->mTicksPerSecond : 25.0; + const double durationTicks = aiAnim->mDuration > 0.0 + ? aiAnim->mDuration + : GetMaxAnimationKeyTick(aiAnim); + SkeletalAnimation anim; - anim.name = aiAnim->mName.C_Str(); - anim.duration = static_cast(aiAnim->mDuration / tps); + anim.name = aiAnim->mName.length > 0 + ? std::string(aiAnim->mName.C_Str()) + : ("Animation_" + std::to_string(a)); + anim.duration = static_cast(durationTicks / tps); anim.looped = true; + anim.tracks.reserve(aiAnim->mNumChannels); + for (unsigned int c = 0; c < aiAnim->mNumChannels; ++c) { const aiNodeAnim* ch = aiAnim->mChannels[c]; - std::string boneName(ch->mNodeName.C_Str()); + const std::string boneName(ch->mNodeName.C_Str()); - // Map bone name to joint index - std::uint32_t jointIdx = 0; - bool found = false; - for (std::size_t j = 0; j < skeleton.jointNames.size(); ++j) { - if (skeleton.jointNames[j] == boneName) { - jointIdx = static_cast(j); - found = true; - break; - } - } - if (!found) continue; + const auto jointIt = jointIndexByName.find(boneName); + if (jointIt == jointIndexByName.end()) continue; - SkeletalAnimation::Track track; - track.jointIndex = jointIdx; + const auto defaultIt = nodeDefaults.find(boneName); + const NodeTRS defaults = defaultIt != nodeDefaults.end() + ? defaultIt->second + : NodeTRS{}; // Position, rotation and scale channels can have different keyframe - // counts and independent time axes. Build the track from the position - // channel's timeline and pick the nearest rotation/scale key for each - // sample rather than assuming index alignment. This avoids corrupted - // keyframes when counts differ. - const unsigned int numPos = ch->mNumPositionKeys; - const unsigned int numRot = ch->mNumRotationKeys; - const unsigned int numSca = ch->mNumScalingKeys; + // counts and independent time axes. Build a unified timeline from + // every key time, then sample/interpolate every TRS component at + // that time. + // + // This fixes the old loader's bad assumptions: + // - rotation/scale keys might be absent; + // - scale-only tracks are valid; + // - key counts do not have to match; + // - key index k does not imply the same timestamp in each channel; + // - missing components must fall back to the node's bind/local pose, + // not zero translation or invalid array access. + std::vector keyTicks; + keyTicks.reserve(ch->mNumPositionKeys + ch->mNumRotationKeys + ch->mNumScalingKeys); - // Use position keys to drive the timeline (most common case). - // If there are no position keys fall back to rotation keys. - const unsigned int numKeys = numPos > 0 ? numPos : numRot; + AppendUniqueKeyTimes(keyTicks, ch->mPositionKeys, ch->mNumPositionKeys); + AppendUniqueKeyTimes(keyTicks, ch->mRotationKeys, ch->mNumRotationKeys); + AppendUniqueKeyTimes(keyTicks, ch->mScalingKeys, ch->mNumScalingKeys); + SortAndUniqueKeyTimes(keyTicks); - for (unsigned int k = 0; k < numKeys; ++k) { + if (keyTicks.empty()) { + // Extremely defensive: an animation channel with no keys should + // not normally exist, but keep a stable bind-pose track instead + // of returning an empty/corrupt track. + keyTicks.push_back(0.0); + } + + SkeletalAnimation::Track track; + track.jointIndex = jointIt->second; + track.keyframes.reserve(keyTicks.size()); + + for (double tick : keyTicks) { SkeletalAnimation::Keyframe kf; - - // Time comes from whichever channel drives this loop - if (numPos > 0) { - kf.time = static_cast(ch->mPositionKeys[k].mTime / tps); - const auto& p = ch->mPositionKeys[k].mValue; - kf.translation = { p.x, p.y, p.z }; - } else { - kf.time = static_cast(ch->mRotationKeys[k].mTime / tps); - kf.translation = { 0.f, 0.f, 0.f }; - } - - // Nearest rotation key at this index - { - const unsigned int ri = std::min(k, numRot - 1); - const auto& r = ch->mRotationKeys[ri].mValue; - kf.rotation = glm::quat(r.w, r.x, r.y, r.z); // glm: (w,x,y,z) - } - - // Nearest scale key at this index - { - const unsigned int si = std::min(k, numSca - 1); - const auto& s = ch->mScalingKeys[si].mValue; - kf.scale = { s.x, s.y, s.z }; - } + kf.time = static_cast(tick / tps); + kf.translation = SampleVectorKeys(ch->mPositionKeys, + ch->mNumPositionKeys, + tick, + defaults.translation); + kf.rotation = SampleQuatKeys(ch->mRotationKeys, + ch->mNumRotationKeys, + tick, + defaults.rotation); + kf.scale = SampleVectorKeys(ch->mScalingKeys, + ch->mNumScalingKeys, + tick, + defaults.scale); track.keyframes.push_back(kf); } @@ -558,4 +743,4 @@ static SkinnedModel LoadSkinnedModel(const std::string& path) { } // namespace ModelLoader -#endif // MODELLOADER_H \ No newline at end of file +#endif // MODELLOADER_H diff --git a/destrum/src/App.cpp b/destrum/src/App.cpp index fc6442f..e895608 100644 --- a/destrum/src/App.cpp +++ b/destrum/src/App.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -66,6 +67,8 @@ void App::run() { InputManager::GetInstance().BeginFrame(); camera.Update(dt); + + SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { @@ -76,8 +79,11 @@ void App::run() { switch (event.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_RESIZED: - m_params.windowSize = { event.window.data1, event.window.data2 }; + { + resizePending = true; + lastResizeTime = std::chrono::steady_clock::now(); break; + } } } if (InputManager::GetInstance().ProcessEvent(event)) { @@ -104,15 +110,33 @@ void App::run() { const float alpha = accumulator / fixedDt; - if (!gfxDevice.needsSwapchainRecreate()) { - customDraw(); + if (gfxDevice.needsSwapchainRecreate() || resizePending) { + auto now = std::chrono::steady_clock::now(); + + if (resizePending && + now - lastResizeTime < std::chrono::milliseconds(100)) { + continue; + } + + int w = 0; + int h = 0; + SDL_Vulkan_GetDrawableSize(window, &w, &h); + + if (w == 0 || h == 0) { + continue; + } + + spdlog::info("Recreating swapchain to size: {}x{}", w, h); + + gfxDevice.recreateSwapchain(w, h); + onWindowResize(w, h); + + resizePending = false; + continue; } - if (gfxDevice.needsSwapchainRecreate()) { - spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y); - gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y); - onWindowResize(m_params.windowSize.x, m_params.windowSize.y); - } + customDraw(); + if (frameLimit) { auto sleepTime = Time::GetInstance().SleepDuration(); diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp index 9bd0d80..1f29409 100644 --- a/destrum/src/Graphics/GfxDevice.cpp +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -45,7 +45,8 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) .imageCubeArray = VK_TRUE, .geometryShader = VK_TRUE, // for im3d .depthClamp = VK_TRUE, - .samplerAnisotropy = VK_TRUE, + .fillModeNonSolid = VK_TRUE, + .samplerAnisotropy = VK_TRUE }; constexpr auto features12 = VkPhysicalDeviceVulkan12Features{ @@ -63,11 +64,13 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) .dynamicRendering = true, }; + physicalDevice = vkb::PhysicalDeviceSelector{instance} .set_minimum_version(1, 3) .set_required_features(deviceFeatures) .set_required_features_12(features12) .set_required_features_13(features13) + .add_required_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) .set_surface(surface) .prefer_gpu_device_type(vkb::PreferredDeviceType::discrete) .select() diff --git a/destrum/src/Graphics/Pipeline.cpp b/destrum/src/Graphics/Pipeline.cpp index 45c0dd4..356ce90 100644 --- a/destrum/src/Graphics/Pipeline.cpp +++ b/destrum/src/Graphics/Pipeline.cpp @@ -89,7 +89,7 @@ void Pipeline::DefaultPipelineConfigInfo(PipelineConfigInfo& configInfo) { configInfo.depthStencilInfo.front = {}; // Optional configInfo.depthStencilInfo.back = {}; // Optional - configInfo.dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + configInfo.dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_POLYGON_MODE_EXT}; configInfo.dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; configInfo.dynamicStateInfo.pDynamicStates = configInfo.dynamicStateEnables.data(); configInfo.dynamicStateInfo.dynamicStateCount = static_cast(configInfo.dynamicStateEnables.size()); diff --git a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp index ca8c552..bf256c7 100644 --- a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp @@ -89,6 +89,8 @@ void MeshPipeline::draw(VkCommandBuffer cmd, }; vkCmdSetScissor(cmd, 0, 1, &scissor); + vkCmdSetPolygonModeEXT(cmd, m_renderWireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL); + auto prevMeshId = NULL_MESH_ID; const auto frustum = edge::createFrustumFromCamera(camera); diff --git a/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp b/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp index c36cd73..2a0b22c 100644 --- a/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp @@ -81,6 +81,8 @@ void SkyboxPipeline::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camer skyboxRotation = glm::rotate(skyboxRotation, rotationSpeed * static_cast(Time::GetInstance().DeltaTime()), glm::vec3(0.f, 1.f, 0.f)); pipeline->bind(cmd); + vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL); + gfxDevice.bindBindlessDescSet(cmd, pipelineLayout); const glm::mat3 r = glm::mat3(skyboxRotation); diff --git a/destrum/src/Graphics/Swapchain.cpp b/destrum/src/Graphics/Swapchain.cpp index 73085c9..19fc22b 100644 --- a/destrum/src/Graphics/Swapchain.cpp +++ b/destrum/src/Graphics/Swapchain.cpp @@ -65,40 +65,72 @@ void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint extent = m_swapchain.extent; } -void Swapchain::recreateSwapchain(const GfxDevice& gfxDevice, VkFormat format, std::uint32_t width, std::uint32_t height, bool vSync) { - assert(m_swapchain); +void Swapchain::recreateSwapchain( + const GfxDevice& gfxDevice, + VkFormat format, + std::uint32_t width, + std::uint32_t height, + bool vSync) +{ + if (width == 0 || height == 0) { + dirty = true; + return; + } + + VkDevice device = gfxDevice.getDevice(); + + vkDeviceWaitIdle(device); + + auto oldSwapchain = m_swapchain; + + auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()} + .set_old_swapchain(oldSwapchain) + .set_desired_format(VkSurfaceFormatKHR{ + .format = format, + .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + }) + .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT) + .set_desired_present_mode( + vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR) + .set_desired_extent(width, height) + .build(); - assert(format == VK_FORMAT_B8G8R8A8_SRGB && "TODO: test other formats"); - auto res = vkb::SwapchainBuilder{gfxDevice.getDevice()} - .set_old_swapchain(m_swapchain) - .set_desired_format(VkSurfaceFormatKHR{ - .format = format, - .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - }) - .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT) - .set_desired_present_mode( - vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR) - .set_desired_extent(width, height) - .build(); if (!res.has_value()) { throw std::runtime_error(std::format( "failed to create swapchain: error = {}, vk result = {}", res.full_error().type.message(), string_VkResult(res.full_error().vk_result))); } - vkb::destroy_swapchain(m_swapchain); - for (auto imageView: imageViews) { - vkDestroyImageView(m_gfxDevice->getDevice(), imageView, nullptr); + for (auto sem : imageRenderSemaphores) { + vkDestroySemaphore(device, sem, nullptr); } + imageRenderSemaphores.clear(); + + for (auto imageView : imageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + imageViews.clear(); + + vkb::destroy_swapchain(oldSwapchain); m_swapchain = res.value(); images = m_swapchain.get_images().value(); imageViews = m_swapchain.get_image_views().value(); - dirty = false; + VkSemaphoreCreateInfo sci{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + + imageRenderSemaphores.resize(images.size()); + + for (auto& sem : imageRenderSemaphores) { + VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &sem)); + } + extent = m_swapchain.extent; + dirty = false; } void Swapchain::cleanup() { @@ -126,6 +158,12 @@ void Swapchain::resetFences(int index) const { VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence)); } +struct SwapchainAcquireResult { + VkResult result = VK_SUCCESS; + VkImage image = VK_NULL_HANDLE; + uint32_t imageIndex = 0; +}; + std::pair Swapchain::acquireNextImage(int index) { std::uint32_t swapchainImageIndex{}; const auto result = vkAcquireNextImageKHR( diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index b203a02..33fd4cb 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -25,143 +25,147 @@ void LightKeeper::customInit() { const float aspectRatio = static_cast(m_params.renderSize.x) / static_cast(m_params.renderSize.y); camera.setAspectRatio(aspectRatio); - // - // testMesh.name = "Test Mesh"; - // auto list_of_models = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://kitty.glb").generic_string()); - // testMesh = list_of_models[0]; - // testMeshID = meshCache.addMesh(gfxDevice, testMesh); - // spdlog::info("TestMesh uploaded with id: {}", testMeshID); - // - // auto testimgID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://kitty.png")); - // spdlog::info("Test image loaded with id: {}", testimgID); - // testMaterialID = materialCache.addMaterial(gfxDevice, { - // .baseColor = glm::vec3(1.f), - // .diffuseTexture = testimgID, - // }); - // spdlog::info("Test material created with id: {}", testMaterialID); - // - // camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); - // + + testMesh.name = "Test Mesh"; + auto list_of_models = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://kitty.glb").generic_string()); + testMesh = list_of_models[0]; + testMeshID = meshCache.addMesh(gfxDevice, testMesh); + spdlog::info("TestMesh uploaded with id: {}", testMeshID); + + auto testimgID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://kitty.png")); + spdlog::info("Test image loaded with id: {}", testimgID); + testMaterialID = materialCache.addMaterial(gfxDevice, { + .baseColor = glm::vec3(1.f), + .diffuseTexture = testimgID, + }); + spdlog::info("Test material created with id: {}", testMaterialID); + + camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); + auto& scene = SceneManager::GetInstance().CreateScene("Main"); + + // auto testCube = std::make_shared("TestCube"); + // auto meshComp = testCube->AddComponent(); + // meshComp->SetMeshID(testMeshID); + // meshComp->SetMaterialID(testMaterialID); + const int count = 100; + const float radius = 5.0f; + + const float orbitRadius = 5.0f; + + for (int i = 0; i < count; ++i) { + // auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); + // + // auto childMeshComp = childCube->AddComponent(); + // childMeshComp->SetMeshID(testMeshID); + // childMeshComp->SetMaterialID(testMaterialID); + // + // childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); + // + // // Add orbit + self spin + // auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); + // orbit->Randomize(1337u + (uint32_t)i); // stable random per index + // + // scene.Add(childCube); + } + // testCube->AddComponent(glm::vec3(0, 1, 0), glm::radians(10.0f)); // spin around Y, rad/sec + //rotate 180 around X axis + // testCube->GetTransform().SetLocalRotation(glm::quat(glm::vec3(glm::radians(180.0f), 0.0f, 0.0f))); // - // // auto testCube = std::make_shared("TestCube"); - // // auto meshComp = testCube->AddComponent(); - // // meshComp->SetMeshID(testMeshID); - // // meshComp->SetMaterialID(testMaterialID); - // const int count = 100; - // const float radius = 5.0f; + auto globeRoot = std::make_shared("GlobeRoot"); + globeRoot->GetTransform().SetWorldPosition(glm::vec3(0.0f)); + globeRoot->AddComponent(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec + scene.Add(globeRoot); + + + + // scene.Add(testCube); + + // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg"); + // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/mars.jpg"); + const auto skyboxID = AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr"); // - // const float orbitRadius = 5.0f; + // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png"); // - // for (int i = 0; i < count; ++i) { - // // auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); - // // - // // auto childMeshComp = childCube->AddComponent(); - // // childMeshComp->SetMeshID(testMeshID); - // // childMeshComp->SetMaterialID(testMaterialID); - // // - // // childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); - // // - // // // Add orbit + self spin - // // auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); - // // orbit->Randomize(1337u + (uint32_t)i); // stable random per index - // // - // // scene.Add(childCube); - // } - // // testCube->AddComponent(glm::vec3(0, 1, 0), glm::radians(10.0f)); // spin around Y, rad/sec - // //rotate 180 around X axis - // // testCube->GetTransform().SetLocalRotation(glm::quat(glm::vec3(glm::radians(180.0f), 0.0f, 0.0f))); - // // - // auto globeRoot = std::make_shared("GlobeRoot"); - // globeRoot->GetTransform().SetWorldPosition(glm::vec3(0.0f)); - // globeRoot->AddComponent(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec - // scene.Add(globeRoot); + const auto vertShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert"); + const auto fragShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag"); // + skyboxCubemap = std::make_unique(); + skyboxCubemap->LoadCubeMap(skyboxID.generic_string()); + skyboxCubemap->InitCubemapPipeline(vertShaderPath.generic_string(), fragShaderPath.generic_string()); + skyboxCubemap->CreateCubeMap(); + + renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID()); + // - // - // // scene.Add(testCube); - // - // // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg"); - // // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/mars.jpg"); - // const auto skyboxID = AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr"); - // // - // // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png"); - // // - // const auto vertShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert"); - // const auto fragShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag"); - // // - // skyboxCubemap = std::make_unique(); - // skyboxCubemap->LoadCubeMap(skyboxID.generic_string()); - // skyboxCubemap->InitCubemapPipeline(vertShaderPath.generic_string(), fragShaderPath.generic_string()); - // skyboxCubemap->CreateCubeMap(); - // - // renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID()); - // - // // - // const auto planeObj = std::make_shared("GroundPlane"); - // const auto planeMeshComp = planeObj->AddComponent(); - // const auto planeModel = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://plane.glb").generic_string()); - // const auto planeMeshID = meshCache.addMesh(gfxDevice, planeModel[0]); - // - // const auto planeTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://grass.png")); - // const auto planeMaterialID = materialCache.addMaterial(gfxDevice, { - // .baseColor = glm::vec3(1.f), - // .textureFilteringMode = TextureFilteringMode::Nearest, - // .diffuseTexture = planeTextureID, - // .name = "GroundPlaneMaterial", - // }); - // planeMeshComp->SetMeshID(planeMeshID); - // planeMeshComp->SetMaterialID(planeMaterialID); - // planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f)); - // planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f)); - // scene.Add(planeObj); - // - // - // // At the bottom of customInit(), replace the incomplete CharObj block: - // - // const auto CharObj = std::make_shared("Character"); - // - // auto charModel = ModelLoader::LoadSkinnedModel( - // AssetFS::GetInstance().GetFullPath("engine://char2.fbx").generic_string() - // ); - // - // const auto charMeshID = meshCache.addMesh(gfxDevice, charModel.meshes[0]); - // - // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg")); - // const auto charMaterialID = materialCache.addMaterial(gfxDevice, { - // .baseColor = glm::vec3(1.f), - // .diffuseTexture = charTextureID, - // .name = "CharacterMaterial", - // }); - // - // const auto charMeshComp = CharObj->AddComponent(); - // charMeshComp->SetMeshID(charMeshID); - // charMeshComp->SetMaterialID(charMaterialID); - // - // - // const auto animator = CharObj->AddComponent(); - // animator->setSkeleton(std::move(charModel.skeleton)); - // for (auto& clip : charModel.animations) { - // animator->addClip(std::make_shared(std::move(clip))); - // } - // - // for (const auto& clip : charModel.animations) - // spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); - // - // if (!charModel.animations.empty()) - // // animator->play("Armature|Armature|mixamo.com"); - // // // animator->play(charModel.animations[0].name); - // animator->play("Armature|Armature|Armature|main"); - // // or: animator->play("Run", 0.2f); // 0.2s cross-fade - // - // CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f)); - // // CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f); - // scene.Add(CharObj); + const auto planeObj = std::make_shared("GroundPlane"); + const auto planeMeshComp = planeObj->AddComponent(); + const auto planeModel = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://plane.glb").generic_string()); + const auto planeMeshID = meshCache.addMesh(gfxDevice, planeModel[0]); + + const auto planeTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://grass.png")); + const auto planeMaterialID = materialCache.addMaterial(gfxDevice, { + .baseColor = glm::vec3(1.f), + .textureFilteringMode = TextureFilteringMode::Nearest, + .diffuseTexture = planeTextureID, + .name = "GroundPlaneMaterial", + }); + planeMeshComp->SetMeshID(planeMeshID); + planeMeshComp->SetMaterialID(planeMaterialID); + planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f)); + planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f)); + scene.Add(planeObj); + + + // At the bottom of customInit(), replace the incomplete CharObj block: + + const auto CharObj = std::make_shared("Character"); + + auto charModel = ModelLoader::LoadSkinnedModel( + AssetFS::GetInstance().GetFullPath("engine://char2.fbx").generic_string() + ); + + const auto charMeshID = meshCache.addMesh(gfxDevice, charModel.meshes[0]); + + const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://textures/T_T.png")); + const auto charMaterialID = materialCache.addMaterial(gfxDevice, { + .baseColor = glm::vec3(1.f), + .diffuseTexture = charTextureID, + .name = "CharacterMaterial", + }); + + const auto charMeshComp = CharObj->AddComponent(); + charMeshComp->SetMeshID(charMeshID); + charMeshComp->SetMaterialID(charMaterialID); + + + const auto animator = CharObj->AddComponent(); + animator->setSkeleton(std::move(charModel.skeleton)); + for (auto& clip : charModel.animations) { + animator->addClip(std::make_shared(std::move(clip))); + } + + for (const auto& clip : charModel.animations) + spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); + + if (!charModel.animations.empty()) + // animator->play("Armature|Armature|mixamo.com"); + // // animator->play(charModel.animations[0].name); + animator->play("Armature|Armature|Armature|main"); + // or: animator->play("Run", 0.2f); // 0.2s cross-fade + + CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f)); + // CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f); + scene.Add(CharObj); } void LightKeeper::customUpdate(float dt) { camera.Update(dt); SceneManager::GetInstance().Update(); + + if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1)) { + renderer.setRenderWireframe(!renderer.getRenderWireframe()); + } } void LightKeeper::customDraw() {