| @@ -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); | |||||
| } | } | ||||