feat: add audio system

This commit is contained in:
2026-10-23 22:07:52 +02:00
parent a3d3da4a67
commit 0b7cc38543
28 changed files with 7412 additions and 214 deletions
+10
View File
@@ -2,6 +2,7 @@
#define EDITOR_ASSETBROWSER_H
#include <filesystem>
#include <future>
#include <optional>
#include <string>
#include <vector>
@@ -10,6 +11,8 @@ class AssetBrowser final {
public:
std::optional<std::string> Draw(bool* open = nullptr);
void ConfigureCooking(std::filesystem::path cooker, std::vector<std::string> schemes);
private:
struct Entry {
std::string name;
@@ -17,6 +20,8 @@ private:
};
void Refresh();
void StartCook();
void PollCook();
void Navigate(const std::string& scheme, const std::filesystem::path& folder);
[[nodiscard]] std::string VirtualPath(const std::string& name) const;
void DrawEntry(const Entry& entry, std::optional<std::string>& opened, bool& navigated);
@@ -27,6 +32,11 @@ private:
std::vector<Entry> m_Entries;
std::string m_Filter;
bool m_Dirty{true};
std::filesystem::path m_Cooker;
std::vector<std::string> m_CookSchemes;
std::future<bool> m_Cook;
bool m_AppFocused{true};
};
#endif // EDITOR_ASSETBROWSER_H
+77 -1
View File
@@ -2,9 +2,15 @@
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <exception>
#include <utility>
#include <imgui.h>
#include <spdlog/spdlog.h>
#include <destrum/Assets/AssetDatabase.h>
#include <destrum/Assets/AssetTypes.h>
#include <destrum/FS/AssetFS.h>
#include <destrum/Graphics/Material.h>
@@ -42,13 +48,29 @@ namespace {
if (AssetTypes::IsPrefab(name)) return "[prefab] ";
if (AssetTypes::IsMaterial(name)) return "[material] ";
if (AssetTypes::IsImage(name)) return "[image] ";
if (AssetTypes::IsAudio(name)) return "[audio] ";
return "";
}
}
void AssetBrowser::ConfigureCooking(std::filesystem::path cooker, std::vector<std::string> schemes)
{
m_Cooker = std::move(cooker);
m_CookSchemes = std::move(schemes);
}
std::optional<std::string> AssetBrowser::Draw(bool* open)
{
std::optional<std::string> opened;
// Files dropped into assets_src from Explorer show up when the user comes back to the editor
const bool appFocused = !ImGui::GetIO().AppFocusLost;
if (appFocused && !m_AppFocused) {
StartCook();
}
m_AppFocused = appFocused;
PollCook();
if (!ImGui::Begin("Assets", open)) {
ImGui::End();
return opened;
@@ -70,9 +92,14 @@ std::optional<std::string> AssetBrowser::Draw(bool* open)
}
ImGui::SameLine();
}
if (ImGui::Button("Refresh")) {
const bool cooking = m_Cook.valid();
ImGui::BeginDisabled(cooking);
if (ImGui::Button(cooking ? "Cooking..." : "Refresh")) {
m_Dirty = true;
StartCook();
}
ImGui::EndDisabled();
ImGui::SetItemTooltip("Cooks new and changed files from assets_src, then lists the folder again");
ImGui::SameLine();
ImGui::SetNextItemWidth(-FLT_MIN);
ImGuiUtils::InputString("##Filter", m_Filter, 0, "Search this folder");
@@ -172,6 +199,55 @@ void AssetBrowser::Navigate(const std::string& scheme, const std::filesystem::pa
m_Dirty = true;
}
void AssetBrowser::StartCook()
{
if (m_Cooker.empty() || m_Cook.valid()) {
return;
}
std::vector<std::string> commands;
for (const std::string& scheme : m_CookSchemes) {
const FSMount* mount = FindMount(scheme);
if (mount == nullptr || !mount->sourceRoot) {
continue;
}
// cmd.exe strips the outer quotes, the inner ones keep paths with spaces intact
commands.push_back("\"\"" + m_Cooker.string() + "\" --input \"" + mount->sourceRoot->string() +
"\" --output \"" + mount->root.string() + "\"\"");
}
if (commands.empty()) {
return;
}
m_Cook = std::async(std::launch::async, [commands = std::move(commands)] {
bool succeeded = true;
for (const std::string& command : commands) {
succeeded = std::system(command.c_str()) == 0 && succeeded;
}
return succeeded;
});
}
void AssetBrowser::PollCook()
{
if (!m_Cook.valid() || m_Cook.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
return;
}
if (!m_Cook.get()) {
spdlog::error("Cooking the assets failed, see the log above");
}
try {
for (const std::string& scheme : m_CookSchemes) {
AssetFS::GetInstance().ReloadManifest(scheme);
}
AssetDatabase::GetInstance().Refresh();
} catch (const std::exception& exception) {
spdlog::error("Could not reload the asset manifest: {}", exception.what());
}
m_Dirty = true;
}
void AssetBrowser::Refresh()
{
m_Dirty = false;
+4
View File
@@ -53,6 +53,10 @@ void Editor::onAttach()
#if defined(DESTRUM_ENGINE_ASSETS_SRC_DIR) && defined(DESTRUM_THECHEF_PATH)
m_ShaderHotReload.Configure(DESTRUM_THECHEF_PATH, DESTRUM_ENGINE_ASSETS_SRC_DIR, assetFs.GetFullPath("engine://shaders").parent_path());
#endif
#ifdef DESTRUM_THECHEF_PATH
// Engine assets are left to the shader hot reload, two cooks writing one manifest would race
m_AssetBrowser.ConfigureCooking(DESTRUM_THECHEF_PATH, {"game"});
#endif
if (m_Settings.startInEditMode) {
m_PlayMode.EnterEditMode();
+6
View File
@@ -9,6 +9,7 @@
#include <destrum/App.h>
#include <destrum/Assets/AssetTypes.h>
#include <destrum/Components/AudioSource.h>
#include <destrum/Components/MeshRendererComponent.h>
#include <destrum/Components/Physics/Collider.h>
#include <destrum/Components/Physics/Rigidbody.h>
@@ -179,6 +180,11 @@ void Editor::OnAssetDropped(std::string_view path)
}
instance->GetTransform().SetWorldPosition(spawnPosition);
scene->SetSelectedObject(instance->GetId());
} else if (AssetTypes::IsAudio(path)) {
GameObject* object = scene->CreateGameObject(name);
object->AddComponent<AudioSource>(std::string{path});
object->GetTransform().SetWorldPosition(spawnPosition);
scene->SetSelectedObject(object->GetId());
} else if (AssetTypes::IsMaterial(path)) {
GameObject* selected = scene->GetSelectedObject();
if (selected == nullptr) {