Files
Modularity/src/Engine.h

520 lines
20 KiB
C++

#pragma once
#include "Common.h"
#include "SceneObject.h"
#include "Camera.h"
#include "Rendering.h"
#include "ProjectManager.h"
#include "EditorUI.h"
#include "MeshBuilder.h"
#include "ScriptCompiler.h"
#include "ScriptRuntime.h"
#include "PhysicsSystem.h"
#include "AudioSystem.h"
#include "PackageManager.h"
#include "ManagedScriptRuntime.h"
#include "ThirdParty/ImGuiColorTextEdit/TextEditor.h"
#include "../include/Window/Window.h"
#include <unordered_map>
#include <unordered_set>
#include <atomic>
#include <deque>
#include <future>
#include <mutex>
#include <thread>
void window_size_callback(GLFWwindow* window, int width, int height);
fs::path resolveScriptsConfigPath(const Project& project);
class Engine {
private:
Window window;
GLFWwindow* editorWindow = nullptr;
Renderer renderer;
Camera camera;
ViewportController viewportController;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
bool cursorLocked = false; // true only while holding right mouse for freelook
int viewportWidth = 800;
int viewportHeight = 600;
bool gizmoHistoryCaptured = false;
// Standalone material inspection cache
std::string inspectedMaterialPath;
MaterialProperties inspectedMaterial;
std::string inspectedAlbedo;
std::string inspectedOverlay;
std::string inspectedNormal;
std::string inspectedVertShader;
std::string inspectedFragShader;
bool inspectedUseOverlay = false;
bool inspectedMaterialValid = false;
struct SceneSnapshot {
std::vector<SceneObject> objects;
std::vector<int> selectedIds;
int nextId = 0;
};
std::vector<SceneSnapshot> undoStack;
std::vector<SceneSnapshot> redoStack;
std::vector<SceneObject> sceneObjects;
int selectedObjectId = -1; // primary selection (last)
std::vector<int> selectedObjectIds; // multi-select
int nextObjectId = 0;
// Gizmo state
ImGuizmo::OPERATION mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGuizmo::MODE mCurrentGizmoMode = ImGuizmo::LOCAL;
bool useSnap = false;
float snapValue[3] = { 0.5f, 0.5f, 0.5f };
float rotationSnapValue = 15.0f;
FileBrowser fileBrowser;
bool viewportFullscreen = false;
bool showHierarchy = true;
bool showInspector = true;
bool showFileBrowser = true;
bool showConsole = true;
bool showProjectBrowser = true; // Now merged into file browser
bool showMeshBuilder = false;
bool showBuildSettings = false;
bool showStyleEditor = false;
bool showScriptingWindow = false;
bool firstFrame = true;
bool playerMode = false;
bool autoStartRequested = false;
bool autoStartPlayerMode = false;
std::string autoStartProjectPath;
std::string autoStartSceneName;
std::vector<std::string> consoleLog;
int draggedObjectId = -1;
ProjectManager projectManager;
PackageManager packageManager;
bool showLauncher = true;
bool showNewSceneDialog = false;
bool showSaveSceneAsDialog = false;
char newSceneName[128] = "";
char saveSceneAsName[128] = "";
struct ScriptEditorWindowEntry {
fs::path binaryPath;
std::string label;
bool open = false;
};
std::vector<ScriptEditorWindowEntry> scriptEditorWindows;
bool scriptEditorWindowsDirty = true;
bool rendererInitialized = false;
bool showImportOBJDialog = false;
bool showImportModelDialog = false; // For Assimp models
std::string pendingOBJPath;
std::string pendingModelPath; // For Assimp models
char importOBJName[128] = "";
char importModelName[128] = ""; // For Assimp models
char fileBrowserSearch[256] = "";
float fileBrowserIconScale = 1.0f; // 0.5 to 2.0 range
float fileBrowserSidebarWidth = 220.0f;
bool showFileBrowserSidebar = true;
std::vector<fs::path> fileBrowserFavorites;
std::string uiStylePresetName = "Current";
enum class UIAnimationMode {
Off = 0,
Snappy = 1,
Fluid = 2
};
enum class WorkspaceMode {
Default = 0,
Animation = 1,
Scripting = 2
};
UIAnimationMode uiAnimationMode = UIAnimationMode::Off;
WorkspaceMode currentWorkspace = WorkspaceMode::Default;
bool workspaceLayoutDirty = false;
bool pendingWorkspaceReload = false;
fs::path pendingWorkspaceIniPath;
bool editorSettingsDirty = false;
bool showEnvironmentWindow = true;
bool showCameraWindow = true;
bool showAnimationWindow = false;
int animationTargetId = -1;
int animationSelectedKey = -1;
float animationCurrentTime = 0.0f;
bool animationIsPlaying = false;
float animationLastAppliedTime = -1.0f;
bool hierarchyShowTexturePreview = false;
bool hierarchyPreviewNearest = false;
std::unordered_map<std::string, bool> texturePreviewFilterOverrides;
bool audioPreviewLoop = false;
bool audioPreviewAutoPlay = false;
std::string audioPreviewSelectedPath;
bool isPlaying = false;
bool isPaused = false;
bool showViewOutput = true;
bool showSceneGizmos = true;
bool showGameViewport = true;
int previewCameraId = -1;
bool gameViewCursorLocked = false;
bool gameViewportFocused = false;
bool showGameProfiler = true;
bool showCanvasOverlay = false;
bool showUIWorldGrid = true;
bool showSceneGrid3D = false;
int gameViewportResolutionIndex = 0;
int gameViewportCustomWidth = 1920;
int gameViewportCustomHeight = 1080;
float gameViewportZoom = 1.0f;
bool gameViewportAutoFit = true;
int activePlayerId = -1;
MeshBuilder meshBuilder;
char meshBuilderPath[260] = "";
char meshBuilderFaceInput[128] = "";
bool meshEditMode = false;
bool meshEditLoaded = false;
bool meshEditDirty = false;
bool meshEditExtrudeMode = false;
std::string meshEditPath;
RawMeshAsset meshEditAsset;
std::vector<int> meshEditSelectedVertices;
std::vector<int> meshEditSelectedEdges; // indices into generated edge list
std::vector<int> meshEditSelectedFaces; // indices into mesh faces
struct UIAnimationState {
float hover = 0.0f;
float active = 0.0f;
float sliderValue = 0.0f;
bool initialized = false;
};
std::unordered_map<int, UIAnimationState> uiAnimationStates;
struct UIWorldCamera2D {
glm::vec2 position = glm::vec2(0.0f);
float zoom = 100.0f; // pixels per world unit
glm::vec2 viewportSize = glm::vec2(0.0f);
glm::vec2 WorldToScreen(const glm::vec2& world) const {
return glm::vec2(
(world.x - position.x) * zoom + viewportSize.x * 0.5f,
(position.y - world.y) * zoom + viewportSize.y * 0.5f
);
}
glm::vec2 ScreenToWorld(const glm::vec2& screen) const {
return glm::vec2(
(screen.x - viewportSize.x * 0.5f) / zoom + position.x,
position.y - (screen.y - viewportSize.y * 0.5f) / zoom
);
}
};
bool uiWorldMode = false;
bool uiWorldPanning = false;
UIWorldCamera2D uiWorldCamera;
bool consoleWrapText = true;
enum class MeshEditSelectionMode { Vertex = 0, Edge = 1, Face = 2 };
MeshEditSelectionMode meshEditSelectionMode = MeshEditSelectionMode::Vertex;
ScriptCompiler scriptCompiler;
ScriptRuntime scriptRuntime;
ManagedScriptRuntime managedRuntime;
PhysicsSystem physics;
AudioSystem audio;
bool showCompilePopup = false;
bool compilePopupOpened = false;
double compilePopupHideTime = 0.0;
bool lastCompileSuccess = false;
std::string lastCompileStatus;
std::string lastCompileLog;
float compileProgress = 0.0f;
std::string compileStage;
enum class BuildPlatform {
Windows = 0,
Linux = 1,
Android = 2
};
struct BuildSceneEntry {
std::string name;
bool enabled = true;
};
struct BuildSettings {
BuildPlatform platform = BuildPlatform::Windows;
std::string architecture = "x86_64";
bool developmentBuild = false;
bool autoConnectProfiler = false;
bool scriptDebugging = false;
bool deepProfiling = false;
bool scriptsOnlyBuild = false;
bool serverBuild = false;
std::string compressionMethod = "Default";
std::vector<BuildSceneEntry> scenes;
};
BuildSettings buildSettings;
int buildSettingsSelectedIndex = -1;
bool buildSettingsDirty = false;
struct ExportJobResult {
bool success = false;
std::string message;
fs::path outputDir;
};
struct ExportJobState {
bool active = false;
bool done = false;
bool success = false;
bool cancelled = false;
float progress = 0.0f;
std::string status;
std::string log;
fs::path outputDir;
bool runAfter = false;
std::future<ExportJobResult> future;
};
ExportJobState exportJob;
std::atomic<bool> exportCancelRequested = false;
std::mutex exportMutex;
bool showExportDialog = false;
bool exportRunAfter = false;
char exportOutputPath[512] = "";
struct ScriptCompileJobResult {
bool success = false;
bool isManaged = false;
fs::path scriptPath;
fs::path binaryPath;
std::string compiledSource;
std::string compileLog;
std::string linkLog;
std::string error;
};
std::atomic<bool> compileInProgress = false;
std::atomic<bool> compileResultReady = false;
std::thread compileWorker;
std::mutex compileMutex;
ScriptCompileJobResult compileResult;
std::unordered_map<std::string, fs::file_time_type> scriptLastAutoCompileTime;
std::deque<fs::path> autoCompileQueue;
std::unordered_set<std::string> autoCompileQueued;
bool managedAutoCompileQueued = false;
double scriptAutoCompileLastCheck = 0.0;
double scriptAutoCompileInterval = 0.5;
struct ProjectLoadResult {
bool success = false;
Project project;
std::string error;
std::string path;
};
bool projectLoadInProgress = false;
double projectLoadStartTime = 0.0;
std::string projectLoadPath;
std::future<ProjectLoadResult> projectLoadFuture;
bool sceneLoadInProgress = false;
float sceneLoadProgress = 0.0f;
std::string sceneLoadStatus;
std::string sceneLoadSceneName;
std::vector<SceneObject> sceneLoadObjects;
std::vector<size_t> sceneLoadAssetIndices;
size_t sceneLoadAssetsDone = 0;
int sceneLoadNextId = 0;
int sceneLoadVersion = 9;
float sceneLoadTimeOfDay = -1.0f;
bool specMode = false;
bool testMode = false;
bool collisionWireframe = false;
bool fpsCapEnabled = false;
float fpsCap = 120.0f;
struct UIStylePreset {
std::string name;
ImGuiStyle style;
bool builtin = false;
};
std::vector<UIStylePreset> uiStylePresets;
int uiStylePresetIndex = 0;
struct ScriptEditorState {
fs::path filePath;
std::string buffer;
bool dirty = false;
bool autoCompileOnSave = true;
bool hasWriteTime = false;
fs::file_time_type lastWriteTime;
};
ScriptEditorState scriptEditorState;
std::vector<fs::path> scriptingFileList;
std::vector<std::string> scriptingCompletions;
TextEditor scriptTextEditor;
bool scriptTextEditorReady = false;
char scriptingFilter[128] = "";
bool scriptingFilesDirty = true;
// Private methods
SceneObject* getSelectedObject();
glm::vec3 getSelectionCenterWorld(bool worldSpace) const;
void setPrimarySelection(int id, bool additive = false);
void clearSelection();
static void DecomposeMatrix(const glm::mat4& matrix, glm::vec3& pos, glm::vec3& rot, glm::vec3& scale);
static glm::mat4 ComposeTransform(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& scale);
static glm::mat4 ComposeTransform(const glm::vec3& position, const glm::vec3& rotationDeg, const glm::vec3& scale);
void updateHierarchyWorldTransforms();
void updateLocalFromWorld(SceneObject& obj, const glm::vec3& parentPos, const glm::quat& parentRot, const glm::vec3& parentScale);
void initializeLocalTransformsFromWorld(int sceneVersion);
void importOBJToScene(const std::string& filepath, const std::string& objectName);
void importModelToScene(const std::string& filepath, const std::string& objectName); // Assimp import
void convertModelToRawMesh(const std::string& filepath);
void createRMeshPrimitive(const std::string& primitiveName);
bool ensureMeshEditTarget(SceneObject* obj);
bool syncMeshEditToGPU(SceneObject* obj);
bool saveMeshEditAsset(std::string& error);
void handleKeyboardShortcuts();
void OpenProjectPath(const std::string& path);
// UI rendering methods
void renderLauncher();
void renderNewProjectDialog();
void renderOpenProjectDialog();
void renderMainMenuBar();
void renderPlayControlsBar();
void renderEnvironmentWindow();
void renderCameraWindow();
void renderAnimationWindow();
void renderHierarchyPanel();
void renderObjectNode(SceneObject& obj, const std::string& filter,
std::vector<bool>& ancestorHasNext, bool isLast, int depth);
void renderFileBrowserPanel();
void renderMeshBuilderPanel();
void renderInspectorPanel();
void renderConsolePanel();
void renderViewport();
void renderPlayerViewport();
void renderGameViewportWindow();
void renderBuildSettingsWindow();
void renderScriptingWindow();
void renderDialogs();
void updateCompileJob();
void renderProjectBrowserPanel();
void renderScriptEditorWindows();
void refreshScriptEditorWindows();
void refreshScriptingFileList();
Camera makeCameraFromObject(const SceneObject& obj) const;
void compileScriptFile(const fs::path& scriptPath);
void updateAutoCompileScripts();
void processAutoCompileQueue();
void queueAutoCompile(const fs::path& scriptPath, const fs::file_time_type& sourceTime);
void startProjectLoad(const std::string& path);
void pollProjectLoad();
void finishProjectLoad(ProjectLoadResult& result);
void beginDeferredSceneLoad(const std::string& sceneName);
void pollSceneLoad();
void finalizeDeferredSceneLoad();
void syncPlayerCamera();
void updateScripts(float delta);
void updatePlayerController(float delta);
void updateRigidbody2D(float delta);
void updateCameraFollow2D(float delta);
void updateSkeletalAnimations(float delta);
void updateSkinningMatrices();
void rebuildSkeletalBindings();
void initUIStylePresets();
int findUIStylePreset(const std::string& name) const;
const UIStylePreset* getUIStylePreset(const std::string& name) const;
void registerUIStylePreset(const std::string& name, const ImGuiStyle& style, bool replace);
bool applyUIStylePresetByName(const std::string& name);
void applyWorkspacePreset(WorkspaceMode mode, bool rebuildLayout);
void buildWorkspaceLayout(WorkspaceMode mode);
fs::path getEditorUserSettingsPath() const;
fs::path getEditorLayoutPath() const;
fs::path getWorkspaceLayoutPath(WorkspaceMode mode) const;
void loadEditorUserSettings();
void saveEditorUserSettings() const;
void exportEditorThemeLayout();
void resetBuildSettings();
void loadBuildSettings();
void saveBuildSettings();
bool addSceneToBuildSettings(const std::string& sceneName, bool enabled);
void loadAutoStartConfig();
void applyAutoStartMode();
void startExportBuild(const fs::path& outputDir, bool runAfter);
void pollExportBuild();
void renderFileBrowserToolbar();
void renderFileBrowserBreadcrumb();
void renderFileBrowserGridView();
void renderFileBrowserListView();
void renderFileContextMenu(const fs::directory_entry& entry);
void handleFileDoubleClick(const fs::directory_entry& entry);
void openScriptInEditor(const fs::path& path);
ImVec4 getFileCategoryColor(FileCategory category) const;
const char* getFileCategoryIconText(FileCategory category) const;
// Project/scene management
void createNewProject(const char* name, const char* location);
void loadRecentScenes();
void saveCurrentScene();
void loadScene(const std::string& sceneName);
void createNewScene(const std::string& sceneName);
// Scene object management
void addObject(ObjectType type, const std::string& baseName);
void duplicateSelected();
void deleteSelected();
void setParent(int childId, int parentId);
void loadMaterialFromFile(SceneObject& obj);
void saveMaterialToFile(const SceneObject& obj);
void recordState(const char* reason = "");
void undo();
void redo();
// Console/logging
void addConsoleMessage(const std::string& message, ConsoleMessageType type);
void logToConsole(const std::string& message);
// Material helpers
bool loadMaterialData(const std::string& path, MaterialProperties& props,
std::string& albedo, std::string& overlay,
std::string& normal, bool& useOverlay,
std::string* vertexShaderOut = nullptr,
std::string* fragmentShaderOut = nullptr);
bool saveMaterialData(const std::string& path, const MaterialProperties& props,
const std::string& albedo, const std::string& overlay,
const std::string& normal, bool useOverlay,
const std::string& vertexShader,
const std::string& fragmentShader);
// ImGui setup
void setupImGui();
bool initRenderer();
public:
Engine() = default;
bool init();
void run();
void shutdown();
SceneObject* findObjectByName(const std::string& name);
SceneObject* findObjectById(int id);
fs::path resolveScriptBinary(const fs::path& sourcePath);
fs::path resolveManagedAssembly(const fs::path& sourcePath);
fs::path getManagedProjectPath() const;
fs::path getManagedOutputDll() const;
void compileManagedScripts();
void markProjectDirty();
// Script-accessible logging wrapper
void addConsoleMessageFromScript(const std::string& message, ConsoleMessageType type);
// Script-accessible physics helpers
bool setRigidbodyVelocityFromScript(int id, const glm::vec3& velocity);
bool getRigidbodyVelocityFromScript(int id, glm::vec3& outVelocity);
bool setRigidbodyAngularVelocityFromScript(int id, const glm::vec3& velocity);
bool getRigidbodyAngularVelocityFromScript(int id, glm::vec3& outVelocity);
bool teleportPhysicsActorFromScript(int id, const glm::vec3& position, const glm::vec3& rotationDeg);
bool addRigidbodyForceFromScript(int id, const glm::vec3& force);
bool addRigidbodyImpulseFromScript(int id, const glm::vec3& impulse);
bool addRigidbodyTorqueFromScript(int id, const glm::vec3& torque);
bool addRigidbodyAngularImpulseFromScript(int id, const glm::vec3& impulse);
bool setRigidbodyYawFromScript(int id, float yawDegrees);
bool raycastClosestFromScript(const glm::vec3& origin, const glm::vec3& dir, float distance,
int ignoreId, glm::vec3* hitPos, glm::vec3* hitNormal,
float* hitDistance) const;
// Audio control exposed to scripts
bool playAudioFromScript(int id);
bool stopAudioFromScript(int id);
bool setAudioLoopFromScript(int id, bool loop);
bool setAudioVolumeFromScript(int id, float volume);
bool setAudioClipFromScript(int id, const std::string& path);
void syncLocalTransform(SceneObject& obj);
const std::vector<UIStylePreset>& getUIStylePresets() const { return uiStylePresets; }
void registerUIStylePresetFromScript(const std::string& name, const ImGuiStyle& style, bool replace = false);
void setFrameRateCapFromScript(bool enabled, float cap);
};