Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
$editorItems = @(
"imgui.ini",
"App.lsettings",
"SandboxProject",
"LuxSampleProject",
"Resources",
"mono"
)
Expand Down
10 changes: 7 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Intermediates/
**.vcxproj.filters
**.vcxproj.user
**.csproj
**.csproj.user

# Directories
scripts/__pycache__
Expand All @@ -25,8 +26,11 @@ Sandbox/Resources/Cache
Core/vendor/GLAD
Core/vendor/NFD-Extended
Core/vendor/tracy
Editor/SandboxProject/Assets/Scripts/Binaries
Editor/LuxSampleProject/Assets/Scripts/Binaries
Editor/Resources/Cache
Lux.slnx
Editor/SandboxProject/Assets/Cache/Thumbnails
Editor/SandboxProject/Assets/AssetRegistry.lzr
Editor/LuxSampleProject/Assets/Cache/Thumbnails
Editor/LuxSampleProject/Assets/AssetRegistry.lzr
*.lap
*.aps
*.slnx
10 changes: 9 additions & 1 deletion Core/Source/Lux/Asset/MeshSerializer.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#include "lpch.h"
#include "MeshSerializer.h"

#include "AssimpMeshImporter.h"
#include "MeshRuntimeSerializer.h"

#include "Lux/Asset/AssetManager.h"
#include "Lux/Project/Project.h"
#include "Lux/Renderer/Mesh.h"

#ifndef LUX_DIST
#include "AssimpMeshImporter.h"
#endif

#include <fstream>
#include <sstream>

Expand Down Expand Up @@ -148,6 +151,10 @@ namespace Lux

bool MeshSourceSerializer::TryLoadData(const AssetMetadata& metadata, Ref<Asset>& asset) const
{
#ifdef LUX_DIST
LUX_CORE_ERROR("MeshSourceSerializer cannot import source meshes in Dist builds: {}", metadata.FilePath.string());
return false;
#else
AssimpMeshImporter importer(Project::GetEditorAssetManager()->GetFileSystemPath(metadata));
Ref<MeshSource> meshSource = importer.ImportToMeshSource();
if (!meshSource)
Expand All @@ -156,6 +163,7 @@ namespace Lux
meshSource->Handle = metadata.Handle;
asset = meshSource;
return true;
#endif
}

bool MeshSourceSerializer::SerializeToAssetPack(AssetHandle handle, FileStreamWriter& stream, AssetSerializationInfo& outInfo) const
Expand Down
1 change: 1 addition & 0 deletions Core/Source/Lux/Core/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ namespace Lux {
else
m_Window->CenterWindow();
m_Window->SetResizable(specification.Resizable);
m_Window->Show();

if (m_Specification.EnableImGui)
{
Expand Down
5 changes: 5 additions & 0 deletions Core/Source/Lux/Core/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,11 @@ namespace Lux {
glfwSetWindowPos(m_WindowHandle, x, y);
}

void Window::Show()
{
glfwShowWindow(m_WindowHandle);
}

void Window::SetTitle(const std::string& title)
{
m_Data.Title = title;
Expand Down
1 change: 1 addition & 0 deletions Core/Source/Lux/Core/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ namespace Lux {

virtual void Maximize();
virtual void CenterWindow();
virtual void Show();

virtual const std::string& GetTitle() const { return m_Data.Title; }
virtual void SetTitle(const std::string& title);
Expand Down
71 changes: 71 additions & 0 deletions Core/Source/Lux/Project/Project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@

namespace Lux
{
const char* RuntimeExportTargetToString(RuntimeExportTarget target)
{
switch (target)
{
case RuntimeExportTarget::Debug: return "Debug";
case RuntimeExportTarget::Release: return "Release";
case RuntimeExportTarget::Dist: return "Dist";
}

return "Release";
}

RuntimeExportTarget RuntimeExportTargetFromString(std::string_view value)
{
if (value == "Debug" || value == "0")
return RuntimeExportTarget::Debug;
if (value == "Dist" || value == "2")
return RuntimeExportTarget::Dist;
return RuntimeExportTarget::Release;
}

std::filesystem::path Project::GetAssetAbsolutePath(const std::filesystem::path& path) const
{
return GetAssetDirectory() / path;
Expand Down Expand Up @@ -50,6 +71,15 @@ namespace Lux
s_ActiveProject->m_Config.StartScene = startSceneMetadata.FilePath.generic_string();
}

if (!s_ActiveProject->m_Config.RuntimeExport.IconPath.empty())
s_ActiveProject->m_Config.RuntimeExport.IconHandle = GetEditorAssetManager()->GetAssetHandleFromFilePath(s_ActiveProject->m_Config.RuntimeExport.IconPath);
else if (s_ActiveProject->m_Config.RuntimeExport.IconHandle)
{
AssetMetadata iconMetadata = GetEditorAssetManager()->GetMetadata(s_ActiveProject->m_Config.RuntimeExport.IconHandle);
if (iconMetadata.IsValid())
s_ActiveProject->m_Config.RuntimeExport.IconPath = iconMetadata.FilePath.generic_string();
}

if (!AudioEngine::HasInitializedEngine())
{
AudioEngine::Init();
Expand All @@ -66,13 +96,31 @@ namespace Lux
}

s_ActiveProject = project;
if (AudioEngine::HasInitializedEngine())
{
AudioEngine::Shutdown();
AudioEngine::SetInitalizedEngine(false);
}

if (!s_ActiveProject)
return;
Comment on lines 105 to 106
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not skip audio shutdown when clearing the active runtime project.

Line 70 returns on nullptr and bypasses the cleanup block at Line 80, so Project::SetActiveRuntime(nullptr, nullptr) can leave the audio engine initialized.

🛠️ Suggested fix
 		s_ActiveProject = project;
-		if (!s_ActiveProject)
-			return;
-
-		if (s_ActiveProject->m_ProjectDirectory.empty())
-			s_ActiveProject->m_ProjectDirectory = s_ActiveProject->m_Config.ProjectDirectory;
-		if (s_ActiveProject->m_ProjectFilePath.empty() && !s_ActiveProject->m_ProjectDirectory.empty())
-			s_ActiveProject->m_ProjectFilePath = s_ActiveProject->m_ProjectDirectory / s_ActiveProject->m_Config.ProjectFileName;
-
-		s_ActiveProject->m_Config.ProjectDirectory = s_ActiveProject->m_ProjectDirectory;
-		s_ActiveProject->m_Config.ProjectFileName = s_ActiveProject->m_ProjectFilePath.filename().string();
-
 		if (AudioEngine::HasInitializedEngine())
 		{
 			AudioEngine::Shutdown();
 			AudioEngine::SetInitalizedEngine(false);
 		}
+
+		if (!s_ActiveProject)
+			return;
+
+		if (s_ActiveProject->m_ProjectDirectory.empty())
+			s_ActiveProject->m_ProjectDirectory = s_ActiveProject->m_Config.ProjectDirectory;
+		if (s_ActiveProject->m_ProjectFilePath.empty() && !s_ActiveProject->m_ProjectDirectory.empty())
+			s_ActiveProject->m_ProjectFilePath = s_ActiveProject->m_ProjectDirectory / s_ActiveProject->m_Config.ProjectFileName;
+
+		s_ActiveProject->m_Config.ProjectDirectory = s_ActiveProject->m_ProjectDirectory;
+		s_ActiveProject->m_Config.ProjectFileName = s_ActiveProject->m_ProjectFilePath.filename().string();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!s_ActiveProject)
return;
s_ActiveProject = project;
if (AudioEngine::HasInitializedEngine())
{
AudioEngine::Shutdown();
AudioEngine::SetInitalizedEngine(false);
}
if (!s_ActiveProject)
return;
if (s_ActiveProject->m_ProjectDirectory.empty())
s_ActiveProject->m_ProjectDirectory = s_ActiveProject->m_Config.ProjectDirectory;
if (s_ActiveProject->m_ProjectFilePath.empty() && !s_ActiveProject->m_ProjectDirectory.empty())
s_ActiveProject->m_ProjectFilePath = s_ActiveProject->m_ProjectDirectory / s_ActiveProject->m_Config.ProjectFileName;
s_ActiveProject->m_Config.ProjectDirectory = s_ActiveProject->m_ProjectDirectory;
s_ActiveProject->m_Config.ProjectFileName = s_ActiveProject->m_ProjectFilePath.filename().string();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Core/Source/Lux/Project/Project.cpp` around lines 69 - 70, The early return
when s_ActiveProject is null in Project::SetActiveRuntime skips the cleanup that
should uninitialize the audio engine; remove the early return (or move the
cleanup block above it) so the audio shutdown logic always runs when
SetActiveRuntime(nullptr, nullptr) is called, and explicitly invoke the audio
cleanup/uninitialize routine used by the project (the audio shutdown function
your project normally calls during project teardown) as part of the same cleanup
sequence to ensure the audio engine is always shut down even if s_ActiveProject
is nullptr.


if (s_ActiveProject->m_ProjectDirectory.empty())
s_ActiveProject->m_ProjectDirectory = s_ActiveProject->m_Config.ProjectDirectory;
if (s_ActiveProject->m_ProjectFilePath.empty() && !s_ActiveProject->m_ProjectDirectory.empty())
s_ActiveProject->m_ProjectFilePath = s_ActiveProject->m_ProjectDirectory / s_ActiveProject->m_Config.ProjectFileName;

s_ActiveProject->m_Config.ProjectDirectory = s_ActiveProject->m_ProjectDirectory;
s_ActiveProject->m_Config.ProjectFileName = s_ActiveProject->m_ProjectFilePath.filename().string();

s_AssetManager = Ref<RuntimeAssetManager>::Create();
GetRuntimeAssetManager()->SetAssetPack(assetPack);

if (!AudioEngine::HasInitializedEngine())
{
AudioEngine::Init();
AudioEngine::SetInitalizedEngine(true);
}
}

Ref<Project> Project::New()
Expand All @@ -83,6 +131,23 @@ namespace Lux
return s_ActiveProject;
}

Ref<Project> Project::LoadRuntime(const std::filesystem::path& path, Ref<AssetPack> assetPack)
{
Ref<Project> project = Ref<Project>::Create();

ProjectSerializer serializer(project);
if (!serializer.DeserializeRuntime(path))
return nullptr;

project->m_ProjectFilePath = path.lexically_normal();
project->m_ProjectDirectory = path.parent_path();
project->m_Config.ProjectDirectory = project->m_ProjectDirectory;
project->m_Config.ProjectFileName = project->m_ProjectFilePath.filename().string();
Comment on lines +142 to +145

SetActiveRuntime(project, assetPack);
return s_ActiveProject;
}

Ref<Project> Project::Load(const std::filesystem::path& path)
{
Ref<Project> project = Ref<Project>::Create();
Expand All @@ -106,6 +171,9 @@ namespace Lux
if (!s_ActiveProject->m_Config.StartScene.empty() && GetEditorAssetManager())
s_ActiveProject->m_Config.StartSceneHandle = GetEditorAssetManager()->GetAssetHandleFromFilePath(s_ActiveProject->m_Config.StartScene);

if (!s_ActiveProject->m_Config.RuntimeExport.IconPath.empty() && GetEditorAssetManager())
s_ActiveProject->m_Config.RuntimeExport.IconHandle = GetEditorAssetManager()->GetAssetHandleFromFilePath(s_ActiveProject->m_Config.RuntimeExport.IconPath);

if (s_ActiveProject->m_Config.DefaultNamespace.empty())
s_ActiveProject->m_Config.DefaultNamespace = s_ActiveProject->m_Config.Name;

Expand All @@ -132,5 +200,8 @@ namespace Lux

if (m_Config.ScriptModulePath.empty())
m_Config.ScriptModulePath = std::filesystem::path("Scripts/Binaries") / (m_Config.Name + ".dll");

if (m_Config.RuntimeExport.GameName.empty())
m_Config.RuntimeExport.GameName = m_Config.Name;
}
}
24 changes: 24 additions & 0 deletions Core/Source/Lux/Project/Project.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ namespace Lux
CaptureToFile = 1
};

enum class RuntimeExportTarget : uint8_t
{
Debug = 0,
Release = 1,
Dist = 2
};

const char* RuntimeExportTargetToString(RuntimeExportTarget target);
RuntimeExportTarget RuntimeExportTargetFromString(std::string_view value);

struct ProjectAudioSettings
{
double FileStreamingDurationThreshold = 1.0;
Expand Down Expand Up @@ -110,6 +120,18 @@ namespace Lux
float SSRDepthTolerance = 0.8f;
};

struct ProjectRuntimeExportSettings
{
std::string GameName;
uint32_t WindowWidth = 1920;
uint32_t WindowHeight = 1080;
bool Fullscreen = false;
bool VSync = true;
std::filesystem::path IconPath;
AssetHandle IconHandle = 0;
RuntimeExportTarget TargetConfig = RuntimeExportTarget::Release;
};

struct ProjectConfig
{
std::string Name = "Untitled";
Expand Down Expand Up @@ -137,6 +159,7 @@ namespace Lux
ProjectAudioSettings Audio;
ProjectPhysicsSettings Physics;
ProjectSceneRendererSettings SceneRenderer;
ProjectRuntimeExportSettings RuntimeExport;
};

class Project : public RefCounted
Expand Down Expand Up @@ -291,6 +314,7 @@ namespace Lux

static Ref<Project> New();
static Ref<Project> Load(const std::filesystem::path& path);
static Ref<Project> LoadRuntime(const std::filesystem::path& path, Ref<AssetPack> assetPack);
static bool SaveActive(const std::filesystem::path& path);

private:
Expand Down
36 changes: 36 additions & 0 deletions Core/Source/Lux/Project/ProjectSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,19 @@ namespace Lux
out << YAML::Key << "AutoSave" << YAML::Value << config.EnableAutoSave;
out << YAML::Key << "AutoSaveInterval" << YAML::Value << config.AutoSaveIntervalSeconds;
out << YAML::Key << "RenderingTechnique" << YAML::Value << RenderingTechniqueToString(config.RendererTechnique);
out << YAML::Key << "RuntimeExport" << YAML::Value;
{
out << YAML::BeginMap;
out << YAML::Key << "GameName" << YAML::Value << config.RuntimeExport.GameName;
out << YAML::Key << "WindowWidth" << YAML::Value << config.RuntimeExport.WindowWidth;
out << YAML::Key << "WindowHeight" << YAML::Value << config.RuntimeExport.WindowHeight;
out << YAML::Key << "Fullscreen" << YAML::Value << config.RuntimeExport.Fullscreen;
out << YAML::Key << "VSync" << YAML::Value << config.RuntimeExport.VSync;
out << YAML::Key << "IconPath" << YAML::Value << config.RuntimeExport.IconPath.generic_string();
out << YAML::Key << "IconHandle" << YAML::Value << (uint64_t)config.RuntimeExport.IconHandle;
out << YAML::Key << "TargetConfig" << YAML::Value << RuntimeExportTargetToString(config.RuntimeExport.TargetConfig);
out << YAML::EndMap;
}
SerializeSceneRendererSettings(out, config.SceneRenderer);

out << YAML::Key << "Audio" << YAML::Value;
Expand Down Expand Up @@ -769,6 +782,29 @@ namespace Lux
else
config.RendererTechnique = renderingTechniqueNode.as<int>((int)RenderingTechnique::Forward) == (int)RenderingTechnique::Deferred ? RenderingTechnique::Deferred : RenderingTechnique::Forward;
}
config.RuntimeExport = {};
if (auto runtimeExportNode = projectNode["RuntimeExport"])
{
config.RuntimeExport.GameName = runtimeExportNode["GameName"].as<std::string>(config.Name);
config.RuntimeExport.WindowWidth = runtimeExportNode["WindowWidth"].as<uint32_t>(config.RuntimeExport.WindowWidth);
config.RuntimeExport.WindowHeight = runtimeExportNode["WindowHeight"].as<uint32_t>(config.RuntimeExport.WindowHeight);
config.RuntimeExport.Fullscreen = runtimeExportNode["Fullscreen"].as<bool>(config.RuntimeExport.Fullscreen);
config.RuntimeExport.VSync = runtimeExportNode["VSync"].as<bool>(config.RuntimeExport.VSync);
config.RuntimeExport.IconPath = runtimeExportNode["IconPath"].as<std::string>(config.RuntimeExport.IconPath.generic_string());
config.RuntimeExport.IconHandle = runtimeExportNode["IconHandle"].as<uint64_t>((uint64_t)config.RuntimeExport.IconHandle);

if (auto targetConfigNode = runtimeExportNode["TargetConfig"])
{
if (targetConfigNode.IsScalar() && !IsNumericString(targetConfigNode.Scalar()))
config.RuntimeExport.TargetConfig = RuntimeExportTargetFromString(targetConfigNode.as<std::string>());
else
config.RuntimeExport.TargetConfig = RuntimeExportTargetFromString(std::to_string(targetConfigNode.as<int>((int)RuntimeExportTarget::Release)));
}
}
else
{
config.RuntimeExport.GameName = config.Name;
}
DeserializeSceneRendererSettings(projectNode["SceneRenderer"], config.SceneRenderer);
config.StartScene.clear();
config.StartSceneHandle = 0;
Expand Down
Loading
Loading