kopia lustrzana https://github.com/jameshball/osci-render
commit
d0f0c19e64
61
README.md
61
README.md
|
@ -6,7 +6,7 @@ Program for drawing objects, text, and images on an oscilloscope using audio out
|
||||||
|
|
||||||
This allows for 3D rendering of `.obj` files, `.svg` images, and `.txt` files.
|
This allows for 3D rendering of `.obj` files, `.svg` images, and `.txt` files.
|
||||||
|
|
||||||
Lots of this code was built as part of a 24hr hackathon: IC Hack 20. The original repository can be found here: https://github.com/wdhg/ICHack20 It won 'Best Newcomers Prize' at the event.
|
Some of this was built as part of a 24hr hackathon: IC Hack 20. The original repository can be found here: https://github.com/wdhg/ICHack20 It won 'Best Newcomers Prize' at the event.
|
||||||
|
|
||||||
### Video Demonstration
|
### Video Demonstration
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ Lots of this code was built as part of a 24hr hackathon: IC Hack 20. The origina
|
||||||
- Render `.svg` files
|
- Render `.svg` files
|
||||||
- Render text
|
- Render text
|
||||||
- Rotation of objects
|
- Rotation of objects
|
||||||
- Scaling image
|
- Scaling images
|
||||||
- Translating image
|
- Translating images
|
||||||
- GUI for controlling renderer
|
- Applying image effects
|
||||||
|
|
||||||
## Proposed Features
|
## Proposed Features
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ Lots of this code was built as part of a 24hr hackathon: IC Hack 20. The origina
|
||||||
|
|
||||||
Using osci-render is very easy; run the program and choose the file you would like to render, and it will output as audio to visualise on your oscilloscope.
|
Using osci-render is very easy; run the program and choose the file you would like to render, and it will output as audio to visualise on your oscilloscope.
|
||||||
|
|
||||||
By default, the program loads the `cube.obj` example. If this is working, you're good to go and should be able to load your own objects, files, or images!
|
By default, the program loads the example rotating cube. If this is working, you're good to go and should be able to load your own objects, files, or images!
|
||||||
|
|
||||||
Control the output using the sliders and text boxes provided. Currently the following can be controlled:
|
Control the output using the sliders and text boxes provided. Currently the following can be controlled:
|
||||||
|
|
||||||
|
@ -48,9 +48,58 @@ There are some additional controls for `.obj` files:
|
||||||
- Focal length of camera
|
- Focal length of camera
|
||||||
- Position of camera
|
- Position of camera
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
<img width="250px" height="396px" src="gui.png">
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Head over to [Releases](https://github.com/jameshball/osci-render/releases) and download the latest `.exe` or `.jar`.
|
||||||
|
|
||||||
|
`.exe` is highly recommended if you're on Windows as it is much simpler to get up and running.
|
||||||
|
|
||||||
|
### Running using .exe
|
||||||
|
|
||||||
|
- Download the latest `osci-render-VERSION.exe` from [Releases](https://github.com/jameshball/osci-render/releases)
|
||||||
|
- Open the `.exe` skipping any Windows security warnings (at your own risk!)
|
||||||
|
- It should open briefly and then close without any user input
|
||||||
|
- Check your start menu for `osci-render` or open `osci-render.exe` at `C:\Program Files\osci-render`
|
||||||
|
- Start rendering!
|
||||||
|
|
||||||
|
Updating to later versions is as simple as running the latest `osci-render-VERSION.exe` again.
|
||||||
|
|
||||||
|
To uninstall, use Windows control panel, as you would expect.
|
||||||
|
|
||||||
|
### Running using .jar
|
||||||
|
|
||||||
|
- Download the latest `osci-render-VERSION.jar` from [Releases](https://github.com/jameshball/osci-render/releases)
|
||||||
|
- Download and install [Java 15 or later](https://www.oracle.com/java/technologies/javase-jdk16-downloads.html)
|
||||||
|
- Donwload and unpack [JavaFX 16 or later](https://gluonhq.com/products/javafx/)
|
||||||
|
- Make sure you scroll down to `Latest Release`
|
||||||
|
- Download the SDK for your platform
|
||||||
|
- Unpack the `.zip` at your root directory (e.g. `C:\javafx-sdk-16` for me on Windows)
|
||||||
|
- The `lib` subfolder should be located at `/javafx-sdk-16/lib`
|
||||||
|
- Run the following command from your terminal to run the `.jar`, substituting the correct paths
|
||||||
|
- `java --enable-preview --module-path /javafx-sdk-16/lib --add-modules=javafx.controls,javafx.fxml -jar "path/to/osci-render-VERSION.jar"`
|
||||||
|
- Start rendering!
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
All dependencies are specified in the `pom.xml` file. Cloning this repo and using IntelliJ with Maven should make building a painless process.
|
I am using Maven for dependency management and to package the program. Doing the following will setup the project. I highly recommend using IntelliJ.
|
||||||
|
|
||||||
|
- Download and install [Java 15 or later](https://www.oracle.com/java/technologies/javase-jdk16-downloads.html)
|
||||||
|
- Run `git clone git@github.com:jameshball/osci-render.git`
|
||||||
|
- `cd` into the `osci-render` directory
|
||||||
|
- Run `mvn package`
|
||||||
|
- Open the project in IntelliJ
|
||||||
|
- Add `target/modules` as a library
|
||||||
|
- Right click the folder in IntelliJ and `Add as Library...`
|
||||||
|
- Select `osci-render` in the `Add to module` dropdown
|
||||||
|
- Navigate to `File -> Project Structure`
|
||||||
|
- Remove all external Maven libraries other than the `modules` folder just added
|
||||||
|
- You're good to go!
|
||||||
|
|
||||||
|
You should now be able to run `sh.ball.gui.Gui` and start the program 😊
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 28 KiB |
2
pom.xml
2
pom.xml
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<groupId>sh.ball</groupId>
|
<groupId>sh.ball</groupId>
|
||||||
<artifactId>osci-render</artifactId>
|
<artifactId>osci-render</artifactId>
|
||||||
<version>1.1.1</version>
|
<version>1.1.2</version>
|
||||||
|
|
||||||
<name>osci-render</name>
|
<name>osci-render</name>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package sh.ball.audio;
|
package sh.ball.audio;
|
||||||
|
|
||||||
import sh.ball.audio.effect.Effect;
|
import sh.ball.audio.effect.Effect;
|
||||||
|
import xt.audio.*;
|
||||||
import xt.audio.Enums.XtSample;
|
import xt.audio.Enums.XtSample;
|
||||||
import xt.audio.Enums.XtSetup;
|
import xt.audio.Enums.XtSetup;
|
||||||
import xt.audio.Enums.XtSystem;
|
import xt.audio.Enums.XtSystem;
|
||||||
|
@ -11,12 +12,6 @@ import xt.audio.Structs.XtDeviceStreamParams;
|
||||||
import xt.audio.Structs.XtFormat;
|
import xt.audio.Structs.XtFormat;
|
||||||
import xt.audio.Structs.XtMix;
|
import xt.audio.Structs.XtMix;
|
||||||
import xt.audio.Structs.XtStreamParams;
|
import xt.audio.Structs.XtStreamParams;
|
||||||
import xt.audio.XtAudio;
|
|
||||||
import xt.audio.XtDevice;
|
|
||||||
import xt.audio.XtPlatform;
|
|
||||||
import xt.audio.XtSafeBuffer;
|
|
||||||
import xt.audio.XtService;
|
|
||||||
import xt.audio.XtStream;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -30,38 +25,24 @@ import java.util.List;
|
||||||
|
|
||||||
public class AudioPlayer implements Renderer<List<Shape>> {
|
public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
|
|
||||||
private static final int SAMPLE_RATE = 192000;
|
|
||||||
private static final int BUFFER_SIZE = 20;
|
private static final int BUFFER_SIZE = 20;
|
||||||
|
|
||||||
private final XtMix MIX = new XtMix(SAMPLE_RATE, XtSample.FLOAT32);
|
private final XtFormat format;
|
||||||
private final XtChannels CHANNELS = new XtChannels(0, 0, 2, 0);
|
|
||||||
private final XtFormat FORMAT = new XtFormat(MIX, CHANNELS);
|
|
||||||
private final BlockingQueue<List<Shape>> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE);
|
private final BlockingQueue<List<Shape>> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE);
|
||||||
|
private final Map<Object, Effect> effects = new HashMap<>();
|
||||||
|
|
||||||
private Map<Object, Effect> effects = new HashMap<>();
|
|
||||||
private List<Shape> frame;
|
private List<Shape> frame;
|
||||||
private int currentShape = 0;
|
private int currentShape = 0;
|
||||||
private int audioFramesDrawn = 0;
|
private int audioFramesDrawn = 0;
|
||||||
|
|
||||||
private double translateSpeed = 0;
|
|
||||||
private Vector2 translateVector = new Vector2();
|
|
||||||
private final Phase translatePhase = new Phase();
|
|
||||||
private double rotateSpeed = 0;
|
|
||||||
private final Phase rotatePhase = new Phase();
|
|
||||||
private double scale = 1;
|
|
||||||
private double weight = Shape.DEFAULT_WEIGHT;
|
private double weight = Shape.DEFAULT_WEIGHT;
|
||||||
|
|
||||||
private volatile boolean stopped;
|
private volatile boolean stopped;
|
||||||
|
|
||||||
public AudioPlayer() {
|
public AudioPlayer(int sampleRate) {
|
||||||
}
|
XtMix mix = new XtMix(sampleRate, XtSample.FLOAT32);
|
||||||
|
XtChannels channels = new XtChannels(0, 0, 2, 0);
|
||||||
public AudioPlayer(double rotateSpeed, double translateSpeed, Vector2 translateVector, double scale, double weight) {
|
this.format = new XtFormat(mix, channels);
|
||||||
setRotationSpeed(rotateSpeed);
|
|
||||||
setTranslationSpeed(translateSpeed);
|
|
||||||
setTranslation(translateVector);
|
|
||||||
setScale(scale);
|
|
||||||
setQuality(weight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int render(XtStream stream, XtBuffer buffer, Object user) throws InterruptedException {
|
private int render(XtStream stream, XtBuffer buffer, Object user) throws InterruptedException {
|
||||||
|
@ -73,16 +54,13 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
Shape shape = getCurrentShape();
|
Shape shape = getCurrentShape();
|
||||||
|
|
||||||
shape = shape.setWeight(weight);
|
shape = shape.setWeight(weight);
|
||||||
shape = scale(shape);
|
|
||||||
shape = rotate(shape, FORMAT.mix.rate);
|
|
||||||
shape = translate(shape, FORMAT.mix.rate);
|
|
||||||
|
|
||||||
double totalAudioFrames = shape.getWeight() * shape.getLength();
|
double totalAudioFrames = shape.getWeight() * shape.getLength();
|
||||||
double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames;
|
double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames;
|
||||||
Vector2 nextVector = applyEffects(f, shape.nextVector(drawingProgress));
|
Vector2 nextVector = applyEffects(f, shape.nextVector(drawingProgress));
|
||||||
|
|
||||||
output[f * FORMAT.channels.outputs] = (float) nextVector.getX();
|
output[f * format.channels.outputs] = (float) nextVector.getX();
|
||||||
output[f * FORMAT.channels.outputs + 1] = (float) nextVector.getY();
|
output[f * format.channels.outputs + 1] = (float) nextVector.getY();
|
||||||
|
|
||||||
audioFramesDrawn++;
|
audioFramesDrawn++;
|
||||||
|
|
||||||
|
@ -107,60 +85,6 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Shape rotate(Shape shape, double sampleRate) {
|
|
||||||
if (rotateSpeed != 0) {
|
|
||||||
shape = shape.rotate(
|
|
||||||
nextTheta(sampleRate, rotateSpeed, translatePhase)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Shape translate(Shape shape, double sampleRate) {
|
|
||||||
if (translateSpeed != 0 && !translateVector.equals(new Vector2())) {
|
|
||||||
return shape.translate(translateVector.scale(
|
|
||||||
Math.sin(nextTheta(sampleRate, translateSpeed, rotatePhase))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double nextTheta(double sampleRate, double frequency, Phase phase) {
|
|
||||||
phase.value += frequency / sampleRate;
|
|
||||||
|
|
||||||
if (phase.value >= 1.0) {
|
|
||||||
phase.value = -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return phase.value * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Shape scale(Shape shape) {
|
|
||||||
if (scale != 1) {
|
|
||||||
return shape.scale(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRotationSpeed(double speed) {
|
|
||||||
this.rotateSpeed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTranslation(Vector2 translation) {
|
|
||||||
this.translateVector = translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTranslationSpeed(double speed) {
|
|
||||||
translateSpeed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScale(double scale) {
|
|
||||||
this.scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setQuality(double quality) {
|
public void setQuality(double quality) {
|
||||||
this.weight = quality;
|
this.weight = quality;
|
||||||
|
@ -191,11 +115,11 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
if (defaultOutput == null) return;
|
if (defaultOutput == null) return;
|
||||||
|
|
||||||
try (XtDevice device = service.openDevice(defaultOutput)) {
|
try (XtDevice device = service.openDevice(defaultOutput)) {
|
||||||
if (device.supportsFormat(FORMAT)) {
|
if (device.supportsFormat(format)) {
|
||||||
|
|
||||||
XtBufferSize size = device.getBufferSize(FORMAT);
|
XtBufferSize size = device.getBufferSize(format);
|
||||||
XtStreamParams streamParams = new XtStreamParams(true, this::render, null, null);
|
XtStreamParams streamParams = new XtStreamParams(true, this::render, null, null);
|
||||||
XtDeviceStreamParams deviceParams = new XtDeviceStreamParams(streamParams, FORMAT, size.current);
|
XtDeviceStreamParams deviceParams = new XtDeviceStreamParams(streamParams, format, size.current);
|
||||||
try (XtStream stream = device.openStream(deviceParams, null);
|
try (XtStream stream = device.openStream(deviceParams, null);
|
||||||
XtSafeBuffer safe = XtSafeBuffer.register(stream, true)) {
|
XtSafeBuffer safe = XtSafeBuffer.register(stream, true)) {
|
||||||
stream.start();
|
stream.start();
|
||||||
|
@ -239,9 +163,4 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
effects.remove(identifier);
|
effects.remove(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class Phase {
|
|
||||||
|
|
||||||
private double value = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,8 @@ package sh.ball.audio.effect;
|
||||||
|
|
||||||
public enum EffectType {
|
public enum EffectType {
|
||||||
VECTOR_CANCELLING,
|
VECTOR_CANCELLING,
|
||||||
BIT_CRUSH
|
BIT_CRUSH,
|
||||||
|
SCALE,
|
||||||
|
ROTATE,
|
||||||
|
TRANSLATE
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
public abstract class PhaseEffect implements Effect {
|
||||||
|
|
||||||
|
protected final int sampleRate;
|
||||||
|
|
||||||
|
protected double speed;
|
||||||
|
private double phase;
|
||||||
|
|
||||||
|
protected PhaseEffect(int sampleRate, double speed) {
|
||||||
|
this.sampleRate = sampleRate;
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double nextTheta() {
|
||||||
|
phase += speed / sampleRate;
|
||||||
|
|
||||||
|
if (phase >= 1.0) {
|
||||||
|
phase = -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return phase * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(double speed) {
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
|
public class RotateEffect extends PhaseEffect {
|
||||||
|
|
||||||
|
public RotateEffect(int sampleRate, double speed) {
|
||||||
|
super(sampleRate, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RotateEffect(int sampleRate) {
|
||||||
|
this(sampleRate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2 apply(int count, Vector2 vector) {
|
||||||
|
if (speed != 0) {
|
||||||
|
return vector.rotate(nextTheta());
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
|
public class ScaleEffect implements Effect {
|
||||||
|
|
||||||
|
private double scale;
|
||||||
|
|
||||||
|
public ScaleEffect(double scale) {
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScaleEffect() {
|
||||||
|
this(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScale(double scale) {
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2 apply(int count, Vector2 vector) {
|
||||||
|
return vector.scale(scale);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
|
public class TranslateEffect extends PhaseEffect {
|
||||||
|
|
||||||
|
private Vector2 translation;
|
||||||
|
|
||||||
|
public TranslateEffect(int sampleRate, double speed, Vector2 translation) {
|
||||||
|
super(sampleRate, speed);
|
||||||
|
this.translation = translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranslateEffect(int sampleRate) {
|
||||||
|
this(sampleRate, 0, new Vector2());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2 apply(int count, Vector2 vector) {
|
||||||
|
if (speed != 0 && !translation.equals(new Vector2())) {
|
||||||
|
return vector.translate(translation.scale(Math.sin(nextTheta())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTranslation(Vector2 translation) {
|
||||||
|
this.translation = translation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package sh.ball.gui;
|
package sh.ball.gui;
|
||||||
|
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import sh.ball.audio.AudioPlayer;
|
import sh.ball.audio.Renderer;
|
||||||
import sh.ball.audio.effect.Effect;
|
import sh.ball.audio.effect.*;
|
||||||
import sh.ball.audio.FrameProducer;
|
import sh.ball.audio.FrameProducer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -24,8 +24,6 @@ import javafx.stage.Stage;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import sh.ball.audio.effect.EffectType;
|
|
||||||
import sh.ball.audio.effect.EventFactory;
|
|
||||||
import sh.ball.engine.Vector3;
|
import sh.ball.engine.Vector3;
|
||||||
import sh.ball.parser.obj.ObjFrameSettings;
|
import sh.ball.parser.obj.ObjFrameSettings;
|
||||||
import sh.ball.parser.obj.ObjParser;
|
import sh.ball.parser.obj.ObjParser;
|
||||||
|
@ -35,17 +33,18 @@ import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
public class Controller implements Initializable {
|
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 InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
|
||||||
|
|
||||||
private final FileChooser fileChooser = new FileChooser();
|
private final FileChooser fileChooser = new FileChooser();
|
||||||
// TODO: Reduce coupling on AudioPlayer
|
private final Renderer<List<Shape>> renderer;
|
||||||
private final AudioPlayer renderer = new AudioPlayer();
|
|
||||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
private FrameProducer<List<Shape>> producer = new FrameProducer<>(
|
private final RotateEffect rotateEffect = new RotateEffect(SAMPLE_RATE);
|
||||||
renderer,
|
private final TranslateEffect translateEffect = new TranslateEffect(SAMPLE_RATE);
|
||||||
new ObjParser(DEFAULT_OBJ).parse()
|
private final ScaleEffect scaleEffect = new ScaleEffect();
|
||||||
);
|
|
||||||
|
private FrameProducer<List<Shape>> producer;
|
||||||
|
|
||||||
private Stage stage;
|
private Stage stage;
|
||||||
|
|
||||||
|
@ -94,7 +93,12 @@ public class Controller implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
private Slider bitCrushSlider;
|
private Slider bitCrushSlider;
|
||||||
|
|
||||||
public Controller() throws IOException {
|
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() {
|
private Map<Slider, SliderUpdater<Double>> initializeSliderMap() {
|
||||||
|
@ -102,11 +106,11 @@ public class Controller implements Initializable {
|
||||||
weightSlider,
|
weightSlider,
|
||||||
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
|
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
|
||||||
rotateSpeedSlider,
|
rotateSpeedSlider,
|
||||||
new SliderUpdater<>(rotateSpeedLabel::setText, renderer::setRotationSpeed),
|
new SliderUpdater<>(rotateSpeedLabel::setText, rotateEffect::setSpeed),
|
||||||
translationSpeedSlider,
|
translationSpeedSlider,
|
||||||
new SliderUpdater<>(translationSpeedLabel::setText, renderer::setTranslationSpeed),
|
new SliderUpdater<>(translationSpeedLabel::setText, translateEffect::setSpeed),
|
||||||
scaleSlider,
|
scaleSlider,
|
||||||
new SliderUpdater<>(scaleLabel::setText, renderer::setScale),
|
new SliderUpdater<>(scaleLabel::setText, scaleEffect::setScale),
|
||||||
focalLengthSlider,
|
focalLengthSlider,
|
||||||
new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength)
|
new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength)
|
||||||
);
|
);
|
||||||
|
@ -135,7 +139,7 @@ public class Controller implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
InvalidationListener translationUpdate = observable ->
|
InvalidationListener translationUpdate = observable ->
|
||||||
renderer.setTranslation(new Vector2(
|
translateEffect.setTranslation(new Vector2(
|
||||||
tryParse(translationXTextField.getText()),
|
tryParse(translationXTextField.getText()),
|
||||||
tryParse(translationYTextField.getText())
|
tryParse(translationYTextField.getText())
|
||||||
));
|
));
|
||||||
|
@ -150,7 +154,6 @@ public class Controller implements Initializable {
|
||||||
tryParse(cameraZTextField.getText())
|
tryParse(cameraZTextField.getText())
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
||||||
cameraXTextField.textProperty().addListener(cameraPosUpdate);
|
cameraXTextField.textProperty().addListener(cameraPosUpdate);
|
||||||
cameraYTextField.textProperty().addListener(cameraPosUpdate);
|
cameraYTextField.textProperty().addListener(cameraPosUpdate);
|
||||||
cameraZTextField.textProperty().addListener(cameraPosUpdate);
|
cameraZTextField.textProperty().addListener(cameraPosUpdate);
|
||||||
|
@ -175,6 +178,10 @@ public class Controller implements Initializable {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
renderer.addEffect(EffectType.SCALE, scaleEffect);
|
||||||
|
renderer.addEffect(EffectType.ROTATE, rotateEffect);
|
||||||
|
renderer.addEffect(EffectType.TRANSLATE, translateEffect);
|
||||||
|
|
||||||
executor.submit(producer);
|
executor.submit(producer);
|
||||||
new Thread(renderer).start();
|
new Thread(renderer).start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,20 @@ import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import sh.ball.audio.AudioPlayer;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Gui extends Application {
|
public class Gui extends Application {
|
||||||
|
|
||||||
|
private static final int SAMPLE_RATE = 192000;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws Exception {
|
public void start(Stage stage) throws Exception {
|
||||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/osci-render.fxml"));
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/osci-render.fxml"));
|
||||||
|
Controller controller = new Controller(new AudioPlayer(SAMPLE_RATE));
|
||||||
|
loader.setController(controller);
|
||||||
Parent root = loader.load();
|
Parent root = loader.load();
|
||||||
Controller controller = loader.getController();
|
|
||||||
controller.setStage(stage);
|
|
||||||
|
|
||||||
stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/icons/icon.png"))));
|
stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/icons/icon.png"))));
|
||||||
stage.setTitle("osci-render");
|
stage.setTitle("osci-render");
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<?import javafx.scene.layout.GridPane?>
|
<?import javafx.scene.layout.GridPane?>
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
|
||||||
<GridPane alignment="center" hgap="10" prefWidth="400.0" vgap="10" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sh.ball.gui.Controller">
|
<GridPane alignment="center" hgap="10" prefWidth="400.0" vgap="10" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints />
|
<ColumnConstraints />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
|
@ -26,9 +26,9 @@
|
||||||
<Slider fx:id="rotateSpeedSlider" layoutX="116.0" layoutY="79.0" max="10.0" prefHeight="14.0" prefWidth="198.0" />
|
<Slider fx:id="rotateSpeedSlider" layoutX="116.0" layoutY="79.0" max="10.0" prefHeight="14.0" prefWidth="198.0" />
|
||||||
<Label layoutX="37.0" layoutY="77.0" text="Rotate speed" />
|
<Label layoutX="37.0" layoutY="77.0" text="Rotate speed" />
|
||||||
<Label fx:id="rotateSpeedLabel" layoutX="316.0" layoutY="77.0" maxWidth="40.0" text="0" />
|
<Label fx:id="rotateSpeedLabel" layoutX="316.0" layoutY="77.0" maxWidth="40.0" text="0" />
|
||||||
<Slider fx:id="translationSpeedSlider" layoutX="116.0" layoutY="105.0" max="10.0" prefHeight="14.0" prefWidth="198.0" value="1.0" />
|
<Slider fx:id="translationSpeedSlider" layoutX="116.0" layoutY="105.0" max="10.0" prefHeight="14.0" prefWidth="198.0" />
|
||||||
<Label layoutX="14.0" layoutY="103.0" text="Translation speed" />
|
<Label layoutX="14.0" layoutY="103.0" text="Translation speed" />
|
||||||
<Label fx:id="translationSpeedLabel" layoutX="316.0" layoutY="103.0" maxWidth="40.0" text="1" />
|
<Label fx:id="translationSpeedLabel" layoutX="316.0" layoutY="103.0" maxWidth="40.0" text="0" />
|
||||||
<Slider fx:id="scaleSlider" layoutX="116.0" layoutY="130.0" max="10.0" prefHeight="14.0" prefWidth="198.0" value="1.0" />
|
<Slider fx:id="scaleSlider" layoutX="116.0" layoutY="130.0" max="10.0" prefHeight="14.0" prefWidth="198.0" value="1.0" />
|
||||||
<Label layoutX="80.0" layoutY="128.0" text="Scale" />
|
<Label layoutX="80.0" layoutY="128.0" text="Scale" />
|
||||||
<Label fx:id="scaleLabel" layoutX="316.0" layoutY="128.0" maxWidth="40.0" text="1" />
|
<Label fx:id="scaleLabel" layoutX="316.0" layoutY="128.0" maxWidth="40.0" text="1" />
|
||||||
|
|
Ładowanie…
Reference in New Issue