| @@ -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 | |||
| //////////////////// | |||
| struct CircularShadow : TransparentWidget { | |||
| float blur = 0.0; | |||
| void draw(NVGcontext *vg); | |||
| }; | |||
| struct Light : TransparentWidget { | |||
| NVGcolor color; | |||
| void draw(NVGcontext *vg); | |||
| @@ -137,17 +142,24 @@ struct SVGKnob : Knob, FramebufferWidget { | |||
| /** Not owned */ | |||
| TransformWidget *tw; | |||
| SVGWidget *sw; | |||
| CircularShadow *shadow; | |||
| SVGKnob(); | |||
| void setSVG(std::shared_ptr<SVG> svg); | |||
| void step(); | |||
| void draw(NVGcontext *vg); | |||
| void onChange(); | |||
| }; | |||
| struct Switch : ParamWidget, SpriteWidget { | |||
| }; | |||
| struct SVGSwitch : ParamWidget, FramebufferWidget { | |||
| /** Not owned */ | |||
| TransformWidget *tw; | |||
| SVGWidget *swPressed; | |||
| SVGWidget *swReleased; | |||
| }; | |||
| struct ToggleSwitch : virtual Switch { | |||
| void onDragStart() { | |||
| index = 1; | |||
| @@ -360,7 +360,7 @@ struct CL1362Port : SpritePort { | |||
| }; | |||
| //////////////////// | |||
| // Lights | |||
| // LEDs | |||
| //////////////////// | |||
| 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 | |||
| //////////////////// | |||
| @@ -186,28 +186,24 @@ struct SVGWidget : virtual Widget { | |||
| }; | |||
| /** 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. | |||
| */ | |||
| 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; | |||
| /** 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. | |||
| */ | |||
| int margin = 0; | |||
| /** The root object in the framebuffer scene | |||
| The FramebufferWidget owns the pointer | |||
| */ | |||
| Widget *scene = NULL; | |||
| struct Internal; | |||
| Internal *internal; | |||
| FramebufferWidget(); | |||
| ~FramebufferWidget(); | |||
| void setScene(Widget *w) { | |||
| scene = w; | |||
| } | |||
| void step(); | |||
| 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() { | |||
| margin = 1; | |||
| margin = 4; | |||
| shadow = new CircularShadow(); | |||
| shadow->blur = 5.0; | |||
| shadow->box.pos = Vec(0, 1); | |||
| addChild(shadow); | |||
| tw = new TransformWidget(); | |||
| setScene(tw); | |||
| addChild(tw); | |||
| sw = new SVGWidget(); | |||
| tw->addChild(sw); | |||
| @@ -33,30 +38,12 @@ void SVGKnob::step() { | |||
| tw->translate(center); | |||
| tw->rotate(angle); | |||
| tw->translate(center.neg()); | |||
| // Resize shadow | |||
| shadow->box.size = box.size; | |||
| } | |||
| 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() { | |||
| dirty = true; | |||
| ParamWidget::onChange(); | |||
| @@ -6,7 +6,7 @@ namespace rack { | |||
| SVGPort::SVGPort() { | |||
| sw = new SVGWidget(); | |||
| setScene(sw); | |||
| addChild(sw); | |||
| } | |||
| void SVGPort::setSVG(std::shared_ptr<SVG> svg) { | |||
| @@ -197,6 +197,7 @@ void guiInit() { | |||
| // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | |||
| // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |||
| // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |||
| glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); | |||
| std::string title = gApplicationName + " " + gApplicationVersion; | |||
| window = glfwCreateWindow(1000, 750, title.c_str(), NULL, NULL); | |||
| assert(window); | |||
| @@ -10,6 +10,7 @@ namespace rack { | |||
| struct FramebufferWidget::Internal { | |||
| NVGLUframebuffer *fb = NULL; | |||
| Vec offset; | |||
| ~Internal() { | |||
| setFramebuffer(NULL); | |||
| @@ -26,24 +27,23 @@ FramebufferWidget::FramebufferWidget() { | |||
| } | |||
| FramebufferWidget::~FramebufferWidget() { | |||
| if (scene) { | |||
| delete scene; | |||
| } | |||
| delete internal; | |||
| } | |||
| void FramebufferWidget::step() { | |||
| if (!scene) | |||
| if (children.empty()) | |||
| return; | |||
| // Step scene before rendering | |||
| scene->step(); | |||
| // Step children before rendering | |||
| Widget::step(); | |||
| // Render the scene to the framebuffer 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); | |||
| 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); | |||
| 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); | |||
| nvgluBindFramebuffer(NULL); | |||
| @@ -72,17 +72,18 @@ void FramebufferWidget::step() { | |||
| void FramebufferWidget::draw(NVGcontext *vg) { | |||
| if (!internal->fb) | |||
| return; | |||
| if (!scene) | |||
| return; | |||
| // Draw framebuffer image | |||
| int width, height; | |||
| nvgImageSize(vg, internal->fb->image, &width, &height); | |||
| 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); | |||
| nvgFill(vg); | |||
| // nvgFillColor(vg, nvgRGBA(255, 0, 0, 64)); | |||
| // nvgFill(vg); | |||
| } | |||