osci-render/src/main/java/sh/ball/gui/Controller.java

275 wiersze
8.8 KiB
Java
Czysty Zwykły widok Historia

package sh.ball.gui;
2020-11-22 14:39:38 +00:00
import javafx.scene.control.*;
import sh.ball.audio.Renderer;
2021-05-15 17:53:46 +00:00
import sh.ball.audio.effect.Effect;
import sh.ball.audio.effect.EffectType;
import sh.ball.audio.effect.RotateEffect;
import sh.ball.audio.effect.ScaleEffect;
import sh.ball.audio.effect.EffectFactory;
import sh.ball.audio.FrameProducer;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
2020-11-22 18:03:20 +00:00
import java.net.URL;
import java.util.List;
import java.util.Map;
2020-11-22 18:03:20 +00:00
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.beans.InvalidationListener;
2020-11-22 18:03:20 +00:00
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
2021-05-15 17:53:46 +00:00
import sh.ball.audio.effect.TranslateEffect;
import sh.ball.engine.Vector3;
import sh.ball.parser.obj.ObjFrameSettings;
import sh.ball.parser.obj.ObjParser;
import sh.ball.parser.ParserFactory;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
2020-11-22 18:03:20 +00:00
public class Controller implements Initializable {
private static final int SAMPLE_RATE = 192000;
private static final InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
private static final double DEFAULT_ROTATE_SPEED = 0.1;
private final FileChooser fileChooser = new FileChooser();
private final Renderer<List<Shape>> renderer;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final RotateEffect rotateEffect = new RotateEffect(SAMPLE_RATE);
private final TranslateEffect translateEffect = new TranslateEffect(SAMPLE_RATE);
private final ScaleEffect scaleEffect = new ScaleEffect();
private FrameProducer<List<Shape>> producer;
private Stage stage;
@FXML
private Button chooseFileButton;
@FXML
2020-11-25 21:28:37 +00:00
private Label fileLabel;
@FXML
2020-11-25 21:28:37 +00:00
private TextField translationXTextField;
@FXML
2020-11-25 21:28:37 +00:00
private TextField translationYTextField;
@FXML
2020-11-25 21:28:37 +00:00
private Slider weightSlider;
@FXML
2020-11-25 21:28:37 +00:00
private Label weightLabel;
@FXML
2020-11-25 21:28:37 +00:00
private Slider rotateSpeedSlider;
@FXML
2020-11-25 21:28:37 +00:00
private Label rotateSpeedLabel;
2020-11-22 18:03:20 +00:00
@FXML
2020-11-25 21:28:37 +00:00
private Slider translationSpeedSlider;
2020-11-22 18:03:20 +00:00
@FXML
2020-11-25 21:28:37 +00:00
private Label translationSpeedLabel;
@FXML
2020-11-25 21:28:37 +00:00
private Slider scaleSlider;
@FXML
2020-11-25 21:28:37 +00:00
private Label scaleLabel;
@FXML
2020-11-25 21:28:37 +00:00
private TitledPane objTitledPane;
@FXML
private Slider focalLengthSlider;
@FXML
private Label focalLengthLabel;
2020-11-25 21:28:37 +00:00
@FXML
private TextField cameraXTextField;
@FXML
private TextField cameraYTextField;
@FXML
private TextField cameraZTextField;
@FXML
private Slider objectRotateSpeedSlider;
@FXML
private Label objectRotateSpeedLabel;
@FXML
private TextField rotateXTextField;
@FXML
private TextField rotateYTextField;
@FXML
private TextField rotateZTextField;
@FXML
private CheckBox vectorCancellingCheckBox;
2021-05-09 19:00:46 +00:00
@FXML
private Slider vectorCancellingSlider;
@FXML
private CheckBox bitCrushCheckBox;
@FXML
private Slider bitCrushSlider;
2020-11-22 18:03:20 +00:00
public Controller(Renderer<List<Shape>> renderer) throws IOException {
this.renderer = renderer;
this.producer = new FrameProducer<>(
renderer,
new ObjParser(DEFAULT_OBJ).parse()
);
}
private Map<Slider, SliderUpdater<Double>> initializeSliderMap() {
return Map.of(
weightSlider,
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
rotateSpeedSlider,
new SliderUpdater<>(rotateSpeedLabel::setText, rotateEffect::setSpeed),
translationSpeedSlider,
new SliderUpdater<>(translationSpeedLabel::setText, translateEffect::setSpeed),
scaleSlider,
new SliderUpdater<>(scaleLabel::setText, scaleEffect::setScale),
focalLengthSlider,
new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength),
objectRotateSpeedSlider,
new SliderUpdater<>(objectRotateSpeedLabel::setText, this::setObjectRotateSpeed)
);
}
2021-05-09 19:00:46 +00:00
private Map<EffectType, Slider> effectTypes;
private void initializeEffectTypes() {
effectTypes = Map.of(
EffectType.VECTOR_CANCELLING,
vectorCancellingSlider,
EffectType.BIT_CRUSH,
bitCrushSlider
2021-05-09 19:00:46 +00:00
);
}
// TODO: Refactor and clean up duplication
2020-11-22 18:03:20 +00:00
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
Map<Slider, SliderUpdater<Double>> sliders = initializeSliderMap();
2021-05-09 19:00:46 +00:00
initializeEffectTypes();
for (Slider slider : sliders.keySet()) {
slider.valueProperty().addListener((source, oldValue, newValue) ->
sliders.get(slider).update(slider.getValue())
);
}
InvalidationListener translationUpdate = observable ->
translateEffect.setTranslation(new Vector2(
tryParse(translationXTextField.getText()),
tryParse(translationYTextField.getText())
));
translationXTextField.textProperty().addListener(translationUpdate);
translationYTextField.textProperty().addListener(translationUpdate);
InvalidationListener cameraPosUpdate = observable ->
producer.setFrameSettings(new ObjFrameSettings(new Vector3(
tryParse(cameraXTextField.getText()),
tryParse(cameraYTextField.getText()),
tryParse(cameraZTextField.getText())
)));
cameraXTextField.textProperty().addListener(cameraPosUpdate);
cameraYTextField.textProperty().addListener(cameraPosUpdate);
cameraZTextField.textProperty().addListener(cameraPosUpdate);
2020-11-25 21:28:37 +00:00
InvalidationListener rotateUpdate = observable ->
producer.setFrameSettings(new ObjFrameSettings(new Vector3(
tryParse(rotateXTextField.getText()),
tryParse(rotateYTextField.getText()),
tryParse(rotateZTextField.getText())
), null));
rotateXTextField.textProperty().addListener(rotateUpdate);
rotateYTextField.textProperty().addListener(rotateUpdate);
rotateZTextField.textProperty().addListener(rotateUpdate);
InvalidationListener vectorCancellingListener = e ->
updateEffect(EffectType.VECTOR_CANCELLING, vectorCancellingCheckBox.isSelected(),
2021-05-15 17:53:46 +00:00
EffectFactory.vectorCancelling((int) vectorCancellingSlider.getValue()));
InvalidationListener bitCrushListener = e ->
updateEffect(EffectType.BIT_CRUSH, bitCrushCheckBox.isSelected(),
2021-05-15 17:53:46 +00:00
EffectFactory.bitCrush(bitCrushSlider.getValue()));
vectorCancellingSlider.valueProperty().addListener(vectorCancellingListener);
vectorCancellingCheckBox.selectedProperty().addListener(vectorCancellingListener);
bitCrushSlider.valueProperty().addListener(bitCrushListener);
bitCrushCheckBox.selectedProperty().addListener(bitCrushListener);
chooseFileButton.setOnAction(e -> {
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
chooseFile(file);
}
});
setObjectRotateSpeed(DEFAULT_ROTATE_SPEED);
renderer.addEffect(EffectType.SCALE, scaleEffect);
renderer.addEffect(EffectType.ROTATE, rotateEffect);
renderer.addEffect(EffectType.TRANSLATE, translateEffect);
executor.submit(producer);
new Thread(renderer).start();
}
private void setFocalLength(double focalLength) {
Vector3 pos = (Vector3) producer.setFrameSettings(new ObjFrameSettings(focalLength));
cameraXTextField.setText(String.valueOf(pos.getX()));
cameraYTextField.setText(String.valueOf(pos.getY()));
cameraZTextField.setText(String.valueOf(pos.getZ()));
}
private void setObjectRotateSpeed(double rotateSpeed) {
producer.setFrameSettings(new ObjFrameSettings(null, (Math.exp(3 * rotateSpeed) - 1) / 50));
}
private double tryParse(String value) {
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return 0;
}
}
private void updateEffect(EffectType type, boolean checked, Effect effect) {
if (checked) {
renderer.addEffect(type, effect);
2021-05-09 19:00:46 +00:00
effectTypes.get(type).setDisable(false);
} else {
renderer.removeEffect(type);
2021-05-09 19:00:46 +00:00
effectTypes.get(type).setDisable(true);
}
}
2020-11-25 21:28:37 +00:00
private void chooseFile(File file) {
try {
producer.stop();
2020-11-25 21:28:37 +00:00
String path = file.getAbsolutePath();
producer = new FrameProducer<>(
renderer,
ParserFactory.getParser(path).parse()
);
executor.submit(producer);
2020-11-25 21:28:37 +00:00
if (file.exists() && !file.isDirectory()) {
fileLabel.setText(path);
objTitledPane.setDisable(!ObjParser.isObjFile(path));
} else {
objTitledPane.setDisable(true);
}
} catch (IOException | ParserConfigurationException | SAXException ioException) {
ioException.printStackTrace();
}
}
public void setStage(Stage stage) {
this.stage = stage;
2020-11-22 18:03:20 +00:00
}
2020-11-22 14:39:38 +00:00
}