Skip to content

Commit cdedbf4

Browse files
committed
WIP: File > Export Runtime... now opens a dedicated Export Runtime window instead of immediately starting export.
It includes: Game Name Window Width / Height Fullscreen VSync Icon Target Config Export Preflight Build Runtime Build Scripts Export / Cancel buttons
1 parent ef90e24 commit cdedbf4

2 files changed

Lines changed: 208 additions & 16 deletions

File tree

Editor/Source/EditorLayer.cpp

Lines changed: 202 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <atomic>
4545
#include <cctype>
4646
#include <cmath>
47+
#include <cstring>
4748
#include <fstream>
4849
#include <functional>
4950
#include <limits>
@@ -139,6 +140,11 @@ namespace Lux {
139140
constexpr const char* s_RuntimeProjectFile = "Project.luxruntime";
140141
constexpr const char* s_RuntimeAssetPackFile = "AssetPack.lap";
141142
constexpr const char* s_RuntimeShaderPackFile = "ShaderPack.lsp";
143+
constexpr RuntimeExportTarget s_RuntimeExportTargets[] = {
144+
RuntimeExportTarget::Debug,
145+
RuntimeExportTarget::Release,
146+
RuntimeExportTarget::Dist
147+
};
142148

143149
std::string SanitizeBuildName(std::string value)
144150
{
@@ -526,12 +532,18 @@ namespace Lux {
526532
if (!metadata.IsValid())
527533
return false;
528534

529-
Ref<Scene> scene = Ref<Scene>::Create();
530-
SceneSerializer serializer(scene);
531-
if (!serializer.Deserialize(project->GetAssetDirectory() / metadata.FilePath))
535+
std::ifstream sceneFile(project->GetAssetDirectory() / metadata.FilePath);
536+
if (!sceneFile.is_open())
532537
return false;
533538

534-
return scene->HasScripts();
539+
std::string line;
540+
while (std::getline(sceneFile, line))
541+
{
542+
if (line.find("ScriptComponent") != std::string::npos)
543+
return true;
544+
}
545+
546+
return false;
535547
}
536548

537549
std::filesystem::path ResolveRuntimeIconSource(const ProjectRuntimeExportSettings& settings)
@@ -944,6 +956,7 @@ namespace Lux {
944956
style.WindowMinSize.x = minWinSizeX;
945957

946958
m_PanelManager->OnImGuiRender();
959+
RenderRuntimeExportWindow();
947960

948961
if (m_ShowImGuiMetrics)
949962
ImGui::ShowMetricsWindow(&m_ShowImGuiMetrics);
@@ -2175,15 +2188,187 @@ namespace Lux {
21752188
AddRecentProject(projectFilePath);
21762189
}
21772190

2178-
void EditorLayer::ExportRuntime()
2191+
void EditorLayer::SyncRuntimeExportWindowFromProject()
21792192
{
2193+
Ref<Project> project = Project::GetActive();
2194+
if (!project)
2195+
return;
2196+
2197+
auto& runtime = project->GetConfig().RuntimeExport;
2198+
const std::string gameName = runtime.GameName.empty() ? project->GetConfig().Name : runtime.GameName;
2199+
std::memset(m_RuntimeExportGameNameBuffer, 0, sizeof(m_RuntimeExportGameNameBuffer));
2200+
std::memcpy(m_RuntimeExportGameNameBuffer, gameName.data(), std::min(gameName.size(), sizeof(m_RuntimeExportGameNameBuffer) - 1));
2201+
m_RuntimeExportIcon = runtime.IconHandle;
2202+
}
2203+
2204+
void EditorLayer::RenderRuntimeExportWindow()
2205+
{
2206+
if (!m_ShowRuntimeExportWindow)
2207+
return;
2208+
21802209
Ref<Project> project = Project::GetActive();
21812210
if (!project)
2211+
{
2212+
m_ShowRuntimeExportWindow = false;
2213+
return;
2214+
}
2215+
2216+
ImGui::SetNextWindowSize(ImVec2(560.0f, 0.0f), ImGuiCond_Appearing);
2217+
if (!ImGui::Begin("Export Runtime", &m_ShowRuntimeExportWindow, ImGuiWindowFlags_NoCollapse))
2218+
{
2219+
ImGui::End();
2220+
return;
2221+
}
2222+
2223+
auto& config = project->GetConfig();
2224+
auto& runtime = config.RuntimeExport;
2225+
2226+
auto syncRuntimeIconPath = [&runtime](AssetHandle iconHandle)
2227+
{
2228+
runtime.IconHandle = iconHandle;
2229+
runtime.IconPath.clear();
2230+
2231+
if (!iconHandle)
2232+
return;
2233+
2234+
if (Ref<EditorAssetManager> editorAssetManager = Project::GetEditorAssetManager())
2235+
{
2236+
const AssetMetadata metadata = editorAssetManager->GetMetadata(iconHandle);
2237+
if (metadata.IsValid())
2238+
runtime.IconPath = metadata.FilePath.generic_string();
2239+
}
2240+
};
2241+
2242+
ImGuiEx::BeginPropertyGrid();
2243+
if (ImGuiEx::Property("Game Name", m_RuntimeExportGameNameBuffer, sizeof(m_RuntimeExportGameNameBuffer)))
2244+
runtime.GameName = m_RuntimeExportGameNameBuffer;
2245+
2246+
int32_t width = (int32_t)runtime.WindowWidth;
2247+
if (ImGuiEx::Property("Window Width", width, 320, 16384))
2248+
runtime.WindowWidth = (uint32_t)std::max(width, 320);
2249+
2250+
int32_t height = (int32_t)runtime.WindowHeight;
2251+
if (ImGuiEx::Property("Window Height", height, 240, 16384))
2252+
runtime.WindowHeight = (uint32_t)std::max(height, 240);
2253+
2254+
ImGuiEx::Property("Fullscreen", runtime.Fullscreen);
2255+
ImGuiEx::Property("VSync", runtime.VSync);
2256+
2257+
AssetHandle icon = runtime.IconHandle;
2258+
ImGuiEx::PropertyAssetReferenceSettings iconSettings;
2259+
iconSettings.ShowFullFilePath = true;
2260+
if (ImGuiEx::PropertyAssetReference<Texture2D>("Icon", icon, "Optional PNG/JPG window icon copied beside exported runtime resources.", nullptr, iconSettings))
2261+
{
2262+
m_RuntimeExportIcon = icon;
2263+
syncRuntimeIconPath(icon);
2264+
}
2265+
ImGuiEx::EndPropertyGrid();
2266+
2267+
if (ImGui::BeginCombo("Target Config", RuntimeExportTargetToString(runtime.TargetConfig)))
2268+
{
2269+
for (RuntimeExportTarget target : s_RuntimeExportTargets)
2270+
{
2271+
const bool selected = runtime.TargetConfig == target;
2272+
if (ImGui::Selectable(RuntimeExportTargetToString(target), selected))
2273+
runtime.TargetConfig = target;
2274+
if (selected)
2275+
ImGui::SetItemDefaultFocus();
2276+
}
2277+
ImGui::EndCombo();
2278+
}
2279+
2280+
ImGui::Spacing();
2281+
ImGui::TextUnformatted("Export Preflight");
2282+
ImGui::Separator();
2283+
2284+
auto drawStatus = [](const char* label, bool ok, const std::string& okText, const char* failText)
2285+
{
2286+
ImGui::TextColored(ok ? ImVec4(0.35f, 0.85f, 0.45f, 1.0f) : ImVec4(0.95f, 0.55f, 0.35f, 1.0f),
2287+
"%s: %s", label, ok ? okText.c_str() : failText);
2288+
};
2289+
2290+
std::error_code ec;
2291+
std::filesystem::path repositoryRoot = FindRepositoryRootFrom(project->GetProjectDirectory());
2292+
if (repositoryRoot.empty())
2293+
repositoryRoot = FindRepositoryRootFrom(std::filesystem::current_path());
2294+
2295+
const std::filesystem::path current = std::filesystem::current_path(ec);
2296+
const std::filesystem::path runtimeExe = GetRuntimeExecutablePath(runtime.TargetConfig);
2297+
const std::filesystem::path assetPack = Project::GetActiveAssetDirectory() / s_RuntimeAssetPackFile;
2298+
const std::filesystem::path resources = FindFirstExistingDirectory({
2299+
current / "Resources",
2300+
current / ".." / "Editor" / "Resources",
2301+
repositoryRoot / "Editor" / "Resources",
2302+
std::filesystem::path("Editor") / "Resources"
2303+
});
2304+
const std::filesystem::path mono = FindFirstExistingDirectory({
2305+
current / "mono",
2306+
current / ".." / "Editor" / "mono",
2307+
repositoryRoot / "Editor" / "mono",
2308+
std::filesystem::path("Editor") / "mono"
2309+
});
2310+
const std::filesystem::path scriptModule = Project::GetActiveScriptModuleFilePath();
2311+
const std::filesystem::path scriptProject = ResolveScriptProjectFile(project);
2312+
const bool startupSceneUsesScripts = StartupSceneUsesScripts(project);
2313+
const bool scriptModuleExists = config.ScriptModulePath.empty() || FileExists(scriptModule);
2314+
const bool scriptModuleStale = !config.ScriptModulePath.empty() && scriptModuleExists && IsScriptModuleOutdated(scriptModule, scriptProject);
2315+
2316+
drawStatus("Startup Scene", config.StartSceneHandle != 0, "set", "missing");
2317+
drawStatus("Startup Scene Uses Scripts", true, startupSceneUsesScripts ? "yes" : "no", "");
2318+
drawStatus("Lux-Runtime.exe", !runtimeExe.empty(), runtimeExe.string(), "missing");
2319+
drawStatus("AssetPack.lap", std::filesystem::exists(assetPack, ec), "created", "will be created during export");
2320+
drawStatus("Resources", !resources.empty(), resources.string(), "missing");
2321+
if (config.ScriptModulePath.empty())
2322+
ImGui::TextColored(ImVec4(0.35f, 0.85f, 0.45f, 1.0f), "Script Module: optional");
2323+
else if (!scriptModuleExists)
2324+
ImGui::TextColored(ImVec4(0.95f, 0.55f, 0.35f, 1.0f), "Script Module: missing");
2325+
else
2326+
ImGui::TextColored(scriptModuleStale ? ImVec4(0.95f, 0.75f, 0.35f, 1.0f) : ImVec4(0.35f, 0.85f, 0.45f, 1.0f),
2327+
"Script Module: %s", scriptModuleStale ? "stale" : "found");
2328+
drawStatus("mono", !mono.empty(), mono.string(), "missing");
2329+
2330+
ImGui::Spacing();
2331+
if (ImGui::Button("Build Runtime"))
2332+
BuildRuntimeExecutable(runtime.TargetConfig);
2333+
2334+
ImGui::SameLine();
2335+
if (ImGui::Button("Build Scripts"))
2336+
BuildScriptModule(runtime.TargetConfig);
2337+
2338+
ImGui::Separator();
2339+
if (ImGui::Button("Export..."))
2340+
{
2341+
if (ExportRuntimeNow())
2342+
m_ShowRuntimeExportWindow = false;
2343+
}
2344+
ImGui::SameLine();
2345+
if (ImGui::Button("Cancel"))
2346+
m_ShowRuntimeExportWindow = false;
2347+
2348+
ImGui::End();
2349+
}
2350+
2351+
void EditorLayer::ExportRuntime()
2352+
{
2353+
if (!Project::GetActive())
21822354
{
21832355
LUX_CONSOLE_LOG_ERROR("No active project to export.");
21842356
return;
21852357
}
21862358

2359+
SyncRuntimeExportWindowFromProject();
2360+
m_ShowRuntimeExportWindow = true;
2361+
}
2362+
2363+
bool EditorLayer::ExportRuntimeNow()
2364+
{
2365+
Ref<Project> project = Project::GetActive();
2366+
if (!project)
2367+
{
2368+
LUX_CONSOLE_LOG_ERROR("No active project to export.");
2369+
return false;
2370+
}
2371+
21872372
if (m_SceneState != SceneState::Edit)
21882373
OnSceneStop();
21892374

@@ -2192,7 +2377,7 @@ namespace Lux {
21922377

21932378
const std::filesystem::path selectedFolder = FileSystem::OpenFolderDialog(project->GetProjectDirectory().string().c_str());
21942379
if (selectedFolder.empty())
2195-
return;
2380+
return false;
21962381

21972382
std::error_code ec;
21982383
ProjectRuntimeExportSettings runtimeSettings = project->GetConfig().RuntimeExport;
@@ -2211,7 +2396,7 @@ namespace Lux {
22112396
{
22122397
LUX_CONSOLE_LOG_INFO("Lux-Runtime ({}) is missing or older than runtime sources. Building before export...", RuntimeExportTargetToString(targetConfig));
22132398
if (!BuildRuntimeExecutable(targetConfig))
2214-
return;
2399+
return false;
22152400
runtimeExe = GetRuntimeExecutablePath(targetConfig);
22162401
}
22172402

@@ -2223,7 +2408,7 @@ namespace Lux {
22232408
{
22242409
LUX_CONSOLE_LOG_INFO("Script module is missing or stale. Building scripts before asset pack creation...");
22252410
if (!BuildScriptModule(targetConfig) && startupSceneUsesScripts)
2226-
return;
2411+
return false;
22272412
}
22282413

22292414
const std::filesystem::path current = std::filesystem::current_path(ec);
@@ -2262,13 +2447,13 @@ namespace Lux {
22622447
if (!hasStartupScene || !hasRuntimeExe || !hasResources)
22632448
{
22642449
LUX_CONSOLE_LOG_ERROR("Runtime export preflight failed. Set a Startup Scene and make sure Lux-Runtime and Resources are available.");
2265-
return;
2450+
return false;
22662451
}
22672452

22682453
if (startupSceneUsesScripts && (!hasScriptModule || scriptModuleStale))
22692454
{
22702455
LUX_CONSOLE_LOG_ERROR("Runtime export preflight failed. Startup Scene uses scripts, but the script module is {}.", hasScriptModule ? "stale" : "missing");
2271-
return;
2456+
return false;
22722457
}
22732458

22742459
if (!hasScriptModule)
@@ -2284,27 +2469,27 @@ namespace Lux {
22842469
if (ec)
22852470
{
22862471
LUX_CONSOLE_LOG_ERROR("Failed to create export directory '{}': {}", exportRoot.string(), ec.message());
2287-
return;
2472+
return false;
22882473
}
22892474

22902475
std::atomic<float> assetPackProgress = 0.0f;
22912476
Ref<AssetPack> assetPack = AssetPack::CreateFromActiveProject(assetPackProgress);
22922477
if (!assetPack)
22932478
{
22942479
LUX_CONSOLE_LOG_ERROR("Runtime export failed while building the asset pack.");
2295-
return;
2480+
return false;
22962481
}
22972482

22982483
ProjectSerializer serializer(project);
22992484
const std::filesystem::path runtimeProjectFile = exportAssets / s_RuntimeProjectFile;
23002485
if (!serializer.SerializeRuntime(runtimeProjectFile))
23012486
{
23022487
LUX_CONSOLE_LOG_ERROR("Runtime export failed while writing '{}'.", runtimeProjectFile.string());
2303-
return;
2488+
return false;
23042489
}
23052490

23062491
if (!CopyFileIfExists(Project::GetActiveAssetDirectory() / s_RuntimeAssetPackFile, exportAssets / s_RuntimeAssetPackFile, true))
2307-
return;
2492+
return false;
23082493
LUX_CONSOLE_LOG_INFO(" AssetPack.lap: created");
23092494

23102495
const std::filesystem::path exportedExe = exportRoot / (buildName + ".exe");
@@ -2331,7 +2516,7 @@ namespace Lux {
23312516
LUX_CONSOLE_LOG_WARN("Runtime export could not find an editor Resources directory to copy.");
23322517

23332518
if (!WriteRuntimeShaderPack(exportRoot / "Assets" / s_RuntimeShaderPackFile))
2334-
return;
2519+
return false;
23352520

23362521
if (!monoSource.empty())
23372522
CopyDirectoryRecursive(monoSource, exportRoot / "mono", targetConfig == RuntimeExportTarget::Dist);
@@ -2356,10 +2541,11 @@ namespace Lux {
23562541
}
23572542

23582543
if (!WriteRuntimeSettingsFile(exportAssets / "RuntimeSettings.yaml", runtimeSettings, runtimeIconPath))
2359-
return;
2544+
return false;
23602545

23612546
LUX_CONSOLE_LOG_INFO("Runtime export complete: {}", exportRoot.string());
23622547
FileSystem::OpenDirectoryInExplorer(exportRoot);
2548+
return true;
23632549
}
23642550

23652551
void EditorLayer::NewScene()

Editor/Source/EditorLayer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ namespace Lux
6060
void OpenProject(const std::filesystem::path& path);
6161
void SaveProject();
6262
void ExportRuntime();
63+
bool ExportRuntimeNow();
64+
void RenderRuntimeExportWindow();
65+
void SyncRuntimeExportWindowFromProject();
6366

6467
void NewScene();
6568
void OpenScene();
@@ -132,9 +135,12 @@ namespace Lux
132135
bool m_ShowBoundingBoxes = false;
133136
bool m_ShowEntityIcons = true;
134137
bool m_ShowViewportPerformanceHUD = true;
138+
bool m_ShowRuntimeExportWindow = false;
135139
bool m_UseGizmoSnap = false;
136140
float m_TranslationSnapValue = 0.5f;
137141
float m_RotationSnapValue = 45.0f;
142+
AssetHandle m_RuntimeExportIcon = 0;
143+
char m_RuntimeExportGameNameBuffer[256] = {};
138144

139145
enum class SceneState
140146
{

0 commit comments

Comments
 (0)