kopia lustrzana https://github.com/jameshball/osci-render
Focal length, rotation, and rotation speed sliders all functional, and add Effect initialisable with a lambda
rodzic
a487306784
commit
e2f9a6c4a4
|
@ -5,7 +5,7 @@ EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p) : audioProcessor
|
|||
setText("Audio Effects");
|
||||
|
||||
addAndMakeVisible(frequency);
|
||||
frequency.setHideCheckbox(true);
|
||||
frequency.setCheckboxVisible(false);
|
||||
|
||||
frequency.slider.setSkewFactorFromMidPoint(500.0);
|
||||
frequency.slider.setTextValueSuffix("Hz");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "ObjComponent.h"
|
||||
#include "PluginEditor.h"
|
||||
#include <numbers>
|
||||
|
||||
ObjComponent::ObjComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor) {
|
||||
setText("3D .obj File Settings");
|
||||
|
@ -9,6 +10,31 @@ ObjComponent::ObjComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessor
|
|||
addAndMakeVisible(rotateY);
|
||||
addAndMakeVisible(rotateZ);
|
||||
addAndMakeVisible(rotateSpeed);
|
||||
|
||||
focalLength.slider.onValueChange = [this] {
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
|
||||
audioProcessor.focalLength.setValue(focalLength.slider.getValue());
|
||||
audioProcessor.focalLength.apply();
|
||||
};
|
||||
|
||||
auto onRotationChange = [this]() {
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
|
||||
audioProcessor.rotateX.setValue(rotateX.slider.getValue());
|
||||
audioProcessor.rotateY.setValue(rotateY.slider.getValue());
|
||||
audioProcessor.rotateZ.setValue(rotateZ.slider.getValue());
|
||||
// all the rotate apply functions are the same
|
||||
audioProcessor.rotateX.apply();
|
||||
};
|
||||
|
||||
rotateX.slider.onValueChange = onRotationChange;
|
||||
rotateY.slider.onValueChange = onRotationChange;
|
||||
rotateZ.slider.onValueChange = onRotationChange;
|
||||
|
||||
rotateSpeed.slider.onValueChange = [this] {
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
|
||||
audioProcessor.rotateSpeed.setValue(rotateSpeed.slider.getValue());
|
||||
audioProcessor.rotateSpeed.apply();
|
||||
};
|
||||
}
|
||||
|
||||
void ObjComponent::resized() {
|
||||
|
|
|
@ -166,12 +166,18 @@ void OscirenderAudioProcessor::addLuaSlider() {
|
|||
}
|
||||
|
||||
void OscirenderAudioProcessor::updateLuaValues() {
|
||||
Vector2 vector;
|
||||
for (auto& effect : luaEffects) {
|
||||
effect->apply(0, vector);
|
||||
effect->apply();
|
||||
}
|
||||
}
|
||||
|
||||
// parsersLock should be held when calling this
|
||||
void OscirenderAudioProcessor::updateObjValues() {
|
||||
focalLength.apply();
|
||||
rotateX.apply();
|
||||
rotateSpeed.apply();
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessor::updateAngleDelta() {
|
||||
auto cyclesPerSample = frequency / currentSampleRate;
|
||||
thetaDelta = cyclesPerSample * 2.0 * juce::MathConstants<double>::pi;
|
||||
|
@ -269,6 +275,7 @@ void OscirenderAudioProcessor::openFile(int index) {
|
|||
currentFile = index;
|
||||
invalidateFrameBuffer = true;
|
||||
updateLuaValues();
|
||||
updateObjValues();
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessor::changeCurrentFile(int index) {
|
||||
|
@ -417,6 +424,10 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
|
|||
|
||||
x = channels.x;
|
||||
y = channels.y;
|
||||
|
||||
// clip to -1.0 to 1.0
|
||||
x = std::max(-1.0, std::min(1.0, x));
|
||||
y = std::max(-1.0, std::min(1.0, y));
|
||||
|
||||
|
||||
if (totalNumOutputChannels >= 2) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "parser/FrameProducer.h"
|
||||
#include "parser/FrameConsumer.h"
|
||||
#include "audio/Effect.h"
|
||||
#include <numbers>
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
@ -71,12 +72,50 @@ public:
|
|||
std::shared_ptr<std::vector<std::shared_ptr<Effect>>> enabledEffects = std::make_shared<std::vector<std::shared_ptr<Effect>>>();
|
||||
|
||||
std::vector<std::shared_ptr<Effect>> luaEffects;
|
||||
|
||||
// TODO see if there is a way to move this code to .cpp
|
||||
std::function<Vector2(int, Vector2, double, double, int)> onRotationChange = [this](int index, Vector2 input, double value, double frequency, double sampleRate) {
|
||||
if (getCurrentFileIndex() != -1) {
|
||||
auto obj = getCurrentFileParser()->getObject();
|
||||
if (obj == nullptr) return input;
|
||||
obj->setBaseRotation(
|
||||
rotateX.getValue() * std::numbers::pi,
|
||||
rotateY.getValue() * std::numbers::pi,
|
||||
rotateZ.getValue() * std::numbers::pi
|
||||
);
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
Effect focalLength{"Focal length", "focalLength", 1};
|
||||
Effect rotateX{"Rotate x", "rotateX", 0};
|
||||
Effect rotateY{"Rotate y", "rotateY", 0};
|
||||
Effect rotateZ{"Rotate z", "rotateZ", 0};
|
||||
Effect rotateSpeed{"Rotate speed", "rotateSpeed", 0};
|
||||
Effect focalLength{
|
||||
[this](int index, Vector2 input, double value, double frequency, double sampleRate) {
|
||||
if (getCurrentFileIndex() != -1) {
|
||||
auto camera = getCurrentFileParser()->getCamera();
|
||||
if (camera == nullptr) return input;
|
||||
camera->setFocalLength(value);
|
||||
}
|
||||
return input;
|
||||
},
|
||||
"Focal length",
|
||||
"focalLength",
|
||||
1
|
||||
};
|
||||
Effect rotateX{onRotationChange, "Rotate x", "rotateX", 1};
|
||||
Effect rotateY{onRotationChange, "Rotate y", "rotateY", 1};
|
||||
Effect rotateZ{onRotationChange, "Rotate z", "rotateZ", 0};
|
||||
Effect rotateSpeed{
|
||||
[this](int index, Vector2 input, double value, double frequency, double sampleRate) {
|
||||
if (getCurrentFileIndex() != -1) {
|
||||
auto obj = getCurrentFileParser()->getObject();
|
||||
if (obj == nullptr) return input;
|
||||
obj->setRotationSpeed(value);
|
||||
}
|
||||
return input;
|
||||
},
|
||||
"Rotate speed",
|
||||
"rotateSpeed",
|
||||
0
|
||||
};
|
||||
|
||||
juce::SpinLock parsersLock;
|
||||
std::vector<std::shared_ptr<FileParser>> parsers;
|
||||
|
@ -87,7 +126,6 @@ public:
|
|||
std::unique_ptr<FrameProducer> producer;
|
||||
|
||||
void addLuaSlider();
|
||||
void updateLuaValues();
|
||||
void updateAngleDelta();
|
||||
void addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) override;
|
||||
void enableEffect(std::shared_ptr<Effect> effect);
|
||||
|
@ -123,6 +161,8 @@ private:
|
|||
void updateFrame();
|
||||
void updateLengthIncrement();
|
||||
void openFile(int index);
|
||||
void updateLuaValues();
|
||||
void updateObjValues();
|
||||
|
||||
const double MIN_LENGTH_INCREMENT = 0.000001;
|
||||
|
||||
|
|
|
@ -8,11 +8,21 @@ Effect::Effect(juce::String name, juce::String id) : name(name), id(id) {}
|
|||
|
||||
Effect::Effect(juce::String name, juce::String id, double value) : name(name), id(id), value(value) {}
|
||||
|
||||
Effect::Effect(std::function<Vector2(int, Vector2, double, double, int)> application, juce::String name, juce::String id, double value) : Effect(name, id, value) {
|
||||
this->application = application;
|
||||
};
|
||||
|
||||
Vector2 Effect::apply(int index, Vector2 input) {
|
||||
if (effectApplication == nullptr) {
|
||||
return input;
|
||||
if (application) {
|
||||
return application(index, input, value, frequency, sampleRate);
|
||||
} else if (effectApplication != nullptr) {
|
||||
return effectApplication->apply(index, input, value, frequency, sampleRate);
|
||||
}
|
||||
return effectApplication->apply(index, input, value, frequency, sampleRate);
|
||||
return input;
|
||||
}
|
||||
|
||||
void Effect::apply() {
|
||||
apply(0, Vector2());
|
||||
}
|
||||
|
||||
double Effect::getValue() {
|
||||
|
|
|
@ -8,8 +8,10 @@ public:
|
|||
Effect(std::unique_ptr<EffectApplication> effectApplication, juce::String name, juce::String id);
|
||||
Effect(juce::String name, juce::String id);
|
||||
Effect(juce::String name, juce::String id, double value);
|
||||
Effect(std::function<Vector2(int, Vector2, double, double, int)> application, juce::String name, juce::String id, double value);
|
||||
|
||||
Vector2 apply(int index, Vector2 input);
|
||||
void apply();
|
||||
double getValue();
|
||||
void setValue(double value);
|
||||
void setFrequency(double frequency);
|
||||
|
@ -24,6 +26,7 @@ private:
|
|||
int sampleRate = 192000;
|
||||
juce::String name;
|
||||
juce::String id;
|
||||
std::function<Vector2(int, Vector2, double, double, int)> application;
|
||||
|
||||
std::unique_ptr<EffectApplication> effectApplication;
|
||||
};
|
|
@ -12,8 +12,8 @@ EffectComponent::EffectComponent(double min, double max, double step, Effect& ef
|
|||
slider.setValue(effect.getValue(), juce::dontSendNotification);
|
||||
}
|
||||
|
||||
EffectComponent::EffectComponent(double min, double max, double step, Effect& effect, bool hideCheckbox) : EffectComponent(min, max, step, effect) {
|
||||
setHideCheckbox(hideCheckbox);
|
||||
EffectComponent::EffectComponent(double min, double max, double step, Effect& effect, bool checkboxVisible) : EffectComponent(min, max, step, effect) {
|
||||
setCheckboxVisible(checkboxVisible);
|
||||
}
|
||||
|
||||
void EffectComponent::componentSetup() {
|
||||
|
@ -34,11 +34,11 @@ void EffectComponent::resized() {
|
|||
auto bounds = getLocalBounds();
|
||||
bounds.removeFromRight(10);
|
||||
slider.setBounds(bounds.removeFromRight(sliderRight));
|
||||
if (hideCheckbox) {
|
||||
bounds.removeFromLeft(5);
|
||||
} else {
|
||||
if (checkboxVisible) {
|
||||
bounds.removeFromLeft(2);
|
||||
selected.setBounds(bounds.removeFromLeft(25));
|
||||
} else {
|
||||
bounds.removeFromLeft(5);
|
||||
}
|
||||
textBounds = bounds;
|
||||
}
|
||||
|
@ -51,6 +51,6 @@ void EffectComponent::paint(juce::Graphics& g) {
|
|||
g.drawText(name, textBounds, juce::Justification::left);
|
||||
}
|
||||
|
||||
void EffectComponent::setHideCheckbox(bool hide) {
|
||||
hideCheckbox = hide;
|
||||
void EffectComponent::setCheckboxVisible(bool visible) {
|
||||
checkboxVisible = visible;
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ class EffectComponent : public juce::Component {
|
|||
public:
|
||||
EffectComponent(double min, double max, double step, double value, juce::String name, juce::String id);
|
||||
EffectComponent(double min, double max, double step, Effect& effect);
|
||||
EffectComponent(double min, double max, double step, Effect& effect, bool hideCheckbox);
|
||||
EffectComponent(double min, double max, double step, Effect& effect, bool checkboxVisible);
|
||||
~EffectComponent();
|
||||
|
||||
void resized() override;
|
||||
void paint(juce::Graphics& g) override;
|
||||
|
||||
void setHideCheckbox(bool hide);
|
||||
void setCheckboxVisible(bool visible);
|
||||
|
||||
juce::Slider slider;
|
||||
juce::String id;
|
||||
|
@ -23,7 +23,7 @@ public:
|
|||
|
||||
private:
|
||||
void componentSetup();
|
||||
bool hideCheckbox = false;
|
||||
bool checkboxVisible = false;
|
||||
juce::Rectangle<int> textBounds;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectComponent)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
LuaListComponent::LuaListComponent(OscirenderAudioProcessor& p, Effect& effect) {
|
||||
effectComponent = std::make_shared<EffectComponent>(0.0, 1.0, 0.01, effect);
|
||||
effectComponent->setHideCheckbox(true);
|
||||
effectComponent->setCheckboxVisible(false);
|
||||
|
||||
effectComponent->slider.onValueChange = [this, &effect, &p] {
|
||||
effect.setValue(effectComponent->slider.getValue());
|
||||
|
|
|
@ -6,6 +6,7 @@ Camera::Camera(double focalLength, double x, double y, double z) : focalLength(f
|
|||
|
||||
std::vector<std::unique_ptr<Shape>> Camera::draw(WorldObject& object) {
|
||||
std::vector<std::unique_ptr<Shape>> shapes;
|
||||
object.nextFrame();
|
||||
for (auto& edge : object.edges) {
|
||||
Vector2 start = project(object.rotateX, object.rotateY, object.rotateZ, edge.x1, edge.y1, edge.z1);
|
||||
Vector2 end = project(object.rotateX, object.rotateY, object.rotateZ, edge.x2, edge.y2, edge.z2);
|
||||
|
@ -30,14 +31,14 @@ void Camera::findZPos(WorldObject& object) {
|
|||
}
|
||||
}
|
||||
|
||||
void Camera::setFocalLength(double focalLength) {
|
||||
this->focalLength = focalLength;
|
||||
}
|
||||
|
||||
std::vector<Vector2> Camera::sampleVerticesInRender(WorldObject& object) {
|
||||
double rotation = 2.0 * std::numbers::pi / SAMPLE_RENDER_SAMPLES;
|
||||
|
||||
std::vector<Vector2> vertices;
|
||||
|
||||
double oldRotateX = object.rotateX;
|
||||
double oldRotateY = object.rotateY;
|
||||
double oldRotateZ = object.rotateZ;
|
||||
|
||||
for (int i = 0; i < SAMPLE_RENDER_SAMPLES - 1; i++) {
|
||||
for (size_t j = 0; j < std::min(VERTEX_SAMPLES, object.numVertices); j++) {
|
||||
|
|
|
@ -11,6 +11,7 @@ public:
|
|||
|
||||
std::vector<std::unique_ptr<Shape>> draw(WorldObject& object);
|
||||
void findZPos(WorldObject& object);
|
||||
void setFocalLength(double focalLength);
|
||||
private:
|
||||
const double VERTEX_VALUE_THRESHOLD = 1.0;
|
||||
const double CAMERA_MOVE_INCREMENT = -0.1;
|
||||
|
@ -18,7 +19,7 @@ private:
|
|||
const int VERTEX_SAMPLES = 1000;
|
||||
const int MAX_NUM_STEPS = 1000;
|
||||
|
||||
double focalLength;
|
||||
std::atomic<double> focalLength;
|
||||
double x, y, z;
|
||||
|
||||
std::vector<Vector2> sampleVerticesInRender(WorldObject& object);
|
||||
|
|
|
@ -206,3 +206,33 @@ WorldObject::WorldObject(std::string obj_string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObject::setBaseRotation(double x, double y, double z) {
|
||||
baseRotateX = x;
|
||||
baseRotateY = y;
|
||||
baseRotateZ = z;
|
||||
}
|
||||
|
||||
void WorldObject::setRotationSpeed(double rotateSpeed) {
|
||||
this->rotateSpeed = linearSpeedToActualSpeed(rotateSpeed);
|
||||
}
|
||||
|
||||
// called whenever a new frame is drawn, so that the object can update its
|
||||
// rotation
|
||||
void WorldObject::nextFrame() {
|
||||
currentRotateX += baseRotateX * rotateSpeed;
|
||||
currentRotateY += baseRotateY * rotateSpeed;
|
||||
currentRotateZ += baseRotateZ * rotateSpeed;
|
||||
rotateX = baseRotateX + currentRotateX;
|
||||
rotateY = baseRotateY + currentRotateY;
|
||||
rotateZ = baseRotateZ + currentRotateZ;
|
||||
}
|
||||
|
||||
// this just makes the range of the speed more useful
|
||||
double WorldObject::linearSpeedToActualSpeed(double rotateSpeed) {
|
||||
double actualSpeed = (std::exp(3 * std::min(10.0, std::abs(rotateSpeed))) - 1) / 50000;
|
||||
if (rotateSpeed < 0) {
|
||||
actualSpeed *= -1;
|
||||
}
|
||||
return actualSpeed;
|
||||
}
|
||||
|
|
|
@ -6,10 +6,19 @@ class WorldObject {
|
|||
public:
|
||||
WorldObject(std::string);
|
||||
|
||||
double rotateX = 0.0, rotateY = 0.0, rotateZ = 0.0;
|
||||
void setBaseRotation(double x, double y, double z);
|
||||
void setRotationSpeed(double rotateSpeed);
|
||||
void nextFrame();
|
||||
|
||||
std::atomic<double> rotateX = 0.0, rotateY = 0.0, rotateZ = 0.0;
|
||||
|
||||
std::vector<Line3D> edges;
|
||||
std::vector<float> vs;
|
||||
int numVertices;
|
||||
private:
|
||||
double linearSpeedToActualSpeed(double rotateSpeed);
|
||||
|
||||
std::atomic<double> baseRotateX = 0.0, baseRotateY = 0.0, baseRotateZ = 0.0;
|
||||
std::atomic<double> currentRotateX = 0.0, currentRotateY = 0.0, currentRotateZ = 0.0;
|
||||
std::atomic<double> rotateSpeed;
|
||||
};
|
Ładowanie…
Reference in New Issue