@@ -0,0 +1,32 @@ | |||||
ARCH ?= lin | |||||
FLAGS += -fPIC -g -Wall -O3 -msse -mfpmath=sse -ffast-math \ | |||||
-I../../include | |||||
LDFLAGS += | |||||
ifeq ($(ARCH), lin) | |||||
LDFLAGS += -shared | |||||
TARGET = plugin.so | |||||
endif | |||||
ifeq ($(ARCH), mac) | |||||
LDFLAGS += -shared -undefined dynamic_lookup | |||||
TARGET = plugin.dylib | |||||
endif | |||||
ifeq ($(ARCH), win) | |||||
LDFLAGS += -shared -L../../ -lRack | |||||
TARGET = plugin.dll | |||||
endif | |||||
all: $(TARGET) | |||||
clean: | |||||
rm -rfv build $(TARGET) | |||||
include ../../Makefile.inc |
@@ -104,6 +104,11 @@ struct Panel : TransparentWidget { | |||||
// params | // params | ||||
//////////////////// | //////////////////// | ||||
struct CircularShadow : TransparentWidget { | |||||
float blur = 0.0; | |||||
void draw(NVGcontext *vg); | |||||
}; | |||||
struct Light : TransparentWidget { | struct Light : TransparentWidget { | ||||
NVGcolor color; | NVGcolor color; | ||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
@@ -137,17 +142,24 @@ struct SVGKnob : Knob, FramebufferWidget { | |||||
/** Not owned */ | /** Not owned */ | ||||
TransformWidget *tw; | TransformWidget *tw; | ||||
SVGWidget *sw; | SVGWidget *sw; | ||||
CircularShadow *shadow; | |||||
SVGKnob(); | SVGKnob(); | ||||
void setSVG(std::shared_ptr<SVG> svg); | void setSVG(std::shared_ptr<SVG> svg); | ||||
void step(); | void step(); | ||||
void draw(NVGcontext *vg); | |||||
void onChange(); | void onChange(); | ||||
}; | }; | ||||
struct Switch : ParamWidget, SpriteWidget { | struct Switch : ParamWidget, SpriteWidget { | ||||
}; | }; | ||||
struct SVGSwitch : ParamWidget, FramebufferWidget { | |||||
/** Not owned */ | |||||
TransformWidget *tw; | |||||
SVGWidget *swPressed; | |||||
SVGWidget *swReleased; | |||||
}; | |||||
struct ToggleSwitch : virtual Switch { | struct ToggleSwitch : virtual Switch { | ||||
void onDragStart() { | void onDragStart() { | ||||
index = 1; | index = 1; | ||||
@@ -360,7 +360,7 @@ struct CL1362Port : SpritePort { | |||||
}; | }; | ||||
//////////////////// | //////////////////// | ||||
// Lights | |||||
// LEDs | |||||
//////////////////// | //////////////////// | ||||
struct ValueLight : Light { | struct ValueLight : Light { | ||||
@@ -413,6 +413,20 @@ struct SmallLight : BASE { | |||||
} | } | ||||
}; | }; | ||||
//////////////////// | |||||
// Switches | |||||
//////////////////// | |||||
// struct BefacoBigKnob : { | |||||
// BefacoBigKnob() { | |||||
// box.size = Vec(75, 75); | |||||
// minAngle = -0.75*M_PI; | |||||
// maxAngle = 0.75*M_PI; | |||||
// setSVG(SVG::load("res/ComponentLibrary/BefacoBigKnob.svg")); | |||||
// } | |||||
// }; | |||||
//////////////////// | //////////////////// | ||||
// Misc | // Misc | ||||
//////////////////// | //////////////////// | ||||
@@ -186,28 +186,24 @@ struct SVGWidget : virtual Widget { | |||||
}; | }; | ||||
/** Caches a widget's draw() result to a framebuffer so it is called less frequently | /** Caches a widget's draw() result to a framebuffer so it is called less frequently | ||||
When `dirty` is true, `scene` will be re-rendered on the next call to step(). | |||||
When `dirty` is true, its children will be re-rendered on the next call to step(). | |||||
Events are not passed to the underlying scene. | Events are not passed to the underlying scene. | ||||
*/ | */ | ||||
struct FramebufferWidget : virtual Widget { | struct FramebufferWidget : virtual Widget { | ||||
/** Set this to true to re-render the scene to the framebuffer in the next step() */ | |||||
/** Set this to true to re-render the children to the framebuffer in the next step() */ | |||||
bool dirty = true; | bool dirty = true; | ||||
/** A margin in pixels around the scene in the framebuffer | |||||
/** A margin in pixels around the children in the framebuffer | |||||
This prevents cutting the rendered SVG off on the box edges. | This prevents cutting the rendered SVG off on the box edges. | ||||
*/ | */ | ||||
int margin = 0; | int margin = 0; | ||||
/** The root object in the framebuffer scene | /** The root object in the framebuffer scene | ||||
The FramebufferWidget owns the pointer | The FramebufferWidget owns the pointer | ||||
*/ | */ | ||||
Widget *scene = NULL; | |||||
struct Internal; | struct Internal; | ||||
Internal *internal; | Internal *internal; | ||||
FramebufferWidget(); | FramebufferWidget(); | ||||
~FramebufferWidget(); | ~FramebufferWidget(); | ||||
void setScene(Widget *w) { | |||||
scene = w; | |||||
} | |||||
void step(); | void step(); | ||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
}; | }; | ||||
@@ -0,0 +1,21 @@ | |||||
#include "app.hpp" | |||||
namespace rack { | |||||
void CircularShadow::draw(NVGcontext *vg) { | |||||
nvgBeginPath(vg); | |||||
nvgRect(vg, -blur, -blur, box.size.x + 2*blur, box.size.y + 2*blur); | |||||
nvgFillColor(vg, nvgRGBAf(0.0, 0.0, 0.0, 0.25)); | |||||
Vec c = box.size.div(2.0); | |||||
float radius = c.x; | |||||
NVGcolor icol = nvgRGBAf(0.0, 0.0, 0.0, 0.25); | |||||
NVGcolor ocol = nvgRGBAf(0.0, 0.0, 0.0, 0.0); | |||||
NVGpaint paint = nvgRadialGradient(vg, c.x, c.y, radius - blur/2, radius + blur/2, icol, ocol); | |||||
nvgFillPaint(vg, paint); | |||||
nvgFill(vg); | |||||
} | |||||
} // namespace rack |
@@ -5,10 +5,15 @@ namespace rack { | |||||
SVGKnob::SVGKnob() { | SVGKnob::SVGKnob() { | ||||
margin = 1; | |||||
margin = 4; | |||||
shadow = new CircularShadow(); | |||||
shadow->blur = 5.0; | |||||
shadow->box.pos = Vec(0, 1); | |||||
addChild(shadow); | |||||
tw = new TransformWidget(); | tw = new TransformWidget(); | ||||
setScene(tw); | |||||
addChild(tw); | |||||
sw = new SVGWidget(); | sw = new SVGWidget(); | ||||
tw->addChild(sw); | tw->addChild(sw); | ||||
@@ -33,30 +38,12 @@ void SVGKnob::step() { | |||||
tw->translate(center); | tw->translate(center); | ||||
tw->rotate(angle); | tw->rotate(angle); | ||||
tw->translate(center.neg()); | tw->translate(center.neg()); | ||||
// Resize shadow | |||||
shadow->box.size = box.size; | |||||
} | } | ||||
FramebufferWidget::step(); | FramebufferWidget::step(); | ||||
} | } | ||||
void SVGKnob::draw(NVGcontext *vg) { | |||||
// Draw circular shadow below knob | |||||
// TODO This is a hack. Make a CircularShadow its own class | |||||
{ | |||||
nvgBeginPath(vg); | |||||
float margin = 5.0; | |||||
nvgRect(vg, -margin, -margin, box.size.x + 2*margin, box.size.y + 2*margin); | |||||
nvgFillColor(vg, nvgRGBAf(0.0, 0.0, 0.0, 0.25)); | |||||
Vec c = box.size.div(2.0); | |||||
float radius = c.x; | |||||
NVGcolor icol = nvgRGBAf(0.0, 0.0, 0.0, 0.25); | |||||
NVGcolor ocol = nvgRGBAf(0.0, 0.0, 0.0, 0.0); | |||||
NVGpaint paint = nvgRadialGradient(vg, c.x, c.y + 1, radius, radius + 3, icol, ocol); | |||||
nvgFillPaint(vg, paint); | |||||
nvgFill(vg); | |||||
} | |||||
FramebufferWidget::draw(vg); | |||||
} | |||||
void SVGKnob::onChange() { | void SVGKnob::onChange() { | ||||
dirty = true; | dirty = true; | ||||
ParamWidget::onChange(); | ParamWidget::onChange(); | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
SVGPort::SVGPort() { | SVGPort::SVGPort() { | ||||
sw = new SVGWidget(); | sw = new SVGWidget(); | ||||
setScene(sw); | |||||
addChild(sw); | |||||
} | } | ||||
void SVGPort::setSVG(std::shared_ptr<SVG> svg) { | void SVGPort::setSVG(std::shared_ptr<SVG> svg) { | ||||
@@ -197,6 +197,7 @@ void guiInit() { | |||||
// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | ||||
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | ||||
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | ||||
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); | |||||
std::string title = gApplicationName + " " + gApplicationVersion; | std::string title = gApplicationName + " " + gApplicationVersion; | ||||
window = glfwCreateWindow(1000, 750, title.c_str(), NULL, NULL); | window = glfwCreateWindow(1000, 750, title.c_str(), NULL, NULL); | ||||
assert(window); | assert(window); | ||||
@@ -10,6 +10,7 @@ namespace rack { | |||||
struct FramebufferWidget::Internal { | struct FramebufferWidget::Internal { | ||||
NVGLUframebuffer *fb = NULL; | NVGLUframebuffer *fb = NULL; | ||||
Vec offset; | |||||
~Internal() { | ~Internal() { | ||||
setFramebuffer(NULL); | setFramebuffer(NULL); | ||||
@@ -26,24 +27,23 @@ FramebufferWidget::FramebufferWidget() { | |||||
} | } | ||||
FramebufferWidget::~FramebufferWidget() { | FramebufferWidget::~FramebufferWidget() { | ||||
if (scene) { | |||||
delete scene; | |||||
} | |||||
delete internal; | delete internal; | ||||
} | } | ||||
void FramebufferWidget::step() { | void FramebufferWidget::step() { | ||||
if (!scene) | |||||
if (children.empty()) | |||||
return; | return; | ||||
// Step scene before rendering | |||||
scene->step(); | |||||
// Step children before rendering | |||||
Widget::step(); | |||||
// Render the scene to the framebuffer if dirty | // Render the scene to the framebuffer if dirty | ||||
if (dirty) { | if (dirty) { | ||||
assert(scene->box.size.isFinite()); | |||||
int width = ceilf(scene->box.size.x) + 2*margin; | |||||
int height = ceilf(scene->box.size.y) + 2*margin; | |||||
Rect childrenBox = getChildrenBoundingBox(); | |||||
assert(childrenBox.size.isFinite()); | |||||
int width = ceilf(childrenBox.size.x) + 2*margin; | |||||
int height = ceilf(childrenBox.size.y) + 2*margin; | |||||
internal->offset = childrenBox.pos.minus(Vec(margin, margin)); | |||||
internal->setFramebuffer(NULL); | internal->setFramebuffer(NULL); | ||||
NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, width, height, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, width, height, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | ||||
@@ -59,8 +59,8 @@ void FramebufferWidget::step() { | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | ||||
nvgBeginFrame(gVg, width, height, pixelRatio); | nvgBeginFrame(gVg, width, height, pixelRatio); | ||||
nvgTranslate(gVg, margin, margin); | |||||
scene->draw(gVg); | |||||
nvgTranslate(gVg, -internal->offset.x, -internal->offset.y); | |||||
Widget::draw(gVg); | |||||
nvgEndFrame(gVg); | nvgEndFrame(gVg); | ||||
nvgluBindFramebuffer(NULL); | nvgluBindFramebuffer(NULL); | ||||
@@ -72,17 +72,18 @@ void FramebufferWidget::step() { | |||||
void FramebufferWidget::draw(NVGcontext *vg) { | void FramebufferWidget::draw(NVGcontext *vg) { | ||||
if (!internal->fb) | if (!internal->fb) | ||||
return; | return; | ||||
if (!scene) | |||||
return; | |||||
// Draw framebuffer image | // Draw framebuffer image | ||||
int width, height; | int width, height; | ||||
nvgImageSize(vg, internal->fb->image, &width, &height); | nvgImageSize(vg, internal->fb->image, &width, &height); | ||||
nvgBeginPath(vg); | nvgBeginPath(vg); | ||||
nvgRect(vg, -margin, -margin, width, height); | |||||
NVGpaint paint = nvgImagePattern(vg, -margin + scene->box.pos.x, -margin + scene->box.pos.y, width, height, 0.0, internal->fb->image, 1.0); | |||||
nvgRect(vg, internal->offset.x, internal->offset.y, width, height); | |||||
NVGpaint paint = nvgImagePattern(vg, internal->offset.x, internal->offset.y, width, height, 0.0, internal->fb->image, 1.0); | |||||
nvgFillPaint(vg, paint); | nvgFillPaint(vg, paint); | ||||
nvgFill(vg); | nvgFill(vg); | ||||
// nvgFillColor(vg, nvgRGBA(255, 0, 0, 64)); | |||||
// nvgFill(vg); | |||||
} | } | ||||