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