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 ()
0 commit comments