diff --git a/src/main/java/sh/ball/audio/Effect.java b/src/main/java/sh/ball/audio/Effect.java new file mode 100644 index 0000000..1785fd7 --- /dev/null +++ b/src/main/java/sh/ball/audio/Effect.java @@ -0,0 +1,7 @@ +package sh.ball.audio; + +import sh.ball.shapes.Vector2; + +public interface Effect { + Vector2 apply(int count, Vector2 vector); +} diff --git a/src/main/java/sh/ball/audio/FrameProducer.java b/src/main/java/sh/ball/audio/FrameProducer.java index 8e4de61..4f2c236 100644 --- a/src/main/java/sh/ball/audio/FrameProducer.java +++ b/src/main/java/sh/ball/audio/FrameProducer.java @@ -25,7 +25,13 @@ public class FrameProducer implements Runnable { } public Object setFrameSettings(Object settings) { - renderer.flushFrames(); + return setFrameSettings(settings, false); + } + + public Object setFrameSettings(Object settings, boolean flushFrames) { + if (flushFrames) { + renderer.flushFrames(); + } return frames.setFrameSettings(settings); } } diff --git a/src/main/java/sh/ball/engine/Vector3.java b/src/main/java/sh/ball/engine/Vector3.java index 9adbfb5..01c217c 100644 --- a/src/main/java/sh/ball/engine/Vector3.java +++ b/src/main/java/sh/ball/engine/Vector3.java @@ -79,6 +79,7 @@ public final class Vector3 { ); } + // TODO: Is this correctly used?! public double distance(Vector3 vector) { return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2) + Math.pow(vector.z, 2)); } diff --git a/src/main/java/sh/ball/engine/WorldObject.java b/src/main/java/sh/ball/engine/WorldObject.java index 3cf7daa..71169f9 100644 --- a/src/main/java/sh/ball/engine/WorldObject.java +++ b/src/main/java/sh/ball/engine/WorldObject.java @@ -2,7 +2,6 @@ package sh.ball.engine; import com.mokiat.data.front.parser.*; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; diff --git a/src/main/java/sh/ball/gui/Controller.java b/src/main/java/sh/ball/gui/Controller.java index e2b15cf..deacd6c 100644 --- a/src/main/java/sh/ball/gui/Controller.java +++ b/src/main/java/sh/ball/gui/Controller.java @@ -31,6 +31,7 @@ import org.xml.sax.SAXException; import sh.ball.audio.effect.TranslateEffect; import sh.ball.engine.Vector3; import sh.ball.parser.obj.ObjFrameSettings; +import sh.ball.parser.obj.ObjSettingsFactory; import sh.ball.parser.obj.ObjParser; import sh.ball.parser.ParserFactory; import sh.ball.shapes.Shape; @@ -40,6 +41,7 @@ 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> renderer; @@ -90,6 +92,18 @@ public class Controller implements Initializable { @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 Button resetRotationButton; + @FXML private CheckBox vectorCancellingCheckBox; @FXML private Slider vectorCancellingSlider; @@ -117,7 +131,9 @@ public class Controller implements Initializable { scaleSlider, new SliderUpdater<>(scaleLabel::setText, scaleEffect::setScale), focalLengthSlider, - new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength) + new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength), + objectRotateSpeedSlider, + new SliderUpdater<>(objectRotateSpeedLabel::setText, this::setObjectRotateSpeed) ); } @@ -132,6 +148,7 @@ public class Controller implements Initializable { ); } + // TODO: Refactor and clean up duplication @Override public void initialize(URL url, ResourceBundle resourceBundle) { Map> sliders = initializeSliderMap(); @@ -153,16 +170,29 @@ public class Controller implements Initializable { translationYTextField.textProperty().addListener(translationUpdate); InvalidationListener cameraPosUpdate = observable -> - producer.setFrameSettings(new ObjFrameSettings(new Vector3( + producer.setFrameSettings(ObjSettingsFactory.cameraPosition(new Vector3( tryParse(cameraXTextField.getText()), tryParse(cameraYTextField.getText()), tryParse(cameraZTextField.getText()) - ))); + )), true); cameraXTextField.textProperty().addListener(cameraPosUpdate); cameraYTextField.textProperty().addListener(cameraPosUpdate); cameraZTextField.textProperty().addListener(cameraPosUpdate); + InvalidationListener rotateUpdate = observable -> + producer.setFrameSettings(ObjSettingsFactory.rotation(new Vector3( + tryParse(rotateXTextField.getText()), + tryParse(rotateYTextField.getText()), + tryParse(rotateZTextField.getText()) + ))); + + rotateXTextField.textProperty().addListener(rotateUpdate); + rotateYTextField.textProperty().addListener(rotateUpdate); + rotateZTextField.textProperty().addListener(rotateUpdate); + + resetRotationButton.setOnAction(e -> producer.setFrameSettings(ObjSettingsFactory.resetRotation())); + InvalidationListener vectorCancellingListener = e -> updateEffect(EffectType.VECTOR_CANCELLING, vectorCancellingCheckBox.isSelected(), EffectFactory.vectorCancelling((int) vectorCancellingSlider.getValue())); @@ -183,6 +213,8 @@ public class Controller implements Initializable { } }); + setObjectRotateSpeed(DEFAULT_ROTATE_SPEED); + renderer.addEffect(EffectType.SCALE, scaleEffect); renderer.addEffect(EffectType.ROTATE, rotateEffect); renderer.addEffect(EffectType.TRANSLATE, translateEffect); @@ -192,12 +224,21 @@ public class Controller implements Initializable { } private void setFocalLength(double focalLength) { - Vector3 pos = (Vector3) producer.setFrameSettings(new ObjFrameSettings(focalLength)); + Vector3 pos = (Vector3) producer.setFrameSettings( + ObjSettingsFactory.focalLength(focalLength), + true + ); 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( + ObjSettingsFactory.rotateSpeed((Math.exp(3 * rotateSpeed) - 1) / 50) + ); + } + private double tryParse(String value) { try { return Double.parseDouble(value); diff --git a/src/main/java/sh/ball/gui/EffectType.java b/src/main/java/sh/ball/gui/EffectType.java new file mode 100644 index 0000000..c13ffab --- /dev/null +++ b/src/main/java/sh/ball/gui/EffectType.java @@ -0,0 +1,5 @@ +package sh.ball.gui; + +enum EffectType { + VECTOR_CANCELLING +} diff --git a/src/main/java/sh/ball/parser/obj/ObjFrameSet.java b/src/main/java/sh/ball/parser/obj/ObjFrameSet.java index 278d709..2a8450c 100644 --- a/src/main/java/sh/ball/parser/obj/ObjFrameSet.java +++ b/src/main/java/sh/ball/parser/obj/ObjFrameSet.java @@ -12,36 +12,44 @@ public class ObjFrameSet implements FrameSet> { private final WorldObject object; private final Camera camera; - private final Vector3 rotation; private final boolean isDefaultPosition; - public ObjFrameSet(WorldObject object, Camera camera, Vector3 rotation, boolean isDefaultPosition) { + private Vector3 rotation = new Vector3(); + private Double rotateSpeed = 0.0; + + public ObjFrameSet(WorldObject object, Camera camera, boolean isDefaultPosition) { this.object = object; this.camera = camera; - this.rotation = rotation; this.isDefaultPosition = isDefaultPosition; } @Override public List next() { - object.rotate(rotation); + object.rotate(rotation.scale(rotateSpeed)); return camera.draw(object); } + // TODO: Refactor! @Override public Object setFrameSettings(Object settings) { if (settings instanceof ObjFrameSettings obj) { - Double focalLength = obj.focalLength(); - Vector3 cameraPos = obj.cameraPos(); - - if (focalLength != null && camera.getFocalLength() != focalLength) { - camera.setFocalLength(focalLength); + if (obj.focalLength != null && camera.getFocalLength() != obj.focalLength) { + camera.setFocalLength(obj.focalLength); if (isDefaultPosition) { camera.findZPos(object); } } - if (cameraPos != null && camera.getPos() != cameraPos) { - camera.setPos(cameraPos); + if (obj.cameraPos != null && camera.getPos() != obj.cameraPos) { + camera.setPos(obj.cameraPos); + } + if (obj.rotation != null) { + this.rotation = obj.rotation; + } + if (obj.rotateSpeed != null) { + this.rotateSpeed = obj.rotateSpeed; + } + if (obj.resetRotation) { + object.resetRotation(); } } diff --git a/src/main/java/sh/ball/parser/obj/ObjFrameSettings.java b/src/main/java/sh/ball/parser/obj/ObjFrameSettings.java index 70820c6..b83e2f7 100644 --- a/src/main/java/sh/ball/parser/obj/ObjFrameSettings.java +++ b/src/main/java/sh/ball/parser/obj/ObjFrameSettings.java @@ -2,13 +2,28 @@ package sh.ball.parser.obj; import sh.ball.engine.Vector3; -public record ObjFrameSettings(Double focalLength, Vector3 cameraPos) { +public class ObjFrameSettings { - public ObjFrameSettings(double focalLength) { - this(focalLength, null); + protected Double focalLength; + protected Vector3 cameraPos; + protected Vector3 rotation; + protected Double rotateSpeed; + protected boolean resetRotation = false; + + protected ObjFrameSettings(double focalLength) { + this.focalLength = focalLength; } - public ObjFrameSettings(Vector3 cameraPos) { - this(null, cameraPos); + protected ObjFrameSettings(Vector3 cameraPos) { + this.cameraPos = cameraPos; + } + + protected ObjFrameSettings(Vector3 rotation, Double rotateSpeed) { + this.rotation = rotation; + this.rotateSpeed = rotateSpeed; + } + + protected ObjFrameSettings(boolean resetRotation) { + this.resetRotation = resetRotation; } } diff --git a/src/main/java/sh/ball/parser/obj/ObjParser.java b/src/main/java/sh/ball/parser/obj/ObjParser.java index 222f1cd..ba22bcb 100644 --- a/src/main/java/sh/ball/parser/obj/ObjParser.java +++ b/src/main/java/sh/ball/parser/obj/ObjParser.java @@ -16,27 +16,22 @@ import sh.ball.shapes.Shape; public class ObjParser extends FileParser>> { - private static final float DEFAULT_ROTATE_SPEED = 3; - - private final Vector3 rotation; private final boolean isDefaultPosition; private final InputStream input; private final Camera camera; private WorldObject object; - public ObjParser(InputStream input, float rotateSpeed, float cameraX, float cameraY, float cameraZ, + public ObjParser(InputStream input, float cameraX, float cameraY, float cameraZ, float focalLength, boolean isDefaultPosition) { - rotateSpeed *= Math.PI / 1000; this.input = input; this.isDefaultPosition = isDefaultPosition; Vector3 cameraPos = new Vector3(cameraX, cameraY, cameraZ); this.camera = new Camera(focalLength, cameraPos); - this.rotation = new Vector3(0, rotateSpeed, rotateSpeed); } public ObjParser(InputStream input, float focalLength) { - this(input, DEFAULT_ROTATE_SPEED, 0, 0, 0, focalLength, true); + this(input, 0, 0, 0, focalLength, true); } public ObjParser(InputStream input) { @@ -60,7 +55,7 @@ public class ObjParser extends FileParser>> { camera.findZPos(object); } - return new ObjFrameSet(object, camera, rotation, isDefaultPosition); + return new ObjFrameSet(object, camera, isDefaultPosition); } // If camera position arguments haven't been specified, automatically work out the position of @@ -75,8 +70,4 @@ public class ObjParser extends FileParser>> { public static boolean isObjFile(String path) { return path.matches(".*\\.obj"); } - - public void setCameraPos(Vector3 vector) { - camera.setPos(vector); - } } diff --git a/src/main/java/sh/ball/parser/obj/ObjSettingsFactory.java b/src/main/java/sh/ball/parser/obj/ObjSettingsFactory.java new file mode 100644 index 0000000..4ff25ac --- /dev/null +++ b/src/main/java/sh/ball/parser/obj/ObjSettingsFactory.java @@ -0,0 +1,26 @@ +package sh.ball.parser.obj; + +import sh.ball.engine.Vector3; + +public class ObjSettingsFactory { + + public static ObjFrameSettings focalLength(double focalLength) { + return new ObjFrameSettings(focalLength); + } + + public static ObjFrameSettings cameraPosition(Vector3 cameraPos) { + return new ObjFrameSettings(cameraPos); + } + + public static ObjFrameSettings rotation(Vector3 rotation) { + return new ObjFrameSettings(rotation, null); + } + + public static ObjFrameSettings rotateSpeed(double rotateSpeed) { + return new ObjFrameSettings(null, rotateSpeed); + } + + public static ObjFrameSettings resetRotation() { + return new ObjFrameSettings(true); + } +} diff --git a/src/main/resources/fxml/osci-render.fxml b/src/main/resources/fxml/osci-render.fxml index 4741300..b2a7220 100644 --- a/src/main/resources/fxml/osci-render.fxml +++ b/src/main/resources/fxml/osci-render.fxml @@ -12,17 +12,17 @@ - + - +