diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 7e1b4645..1c36954a 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1326,6 +1326,8 @@ Fix entity swap button resetting position Fix block meshgen not incrementing indices correctly Fix block meshgen algorithm iteration bug Add debug control to swap first/third person +Setup scaffolding for drag-and-drop asset handling +Editor mode asset file drag and drop diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java index 4bd0426d..0821fc75 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; import electrosphere.client.ui.menu.debug.entity.ImGuiEntityMacros; +import electrosphere.client.ui.menu.editor.ImGuiEditorWindows; import electrosphere.controls.ControlHandler.ControlsState; import electrosphere.engine.Globals; import electrosphere.renderer.ui.imgui.ImGuiLinePlot; @@ -35,8 +36,8 @@ public class ImGuiWindowMacros { * Initializes imgui windows */ public static void initImGuiWindows(){ - createMainDebugMenu(); - createFramerateGraph(); + ImGuiWindowMacros.createMainDebugMenu(); + ImGuiWindowMacros.createFramerateGraph(); ImGuiPlayerEntity.createPlayerEntityDebugWindow(); ImGuiFluidMonitor.createFluidDebugWindows(); ImGuiEntityMacros.createClientEntityWindows(); @@ -51,6 +52,7 @@ public class ImGuiWindowMacros { ImGuiNetworkMonitor.createNetworkMonitorWindows(); ImGuiGriddedManager.createGriddedManagerWindows(); ImGuiMemory.createMemoryWindows(); + ImGuiEditorWindows.initEditorWindows(); } /** @@ -60,13 +62,13 @@ public class ImGuiWindowMacros { globalFrametimeWindow = new ImGuiWindow("Frametime Graph"); globalFrametimePlot = new ImGuiLinePlot("Frametime plot"); globalFrametimeDatasets = new HashMap(); - initFramerateGraphSeries("totalframerate"); - initFramerateGraphSeries("serversim"); - initFramerateGraphSeries("clientsim"); - initFramerateGraphSeries("render"); - initFramerateGraphSeries("assetLoad"); - initFramerateGraphSeries("clientNetwork"); - initFramerateGraphSeries("controls"); + ImGuiWindowMacros.initFramerateGraphSeries("totalframerate"); + ImGuiWindowMacros.initFramerateGraphSeries("serversim"); + ImGuiWindowMacros.initFramerateGraphSeries("clientsim"); + ImGuiWindowMacros.initFramerateGraphSeries("render"); + ImGuiWindowMacros.initFramerateGraphSeries("assetLoad"); + ImGuiWindowMacros.initFramerateGraphSeries("clientNetwork"); + ImGuiWindowMacros.initFramerateGraphSeries("controls"); globalFrametimeWindow.addElement(globalFrametimePlot); globalFrametimeWindow.setOpen(false); Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(globalFrametimeWindow); diff --git a/src/main/java/electrosphere/client/ui/menu/editor/ImGuiEditorDetailsWindow.java b/src/main/java/electrosphere/client/ui/menu/editor/ImGuiEditorDetailsWindow.java new file mode 100644 index 00000000..a7ce5f22 --- /dev/null +++ b/src/main/java/electrosphere/client/ui/menu/editor/ImGuiEditorDetailsWindow.java @@ -0,0 +1,58 @@ +package electrosphere.client.ui.menu.editor; + +import electrosphere.controls.ControlHandler.ControlsState; +import electrosphere.engine.Globals; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; +import imgui.ImGui; + +/** + * Details window of the editor + */ +public class ImGuiEditorDetailsWindow { + + /** + * The window object + */ + private static ImGuiWindow detailsWindow; + + /** + * Gets the details window + * @return the details window + */ + public static ImGuiWindow getDetailsWindow(){ + return detailsWindow; + } + + /** + * Inits the details menu + */ + protected static void createDetailsMenu(){ + detailsWindow = new ImGuiWindow("Details"); + detailsWindow.setCallback(new ImGuiWindowCallback() { + @Override + public void exec() { + ImGui.text("Some details or smthn"); + //close button + if(ImGui.button("Close")){ + detailsWindow.setOpen(false); + } + } + }); + detailsWindow.setOpen(false); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(detailsWindow); + } + + /** + * Toggles the open state of the menu + */ + protected static void toggleDetailsMenus(){ + detailsWindow.setOpen(!detailsWindow.isOpen()); + if(detailsWindow.isOpen()){ + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); + } else { + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); + } + } + +} diff --git a/src/main/java/electrosphere/client/ui/menu/editor/ImGuiEditorWindows.java b/src/main/java/electrosphere/client/ui/menu/editor/ImGuiEditorWindows.java new file mode 100644 index 00000000..99cb6f78 --- /dev/null +++ b/src/main/java/electrosphere/client/ui/menu/editor/ImGuiEditorWindows.java @@ -0,0 +1,83 @@ +package electrosphere.client.ui.menu.editor; + +import electrosphere.controls.ControlHandler.ControlsState; +import electrosphere.engine.Globals; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; +import imgui.ImGui; + +/** + * Various methods for creating specific imgui windows in engine + */ +public class ImGuiEditorWindows { + + + //scene hierarchy menu + private static ImGuiWindow mainWindow; + + //tracks if the editor menu is open + private static boolean editorIsOpen = false; + + /** + * Initializes imgui windows + */ + public static void initEditorWindows(){ + ImGuiEditorWindows.createMainEditorMenu(); + ImGuiEditorDetailsWindow.createDetailsMenu(); + } + + /** + * Gets the hierarchy window + * @return the hierarchy window + */ + public static ImGuiWindow getHierarchyWindow(){ + return mainWindow; + } + + + /** + * Inits the hierarchy menus + */ + private static void createMainEditorMenu(){ + mainWindow = new ImGuiWindow("Editor"); + mainWindow.setCallback(new ImGuiWindowCallback() { + @Override + public void exec() { + ImGui.text("hello :)"); + //close button + if(ImGui.button("Close")){ + mainWindow.setOpen(false); + } + } + }); + mainWindow.setOpen(false); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(mainWindow); + } + + /** + * Toggles the open state of the menu + */ + public static void toggleEditorMenus(){ + mainWindow.setOpen(!mainWindow.isOpen()); + ImGuiEditorWindows.editorIsOpen = mainWindow.isOpen(); + if(mainWindow.isOpen()){ + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); + } else { + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); + } + + //toggle all windows + ImGuiEditorDetailsWindow.toggleDetailsMenus(); + } + + /** + * Makes sure the main editor menu properly toggles controls if it is closed with the X button + */ + public static void synchronizeMainEditorMenuVisibility(){ + if(ImGuiEditorWindows.editorIsOpen && !mainWindow.isOpen()){ + ImGuiEditorWindows.editorIsOpen = false; + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); + } + } + +} diff --git a/src/main/java/electrosphere/controls/categories/ControlCategoryInGameDebug.java b/src/main/java/electrosphere/controls/categories/ControlCategoryInGameDebug.java index 5ef329c0..b5a62881 100644 --- a/src/main/java/electrosphere/controls/categories/ControlCategoryInGameDebug.java +++ b/src/main/java/electrosphere/controls/categories/ControlCategoryInGameDebug.java @@ -5,6 +5,7 @@ import java.util.List; import org.lwjgl.glfw.GLFW; +import electrosphere.client.ui.menu.editor.ImGuiEditorWindows; import electrosphere.controls.Control; import electrosphere.controls.Control.ControlMethod; import electrosphere.controls.Control.ControlType; @@ -23,6 +24,7 @@ public class ControlCategoryInGameDebug { public static final String DEBUG_FRAMESTEP = "framestep"; public static final String DEBUG_SWAP_EDITOR_MODE = "swapEditorMode"; public static final String DEBUG_SWITCH_FIRST_THIRD = "switchFirstThird"; + public static final String DEBUG_SWAP_GAME_EDITOR = "swapGameEditor"; /** * Maps the controls @@ -30,8 +32,9 @@ public class ControlCategoryInGameDebug { */ public static void mapControls(ControlHandler handler){ handler.addControl(DEBUG_FRAMESTEP, new Control(ControlType.KEY, GLFW.GLFW_KEY_P,false,"Framesetp","Steps the engine forward one frame")); - handler.addControl(DEBUG_SWAP_EDITOR_MODE, new Control(ControlType.KEY, GLFW.GLFW_KEY_F4,false,"Swap Editor Mode","Swaps to/from the editor entity")); + handler.addControl(DEBUG_SWAP_EDITOR_MODE, new Control(ControlType.KEY, GLFW.GLFW_KEY_F4,false,"Swap Editor Entity","Swaps to/from the editor entity")); handler.addControl(DEBUG_SWITCH_FIRST_THIRD, new Control(ControlType.KEY, GLFW.GLFW_KEY_F5,false,"Switch First/Third Person","Swaps between first and third person")); + handler.addControl(DEBUG_SWAP_GAME_EDITOR, new Control(ControlType.KEY, GLFW.GLFW_KEY_F6,false,"Toggle Editor Mode","Toggles between the editor mode and the actual game mode")); } /** @@ -73,6 +76,16 @@ public class ControlCategoryInGameDebug { Globals.controlHandler.setIsThirdPerson(!Globals.controlHandler.cameraIsThirdPerson()); }}); controlMap.get(DEBUG_SWITCH_FIRST_THIRD).setRepeatTimeout(0.5f * Main.targetFrameRate); + + // + //Swap between editor and game mode + // + alwaysOnDebugControlList.add(controlMap.get(DEBUG_SWAP_GAME_EDITOR)); + controlMap.get(DEBUG_SWAP_GAME_EDITOR).setOnPress(new ControlMethod(){public void execute(MouseState mouseState){ + LoggerInterface.loggerEngine.INFO("Toggle Editor Mode"); + ImGuiEditorWindows.toggleEditorMenus(); + }}); + controlMap.get(DEBUG_SWAP_GAME_EDITOR).setRepeatTimeout(0.5f * Main.targetFrameRate); } } diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 2329d3bb..766c8560 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -39,6 +39,7 @@ import electrosphere.controls.ScrollCallback; import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.assetmanager.AssetManager; import electrosphere.engine.loadingthreads.InitialAssetLoading; +import electrosphere.engine.os.fs.FileWatcherService; import electrosphere.engine.profiler.Profiler; import electrosphere.engine.service.ServiceManager; import electrosphere.engine.signal.SignalSystem; @@ -387,6 +388,9 @@ public class Globals { //manager for all widgets currently being drawn to screen public static ElementService elementService; public static int openInventoriesCount = 0; + + //file service + public static FileWatcherService fileWatcherService; //collision world data public static CollisionWorldData commonWorldData; @@ -546,6 +550,7 @@ public class Globals { Globals.particleService = (ParticleService)serviceManager.registerService(new ParticleService()); Globals.scriptEngine = (ScriptEngine)serviceManager.registerService(new ScriptEngine()); Globals.mainThreadSignalService = (MainThreadSignalService)serviceManager.registerService(new MainThreadSignalService()); + Globals.fileWatcherService = (FileWatcherService)serviceManager.registerService(new FileWatcherService()); serviceManager.instantiate(); // //End service manager @@ -557,6 +562,7 @@ public class Globals { Globals.signalSystem.registerService(Globals.particleService); Globals.signalSystem.registerService(Globals.scriptEngine); Globals.signalSystem.registerService(Globals.mainThreadSignalService); + Globals.signalSystem.registerService(Globals.fileWatcherService); } @@ -744,6 +750,7 @@ public class Globals { Globals.threadManager = null; Globals.signalSystem = null; Globals.serviceManager = null; + Globals.fileWatcherService = null; Globals.clientConnection = null; Globals.clientSynchronizationManager = null; Globals.server = null; diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index ab393679..73db5ed9 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -263,7 +263,7 @@ public class Main { LoggerInterface.loggerEngine.DEBUG_LOOP("Begin load assets"); Globals.assetManager.loadAssetsInQueue(); LoggerInterface.loggerEngine.DEBUG_LOOP("Begin delete assets"); - Globals.assetManager.deleteModelsInDeleteQueue(); + Globals.assetManager.handleDeleteQueue(); Globals.profiler.endCpuSample(); } @@ -302,8 +302,11 @@ public class Main { SynchronousSignalHandling.runMainThreadSignalHandlers(); /// - /// S C R I P T E N G I N E + /// E N G I N E S E R V I C E S /// + if(Globals.fileWatcherService != null){ + Globals.fileWatcherService.poll(); + } if(Globals.RUN_SCRIPTS && Globals.scriptEngine != null){ Globals.scriptEngine.scanScriptDir(); } diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java index b042d5b5..0304e798 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java @@ -66,6 +66,7 @@ public class AssetManager { Map poseModelsLoadedIntoMemory = new ConcurrentHashMap(); List poseModelsInQueue = new CopyOnWriteArrayList(); + List poseModelsInDeleteQueue = new CopyOnWriteArrayList(); //A queue of homogenous buffers to allocate this render frame List homogenousBufferAllocationQueue = new CopyOnWriteArrayList(); @@ -173,43 +174,59 @@ public class AssetManager { //allocate homogenous buffers LoggerInterface.loggerEngine.DEBUG_LOOP("AssetManager - Allocate homogenous buffers"); - allocateHomogenousBuffers(); + this.allocateHomogenousBuffers(); //allocate instance array buffers LoggerInterface.loggerEngine.DEBUG_LOOP("AssetManager - Allocate instance array buffers"); - allocateInstanceArrayBuffers(); + this.allocateInstanceArrayBuffers(); //override meshes LoggerInterface.loggerEngine.DEBUG_LOOP("AssetManager - Override meshes"); - performMeshOverrides(); + this.performMeshOverrides(); } - - - - /** - * Deletes all models in the delete queue - */ - public void deleteModelsInDeleteQueue(){ - for(String modelPath : modelsInDeleteQueue){ - Model model = this.fetchModel(modelPath); - if(model != null){ - model.delete(); - } - this.modelsLoadedIntoMemory.remove(modelPath); + + + // + //Updating assets + // + public void updateAsset(String path){ + LoggerInterface.loggerEngine.DEBUG("AssetManager - updateAsset"); + //models + if(modelsLoadedIntoMemory.containsKey(path)){ + this.queueModelForDeletion(path); + this.deleteModelsInDeleteQueue(); + this.addModelPathToQueue(path); + } + //textures + if(texturesLoadedIntoMemory.containsKey(path)){ + this.queueTextureForDeletion(path); + this.deleteTexturesInDeleteQueue(); + this.addTexturePathtoQueue(path); + } + //pose models + if(poseModelsLoadedIntoMemory.containsKey(path)){ + this.queuePoseModelForDeletion(path); + this.deletePoseModelsInDeleteQueue(); + this.addPoseModelPathToQueue(path); + } + if(audioLoadedIntoMemory.containsKey(path)){ + throw new Error("Unhandled asset type! (Audio)"); + } + if(shadersLoadedIntoMemory.containsKey(path)){ + throw new Error("Unhandled asset type! (Shader - Visual)"); + } + if(computeShadersLoadedIntoMemory.containsKey(path)){ + throw new Error("Unhandled asset type! (Shader - Compute)"); } } /** - * Deletes all textures in the delete queue + * Handles the delete queues */ - public void deleteTexturesInDeleteQueue(){ - for(String texturePath : texturesInDeleteQueue){ - Texture texture = this.fetchTexture(texturePath); - if(texture != null){ - texture.free(); - } - this.texturesLoadedIntoMemory.remove(texturePath); - } + public void handleDeleteQueue(){ + this.deleteModelsInDeleteQueue(); + this.deletePoseModelsInDeleteQueue(); + this.deleteTexturesInDeleteQueue(); } @@ -217,6 +234,8 @@ public class AssetManager { + + // //Models // @@ -305,6 +324,19 @@ public class AssetManager { // modelsLoadedIntoMemory.clear(); } + /** + * Deletes all models in the delete queue + */ + public void deleteModelsInDeleteQueue(){ + for(String modelPath : modelsInDeleteQueue){ + Model model = this.fetchModel(modelPath); + if(model != null){ + model.delete(); + } + this.modelsLoadedIntoMemory.remove(modelPath); + } + } + @@ -347,6 +379,27 @@ public class AssetManager { public void registerPoseModelWithPath(PoseModel m, String path){ poseModelsLoadedIntoMemory.put(path, m); } + + /** + * Queues a pose model for deletion + * @param modelPath The path to the pose model + */ + public void queuePoseModelForDeletion(String modelPath){ + poseModelsInDeleteQueue.add(modelPath); + } + + /** + * Deletes all pose models in the delete queue + */ + public void deletePoseModelsInDeleteQueue(){ + for(String modelPath : poseModelsInDeleteQueue){ + PoseModel poseModel = this.fetchPoseModel(modelPath); + if(poseModel != null){ + poseModel.delete(); + } + this.poseModelsLoadedIntoMemory.remove(modelPath); + } + } @@ -423,6 +476,19 @@ public class AssetManager { } return null; } + + /** + * Deletes all textures in the delete queue + */ + public void deleteTexturesInDeleteQueue(){ + for(String texturePath : texturesInDeleteQueue){ + Texture texture = this.fetchTexture(texturePath); + if(texture != null){ + texture.free(); + } + this.texturesLoadedIntoMemory.remove(texturePath); + } + } diff --git a/src/main/java/electrosphere/engine/os/OSDragAndDrop.java b/src/main/java/electrosphere/engine/os/OSDragAndDrop.java new file mode 100644 index 00000000..94dec9cf --- /dev/null +++ b/src/main/java/electrosphere/engine/os/OSDragAndDrop.java @@ -0,0 +1,63 @@ +package electrosphere.engine.os; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +import electrosphere.client.ui.menu.editor.ImGuiEditorWindows; +import electrosphere.engine.Globals; +import electrosphere.engine.os.fs.FileWatcher; +import electrosphere.logger.LoggerInterface; + +/** + * Handles drag-and-drop signals from the OS + */ +public class OSDragAndDrop { + + /** + * Fires when a file path is dropped on the application + * @param paths The paths of the files + */ + public static void handleDragAndDrop(List paths){ + if(ImGuiEditorWindows.getHierarchyWindow().isOpen()){ + OSDragAndDrop.handleDropAsset(paths); + } + } + + /** + * Handles the case where asset path(s) have been dropped on the application + * @param paths The paths of the assets + */ + private static void handleDropAsset(List paths){ + //try watching the file and register it to be an asset + LoggerInterface.loggerFileIO.WARNING("File(s) added to application"); + for(String rawPath : paths){ + String normalized = rawPath.toLowerCase(); + Path absolutePath = new File(rawPath).toPath(); + Path relativePath = new File("./assets").getAbsoluteFile().toPath().relativize(absolutePath); + + //register based on file type + if(normalized.contains(".glsl") || normalized.contains(".dae")){ + Globals.assetManager.addModelPathToQueue(relativePath.toString()); + } else if(normalized.contains(".wav") || normalized.contains(".ogg")){ + Globals.assetManager.addAudioPathToQueue(relativePath.toString()); + } else if(normalized.contains(".jpg") || normalized.contains(".jpeg") || normalized.contains(".png")){ + Globals.assetManager.addTexturePathtoQueue(relativePath.toString()); + } else { + throw new Error("Unhandled file type! " + rawPath); + } + + //watch the asset for file updates + LoggerInterface.loggerEngine.WARNING("Tracking " + rawPath); + Globals.fileWatcherService.trackFile(new FileWatcher(rawPath).setOnWrite(updatedPath -> { + LoggerInterface.loggerFileIO.WARNING("File updated: " + updatedPath.toString()); + Globals.assetManager.updateAsset(updatedPath.toString()); + }).setOnDelete(updatedPath -> { + LoggerInterface.loggerFileIO.WARNING("File deleted: " + updatedPath.toString()); + }).setOnCreate(updatedPath -> { + LoggerInterface.loggerFileIO.WARNING("File created: " + updatedPath.toString()); + })); + } + } + +} diff --git a/src/main/java/electrosphere/engine/os/fs/FileWatcher.java b/src/main/java/electrosphere/engine/os/fs/FileWatcher.java new file mode 100644 index 00000000..abdab1a9 --- /dev/null +++ b/src/main/java/electrosphere/engine/os/fs/FileWatcher.java @@ -0,0 +1,123 @@ +package electrosphere.engine.os.fs; + +import java.io.File; +import java.nio.file.Path; +import java.util.function.Consumer; + +/** + * Sets a callback to fire every time a type of event happens to a fire + */ +public class FileWatcher { + + /** + * The path of the file that is being watched + */ + Path path; + + /** + * The callback that fires when the file is created + */ + Consumer onCreate; + + /** + * The callback that fires when the file is edited + */ + Consumer onWrite; + + /** + * The callback that fires when the file is deleted + */ + Consumer onDelete; + + /** + * Constructor + * @param path The path the watcher should follow + */ + public FileWatcher(String path){ + this.path = new File(path).toPath(); + } + + /** + * Gets the path of the file that this watcher is tracking + * @return The path of the file that this watcher is tracking + */ + public Path getPath(){ + return this.path; + } + + /** + * Sets the on-create callback for the watcher + * @param onCreate The callback that fires when the file is created + * @return The file watcher instance that the callback is being attached to + */ + public FileWatcher setOnCreate(Consumer onCreate){ + this.onCreate = onCreate; + return this; + } + + /** + * Gets the on-create callback + * @return The on-create callback + */ + public Consumer getOnCreate(){ + return this.onCreate; + } + + /** + * Fires when the file is created + */ + protected void onCreate(){ + this.onCreate.accept(path); + } + + /** + * Sets the on-write callback for the watcher + * @param onWrite The callback that fires when the file is edited + * @return The file watcher instance that the callback is being attached to + */ + public FileWatcher setOnWrite(Consumer onWrite){ + this.onWrite = onWrite; + return this; + } + + /** + * Gets the on-write callback + * @return The on-write callback + */ + public Consumer getOnWrite(){ + return this.onWrite; + } + + /** + * Fires when the file is written to + */ + protected void onWrite(){ + this.onWrite.accept(path); + } + + /** + * Sets the on-delete callback for the watcher + * @param onDelete The callback that fires when the file is deleted + * @return The file watcher instance that the callback is being attached to + */ + public FileWatcher setOnDelete(Consumer onDelete){ + this.onDelete = onDelete; + return this; + } + + /** + * Gets the on-delete callback + * @return The on-delete callback + */ + public Consumer getOnDelete(){ + return this.onDelete; + } + + /** + * Fires when the file is deleted + */ + protected void onDelete(){ + this.onDelete.accept(path); + } + +} diff --git a/src/main/java/electrosphere/engine/os/fs/FileWatcherService.java b/src/main/java/electrosphere/engine/os/fs/FileWatcherService.java new file mode 100644 index 00000000..e26acb98 --- /dev/null +++ b/src/main/java/electrosphere/engine/os/fs/FileWatcherService.java @@ -0,0 +1,118 @@ +package electrosphere.engine.os.fs; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import electrosphere.engine.signal.Signal.SignalType; +import electrosphere.engine.signal.SignalServiceImpl; +import electrosphere.logger.LoggerInterface; + +/** + * Service that manages file watchers + */ +public class FileWatcherService extends SignalServiceImpl { + + /** + * The file system object + */ + FileSystem fs; + + /** + * The watch service + */ + WatchService watchService; + + /** + * Map of path to watcher + */ + Map pathWatcherMap; + + /** + * Constructor + */ + public FileWatcherService() { + super( + "FileWatcher", + new SignalType[]{ + } + ); + this.fs = FileSystems.getDefault(); + try { + this.watchService = fs.newWatchService(); + } catch (IOException e) { + LoggerInterface.loggerFileIO.ERROR(e); + } + this.pathWatcherMap = new HashMap(); + } + + /** + * Tracks a file + * @param watcher Tracks a file + */ + public void trackFile(FileWatcher watcher){ + try { + watcher.getPath().getParent().register( + watchService, + new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE} + ); + pathWatcherMap.put(watcher.path.toAbsolutePath().normalize().toString(),watcher); + } catch (IOException e) { + LoggerInterface.loggerEngine.ERROR(e); + } + } + + /** + * Scans all tracked files + */ + public void poll(){ + WatchKey key = null; + while((key = watchService.poll()) != null){ + List> events = key.pollEvents(); + Path keyParentDir = (Path)key.watchable(); + for(WatchEvent event : events){ + if(event.kind() == StandardWatchEventKinds.ENTRY_MODIFY){ + if(event.context() instanceof Path){ + Path filePath = (Path)event.context(); + Path resolved = keyParentDir.resolve(filePath); + String normalized = resolved.toAbsolutePath().normalize().toString(); + FileWatcher watcher = this.pathWatcherMap.get(normalized); + if(watcher != null){ + watcher.onWrite(); + } + } + } else if(event.kind() == StandardWatchEventKinds.ENTRY_CREATE){ + if(event.context() instanceof Path){ + Path filePath = (Path)event.context(); + Path resolved = keyParentDir.resolve(filePath); + String normalized = resolved.toAbsolutePath().normalize().toString(); + FileWatcher watcher = this.pathWatcherMap.get(normalized); + if(watcher != null){ + watcher.onCreate(); + } + } + } else if(event.kind() == StandardWatchEventKinds.ENTRY_DELETE){ + if(event.context() instanceof Path){ + Path filePath = (Path)event.context(); + Path resolved = keyParentDir.resolve(filePath); + String normalized = resolved.toAbsolutePath().normalize().toString(); + FileWatcher watcher = this.pathWatcherMap.get(normalized); + if(watcher != null){ + watcher.onDelete(); + } + } + } + } + key.reset(); + } + } + +} diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index eac0ebd5..823aeba6 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -9,10 +9,13 @@ import static org.lwjgl.opengl.GL30.glBindRenderbuffer; import static org.lwjgl.system.MemoryUtil.NULL; import java.nio.IntBuffer; +import java.util.LinkedList; +import java.util.List; import org.joml.Matrix4d; import org.joml.Vector3d; import org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; @@ -21,8 +24,10 @@ import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL45; import org.lwjgl.opengl.GLDebugMessageCallback; import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; import electrosphere.engine.Globals; +import electrosphere.engine.os.OSDragAndDrop; import electrosphere.engine.signal.Signal.SignalType; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.debug.DebugRendering; @@ -444,6 +449,19 @@ public class RenderingEngine { // fogColor.flip(); // GL11.glFogfv(GL_FOG_COLOR, fogColor); + // + //Set file drag-and-drop for app + // + GLFW.glfwSetDropCallback(Globals.window, (long window, int count, long names) -> { + PointerBuffer charPointers = MemoryUtil.memPointerBuffer(names, count); + List paths = new LinkedList(); + for(int i = 0; i < count; i++){ + String name = MemoryUtil.memUTF8(charPointers.get(i)); + paths.add(name); + } + OSDragAndDrop.handleDragAndDrop(paths); + }); + // //Init pipelines // diff --git a/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java b/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java index e03b2994..d3477410 100644 --- a/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import electrosphere.client.ui.menu.debug.ImGuiWindowMacros; +import electrosphere.client.ui.menu.editor.ImGuiEditorWindows; import electrosphere.engine.Globals; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; @@ -63,6 +64,7 @@ public class ImGuiPipeline implements RenderPipeline { ImGui.render(); imGuiGl13.renderDrawData(ImGui.getDrawData()); ImGuiWindowMacros.synchronizeMainDebugMenuVisibility(); + ImGuiEditorWindows.synchronizeMainEditorMenuVisibility(); } } diff --git a/src/main/java/electrosphere/script/ScriptEngine.java b/src/main/java/electrosphere/script/ScriptEngine.java index 3cf838be..4e5903db 100644 --- a/src/main/java/electrosphere/script/ScriptEngine.java +++ b/src/main/java/electrosphere/script/ScriptEngine.java @@ -161,8 +161,7 @@ public class ScriptEngine extends SignalServiceImpl { } }); } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LoggerInterface.loggerEngine.ERROR(e); } } diff --git a/src/main/java/electrosphere/server/poseactor/PoseModel.java b/src/main/java/electrosphere/server/poseactor/PoseModel.java index 860d115c..f549b97d 100644 --- a/src/main/java/electrosphere/server/poseactor/PoseModel.java +++ b/src/main/java/electrosphere/server/poseactor/PoseModel.java @@ -267,4 +267,11 @@ public class PoseModel { return this.bones; } + /** + * Delete - currently does nothing + */ + public void delete(){ + + } + }