diff --git a/src/main/java/sh/ball/gui/Controller.java b/src/main/java/sh/ball/gui/Controller.java index 0a5cb84..e975709 100644 --- a/src/main/java/sh/ball/gui/Controller.java +++ b/src/main/java/sh/ball/gui/Controller.java @@ -5,6 +5,7 @@ import javafx.animation.Timeline; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.scene.control.*; +import javafx.stage.DirectoryChooser; import javafx.util.Duration; import sh.ball.audio.*; import sh.ball.audio.effect.*; @@ -14,10 +15,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.ResourceBundle; +import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; @@ -47,14 +45,13 @@ import sh.ball.shapes.Vector2; public class Controller implements Initializable, FrequencyListener, Listener { - private static final InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj"); private final FileChooser fileChooser = new FileChooser(); + private final DirectoryChooser folderChooser = new DirectoryChooser(); private final AudioPlayer> audioPlayer; private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private final RotateEffect rotateEffect; private final TranslateEffect translateEffect; private final WobbleEffect wobbleEffect; @@ -63,9 +60,13 @@ public class Controller implements Initializable, FrequencyListener, Listener { private int sampleRate; private FrequencyAnalyser> analyser; private final AudioDevice defaultDevice; - private FrameProducer> producer; private boolean recording = false; + private FrameProducer> producer; + private final List>> frameSets = new ArrayList<>(); + private final List frameSetPaths = new ArrayList<>(); + private int currentFrameSet; + private Stage stage; @FXML @@ -73,6 +74,8 @@ public class Controller implements Initializable, FrequencyListener, Listener { @FXML private Button chooseFileButton; @FXML + private Button chooseFolderButton; + @FXML private Label fileLabel; @FXML private Button recordButton; @@ -130,6 +133,8 @@ public class Controller implements Initializable, FrequencyListener, Listener { public Controller(AudioPlayer> audioPlayer) throws IOException { this.audioPlayer = audioPlayer; FrameSet> frames = new ObjParser(DEFAULT_OBJ).parse(); + frameSets.add(frames); + currentFrameSet = 0; frames.addListener(this); this.producer = new FrameProducer<>(audioPlayer, frames); this.defaultDevice = audioPlayer.getDefaultDevice(); @@ -244,6 +249,13 @@ public class Controller implements Initializable, FrequencyListener, Listener { } }); + chooseFolderButton.setOnAction(e -> { + File file = folderChooser.showDialog(stage); + if (file != null) { + chooseFile(file); + } + }); + recordButton.setOnAction(event -> toggleRecord()); updateObjectRotateSpeed(); @@ -365,7 +377,9 @@ public class Controller implements Initializable, FrequencyListener, Listener { } } - private void changeFrameSet(FrameSet> frames) { + private void changeFrameSet() { + FrameSet> frames = frameSets.get(currentFrameSet); + producer.stop(); frames.addListener(this); producer = new FrameProducer<>(audioPlayer, frames); @@ -380,22 +394,41 @@ public class Controller implements Initializable, FrequencyListener, Listener { }); Timeline timeline = new Timeline(kf1, kf2); Platform.runLater(timeline::play); + fileLabel.setText(frameSetPaths.get(currentFrameSet)); + objTitledPane.setDisable(!ObjParser.isObjFile(frameSetPaths.get(currentFrameSet))); } - private void chooseFile(File file) { + private void chooseFile(File chosenFile) { try { - producer.stop(); - String path = file.getAbsolutePath(); - changeFrameSet(ParserFactory.getParser(path).parse()); + if (chosenFile.exists()) { + String path = chosenFile.getAbsolutePath(); + frameSets.clear(); + frameSetPaths.clear(); - if (file.exists() && !file.isDirectory()) { - fileLabel.setText(path); - objTitledPane.setDisable(!ObjParser.isObjFile(path)); - } else { - objTitledPane.setDisable(true); + if (chosenFile.isDirectory()) { + for (File file : chosenFile.listFiles()) { + try { + frameSets.add(ParserFactory.getParser(file.getAbsolutePath()).parse()); + frameSetPaths.add(file.getAbsolutePath()); + } catch (IOException ignored) {} + } + } else { + frameSets.add(ParserFactory.getParser(path).parse()); + frameSetPaths.add(path); + } + + currentFrameSet = 0; + changeFrameSet(); } } catch (IOException | ParserConfigurationException | SAXException ioException) { ioException.printStackTrace(); + + // display error to user (for debugging purposes) + String oldPath = fileLabel.getText(); + KeyFrame kf1 = new KeyFrame(Duration.seconds(0), e -> fileLabel.setText(ioException.getMessage())); + KeyFrame kf2 = new KeyFrame(Duration.seconds(5), e -> fileLabel.setText(oldPath)); + Timeline timeline = new Timeline(kf1, kf2); + Platform.runLater(timeline::play); } } @@ -403,6 +436,22 @@ public class Controller implements Initializable, FrequencyListener, Listener { this.stage = stage; } + public void nextFrameSet() { + currentFrameSet++; + if (currentFrameSet >= frameSets.size()) { + currentFrameSet = 0; + } + changeFrameSet(); + } + + public void previousFrameSet() { + currentFrameSet--; + if (currentFrameSet < 0) { + currentFrameSet = frameSets.size() - 1; + } + changeFrameSet(); + } + protected boolean mouseRotate() { return rotateCheckBox.isSelected(); } diff --git a/src/main/java/sh/ball/gui/Gui.java b/src/main/java/sh/ball/gui/Gui.java index 3c3a788..c2ac164 100644 --- a/src/main/java/sh/ball/gui/Gui.java +++ b/src/main/java/sh/ball/gui/Gui.java @@ -33,20 +33,30 @@ public class Gui extends Application { stage.setTitle("osci-render"); Scene scene = new Scene(root); scene.getStylesheets().add(getClass().getResource("/css/main.css").toExternalForm()); + + scene.addEventHandler(KeyEvent.KEY_PRESSED, (event -> { + switch (event.getCode()) { + case K -> controller.previousFrameSet(); + case J -> controller.nextFrameSet(); + } + })); + scene.addEventFilter(MouseEvent.MOUSE_MOVED, event -> { if (controller.mouseRotate()) { controller.setObjRotate(new Vector3( 3 * Math.PI * (event.getSceneY() / scene.getHeight()), - 3 * Math.PI * (event.getSceneX() / scene.getWidth()), + 3 * Math.PI * (event.getSceneX() / scene.getWidth()), 0 )); } }); + scene.addEventHandler(KeyEvent.KEY_PRESSED, t -> { if (t.getCode() == KeyCode.ESCAPE) { controller.disableMouseRotate(); } }); + stage.setScene(scene); stage.setResizable(false); diff --git a/src/main/resources/fxml/osci-render.fxml b/src/main/resources/fxml/osci-render.fxml index 272781c..5752351 100644 --- a/src/main/resources/fxml/osci-render.fxml +++ b/src/main/resources/fxml/osci-render.fxml @@ -10,18 +10,19 @@ - - + +