@@ -18,6 +18,6 @@ Install dependencies | |||||
- [libsamplerate](http://www.mega-nerd.com/SRC/) | - [libsamplerate](http://www.mega-nerd.com/SRC/) | ||||
- GTK+-2.0 if Linux (for file open/save dialog) | - GTK+-2.0 if Linux (for file open/save dialog) | ||||
Run `make ARCH=linux` or `make ARCH=windows` or `make ARCH=apple` | |||||
Run `make ARCH=lin` or `make ARCH=win` or `make ARCH=mac` | |||||
If the build breaks because you think I've missed a step, feel free to post an issue. | If the build breaks because you think I've missed a step, feel free to post an issue. |
@@ -1 +1 @@ | |||||
Subproject commit 8feae63a46fdf8ed83613b6a46da25d44adde07f | |||||
Subproject commit c629efbfa6ca90035d6625dfa3586048a9c3201f |
@@ -4,20 +4,38 @@ | |||||
namespace rack { | namespace rack { | ||||
#define SCHEME_BLACK nvgRGB(0x00, 0x00, 0x00) | |||||
#define SCHEME_WHITE nvgRGB(0xff, 0xff, 0xff) | |||||
#define SCHEME_RED nvgRGB(0xed, 0x2c, 0x24) | |||||
#define SCHEME_ORANGE nvgRGB(0xf2, 0xb1, 0x20) | |||||
#define SCHEME_YELLOW nvgRGB(0xf9, 0xdf, 0x1c) | |||||
#define SCHEME_GREEN nvgRGB(0x90, 0xc7, 0x3e) | |||||
#define SCHEME_CYAN nvgRGB(0x22, 0xe6, 0xef) | |||||
#define SCHEME_BLUE nvgRGB(0x29, 0xb2, 0xef) | |||||
#define SCHEME_PURPLE nvgRGB(0xd5, 0x2b, 0xed) | |||||
enum ColorNames { | |||||
COLOR_BLACK, | |||||
COLOR_WHITE, | |||||
COLOR_RED, | |||||
COLOR_ORANGE, | |||||
COLOR_YELLOW, | |||||
COLOR_GREEN, | |||||
COLOR_CYAN, | |||||
COLOR_BLUE, | |||||
COLOR_PURPLE, | |||||
NUM_COLORS | |||||
}; | |||||
extern const NVGcolor colors[NUM_COLORS]; | |||||
//////////////////// | //////////////////// | ||||
// Knobs | // Knobs | ||||
//////////////////// | //////////////////// | ||||
struct SynthTechAlco : SpriteKnob { | |||||
SynthTechAlco() { | |||||
box.size = Vec(45, 45); | |||||
spriteOffset = Vec(-3, -2); | |||||
spriteSize = Vec(51, 51); | |||||
minIndex = 49; | |||||
maxIndex = -51; | |||||
spriteCount = 120; | |||||
spriteImage = Image::load("res/ComponentLibrary/SynthTechAlco.png"); | |||||
} | |||||
}; | |||||
struct KnobDavies1900h : SpriteKnob { | struct KnobDavies1900h : SpriteKnob { | ||||
KnobDavies1900h() { | KnobDavies1900h() { | ||||
box.size = Vec(36, 36); | box.size = Vec(36, 36); | ||||
@@ -102,7 +120,7 @@ typedef PJ301M<OutputPort> OutputPortPJ301M; | |||||
template <typename BASE> | template <typename BASE> | ||||
struct PJ3410 : BASE { | struct PJ3410 : BASE { | ||||
PJ3410() { | PJ3410() { | ||||
this->box.size = Vec(32, 32); | |||||
this->box.size = Vec(32, 31); | |||||
this->spriteOffset = Vec(-1, -1); | this->spriteOffset = Vec(-1, -1); | ||||
this->spriteSize = Vec(36, 36); | this->spriteSize = Vec(36, 36); | ||||
this->spriteImage = Image::load("res/ComponentLibrary/PJ3410.png"); | this->spriteImage = Image::load("res/ComponentLibrary/PJ3410.png"); | ||||
@@ -131,23 +149,31 @@ struct ValueLight : Light { | |||||
float *value; | float *value; | ||||
}; | }; | ||||
struct RedValueLight : ValueLight { | |||||
template <int COLOR> | |||||
struct ColorValueLight : ValueLight { | |||||
void step() { | void step() { | ||||
float v = sqrtBipolar(getf(value)); | float v = sqrtBipolar(getf(value)); | ||||
color = nvgLerpRGBA(SCHEME_BLACK, SCHEME_RED, v); | |||||
color = nvgLerpRGBA(colors[COLOR_BLACK], colors[COLOR], v); | |||||
} | } | ||||
}; | }; | ||||
struct GreenRedPolarityLight : ValueLight { | |||||
typedef ColorValueLight<COLOR_RED> RedValueLight; | |||||
typedef ColorValueLight<COLOR_YELLOW> YellowValueLight; | |||||
typedef ColorValueLight<COLOR_GREEN> GreenValueLight; | |||||
template <int COLOR_POS, int COLOR_NEG> | |||||
struct PolarityLight : ValueLight { | |||||
void step() { | void step() { | ||||
float v = sqrtBipolar(getf(value)); | float v = sqrtBipolar(getf(value)); | ||||
if (v >= 0.0) | if (v >= 0.0) | ||||
color = nvgLerpRGBA(SCHEME_BLACK, SCHEME_GREEN, v); | |||||
color = nvgLerpRGBA(colors[COLOR_BLACK], colors[COLOR_POS], v); | |||||
else | else | ||||
color = nvgLerpRGBA(SCHEME_BLACK, SCHEME_RED, -v); | |||||
color = nvgLerpRGBA(colors[COLOR_BLACK], colors[COLOR_NEG], -v); | |||||
} | } | ||||
}; | }; | ||||
typedef PolarityLight<COLOR_GREEN, COLOR_RED> GreenRedPolarityLight; | |||||
template <typename BASE> | template <typename BASE> | ||||
struct LargeLight : BASE { | struct LargeLight : BASE { | ||||
LargeLight() { | LargeLight() { | ||||
@@ -197,13 +223,13 @@ struct SilverScrew : Screw { | |||||
struct LightPanel : Panel { | struct LightPanel : Panel { | ||||
LightPanel() { | LightPanel() { | ||||
backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8); | backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8); | ||||
borderColor = nvgRGB(0xac, 0xac, 0xac); | |||||
borderColor = nvgRGB(0xa1, 0xa1, 0xa1); | |||||
} | } | ||||
}; | }; | ||||
struct DarkPanel : Panel { | struct DarkPanel : Panel { | ||||
DarkPanel() { | DarkPanel() { | ||||
backgroundColor = nvgRGB(0x0f, 0x0f, 0x0f); | |||||
backgroundColor = nvgRGB(0x17, 0x17, 0x17); | |||||
borderColor = nvgRGB(0x5e, 0x5e, 0x5e); | borderColor = nvgRGB(0x5e, 0x5e, 0x5e); | ||||
} | } | ||||
}; | }; | ||||
@@ -13,7 +13,6 @@ void guiCursorUnlock(); | |||||
const char *guiSaveDialog(const char *filters, const char *filename); | const char *guiSaveDialog(const char *filters, const char *filename); | ||||
const char *guiOpenDialog(const char *filters, const char *filename); | const char *guiOpenDialog(const char *filters, const char *filename); | ||||
// TODO This should probably go in another file, like resources.hpp? | |||||
void drawSVG(NVGcontext *vg, NSVGimage *svg); | |||||
extern NVGcontext *gVg; | |||||
} // namespace rack | } // namespace rack |
@@ -61,6 +61,12 @@ inline float sqrtBipolar(float x) { | |||||
return x >= 0.0 ? sqrtf(x) : -sqrtf(-x); | return x >= 0.0 ? sqrtf(x) : -sqrtf(-x); | ||||
} | } | ||||
/** This is pretty much a scaled sinh */ | |||||
inline float exponentialBipolar(float b, float x) { | |||||
const float a = b - 1.0 / b; | |||||
return (powf(b, x) - powf(b, -x)) / a; | |||||
} | |||||
inline float sincf(float x) { | inline float sincf(float x) { | ||||
if (x == 0.0) | if (x == 0.0) | ||||
return 1.0; | return 1.0; | ||||
@@ -145,6 +151,12 @@ struct Vec { | |||||
Vec round() { | Vec round() { | ||||
return Vec(roundf(x), roundf(y)); | return Vec(roundf(x), roundf(y)); | ||||
} | } | ||||
bool isFinite() { | |||||
return isfinite(x) && isfinite(y); | |||||
} | |||||
bool isZero() { | |||||
return x == 0.0 && y == 0.0; | |||||
} | |||||
}; | }; | ||||
@@ -43,7 +43,7 @@ struct SVG { | |||||
//////////////////// | //////////////////// | ||||
// base class and traits | |||||
// Base widget | |||||
//////////////////// | //////////////////// | ||||
/** A node in the 2D scene graph */ | /** A node in the 2D scene graph */ | ||||
@@ -112,6 +112,22 @@ struct Widget { | |||||
virtual void onChange() {} | virtual void onChange() {} | ||||
}; | }; | ||||
struct TransformWidget : Widget { | |||||
/** The transformation matrix */ | |||||
float transform[6]; | |||||
TransformWidget(); | |||||
void reset(); | |||||
void translate(Vec delta); | |||||
void rotate(float angle); | |||||
void scale(Vec s); | |||||
void draw(NVGcontext *vg); | |||||
}; | |||||
//////////////////// | |||||
// Trait widgets | |||||
//////////////////// | |||||
/** Widget that does not respond to events */ | /** Widget that does not respond to events */ | ||||
struct TransparentWidget : virtual Widget { | struct TransparentWidget : virtual Widget { | ||||
Widget *onMouseDown(Vec pos, int button) {return NULL;} | Widget *onMouseDown(Vec pos, int button) {return NULL;} | ||||
@@ -157,6 +173,33 @@ struct SpriteWidget : virtual Widget { | |||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
}; | }; | ||||
struct SVGWidget : virtual Widget { | |||||
std::shared_ptr<SVG> svg; | |||||
/** Sets the box size to the svg page */ | |||||
void step(); | |||||
void draw(NVGcontext *vg); | |||||
}; | |||||
/** 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(). | |||||
Events are not passed to the underlying scene. | |||||
*/ | |||||
struct FramebufferWidget : virtual Widget { | |||||
bool dirty = true; | |||||
/** The root object in the framebuffer scene | |||||
Its position is ignored for now, fixed at (0, 0) | |||||
The FramebufferWidget owns the pointer | |||||
*/ | |||||
Widget *scene = NULL; | |||||
struct Internal; | |||||
Internal *internal; | |||||
FramebufferWidget(); | |||||
~FramebufferWidget(); | |||||
void step(); | |||||
void draw(NVGcontext *vg); | |||||
}; | |||||
struct QuantityWidget : virtual Widget { | struct QuantityWidget : virtual Widget { | ||||
float value = 0.0; | float value = 0.0; | ||||
float minValue = 0.0; | float minValue = 0.0; | ||||
@@ -179,7 +222,7 @@ struct QuantityWidget : virtual Widget { | |||||
}; | }; | ||||
//////////////////// | //////////////////// | ||||
// gui elements | |||||
// GUI widgets | |||||
//////////////////// | //////////////////// | ||||
struct Label : Widget { | struct Label : Widget { | ||||
@@ -0,0 +1,19 @@ | |||||
#include "components.hpp" | |||||
namespace rack { | |||||
const NVGcolor colors[NUM_COLORS] = { | |||||
nvgRGB(0x00, 0x00, 0x00), | |||||
nvgRGB(0xff, 0xff, 0xff), | |||||
nvgRGB(0xed, 0x2c, 0x24), | |||||
nvgRGB(0xf2, 0xb1, 0x20), | |||||
nvgRGB(0xf9, 0xdf, 0x1c), | |||||
nvgRGB(0x90, 0xc7, 0x3e), | |||||
nvgRGB(0x22, 0xe6, 0xef), | |||||
nvgRGB(0x29, 0xb2, 0xef), | |||||
nvgRGB(0xd5, 0x2b, 0xed), | |||||
}; | |||||
} // namespace rack |
@@ -7,9 +7,10 @@ | |||||
#include "scene.hpp" | #include "scene.hpp" | ||||
// Include implementations here | // Include implementations here | ||||
// By the way, please stop packaging your libraries like this. It's easiest to use a single source file (e.g. foo.c) and a single header (e.g. foo.h) | |||||
// By the way, please stop packaging your libraries like this. It's best to use a single source file (e.g. foo.c) and a single header (e.g. foo.h) | |||||
#define NANOVG_GL3_IMPLEMENTATION | #define NANOVG_GL3_IMPLEMENTATION | ||||
#include "../ext/nanovg/src/nanovg_gl.h" | #include "../ext/nanovg/src/nanovg_gl.h" | ||||
#include "../ext/nanovg/src/nanovg_gl_utils.h" | |||||
#define BLENDISH_IMPLEMENTATION | #define BLENDISH_IMPLEMENTATION | ||||
#include "../ext/oui/blendish.h" | #include "../ext/oui/blendish.h" | ||||
#define NANOSVG_IMPLEMENTATION | #define NANOSVG_IMPLEMENTATION | ||||
@@ -23,8 +24,8 @@ extern "C" { | |||||
namespace rack { | namespace rack { | ||||
static GLFWwindow *window = NULL; | static GLFWwindow *window = NULL; | ||||
static NVGcontext *vg = NULL; | |||||
static std::shared_ptr<Font> defaultFont; | static std::shared_ptr<Font> defaultFont; | ||||
NVGcontext *gVg = NULL; | |||||
void windowSizeCallback(GLFWwindow* window, int width, int height) { | void windowSizeCallback(GLFWwindow* window, int width, int height) { | ||||
@@ -160,16 +161,16 @@ void renderGui() { | |||||
// Update and render | // Update and render | ||||
glViewport(0, 0, width, height); | glViewport(0, 0, width, height); | ||||
glClearColor(1.0, 1.0, 1.0, 1.0); | |||||
glClearColor(0.0, 0.0, 0.0, 1.0); | |||||
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(vg, width, height, 1.0); | |||||
nvgBeginFrame(gVg, width, height, 1.0); | |||||
nvgSave(vg); | |||||
gScene->draw(vg); | |||||
nvgRestore(vg); | |||||
nvgSave(gVg); | |||||
gScene->draw(gVg); | |||||
nvgRestore(gVg); | |||||
nvgEndFrame(vg); | |||||
nvgEndFrame(gVg); | |||||
glfwSwapBuffers(window); | glfwSwapBuffers(window); | ||||
} | } | ||||
@@ -180,12 +181,10 @@ void guiInit() { | |||||
err = glfwInit(); | err = glfwInit(); | ||||
assert(err); | assert(err); | ||||
// #ifndef WINDOWS | |||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | ||||
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); | ||||
// #endif | |||||
window = glfwCreateWindow(1000, 750, gApplicationName.c_str(), NULL, NULL); | window = glfwCreateWindow(1000, 750, gApplicationName.c_str(), NULL, NULL); | ||||
assert(window); | assert(window); | ||||
glfwMakeContextCurrent(window); | glfwMakeContextCurrent(window); | ||||
@@ -210,8 +209,8 @@ void guiInit() { | |||||
glfwSetWindowSizeLimits(window, 240, 160, GLFW_DONT_CARE, GLFW_DONT_CARE); | glfwSetWindowSizeLimits(window, 240, 160, GLFW_DONT_CARE, GLFW_DONT_CARE); | ||||
// Set up NanoVG | // Set up NanoVG | ||||
vg = nvgCreateGL3(NVG_ANTIALIAS); | |||||
assert(vg); | |||||
gVg = nvgCreateGL3(NVG_ANTIALIAS); | |||||
assert(gVg); | |||||
// Set up Blendish | // Set up Blendish | ||||
defaultFont = Font::load("res/DejaVuSans.ttf"); | defaultFont = Font::load("res/DejaVuSans.ttf"); | ||||
@@ -221,7 +220,7 @@ void guiInit() { | |||||
void guiDestroy() { | void guiDestroy() { | ||||
defaultFont.reset(); | defaultFont.reset(); | ||||
nvgDeleteGL3(vg); | |||||
nvgDeleteGL3(gVg); | |||||
glfwDestroyWindow(window); | glfwDestroyWindow(window); | ||||
glfwTerminate(); | glfwTerminate(); | ||||
} | } | ||||
@@ -275,7 +274,7 @@ const char *guiOpenDialog(const char *filters, const char *filename) { | |||||
//////////////////// | //////////////////// | ||||
Font::Font(const std::string &filename) { | Font::Font(const std::string &filename) { | ||||
handle = nvgCreateFont(vg, filename.c_str(), filename.c_str()); | |||||
handle = nvgCreateFont(gVg, filename.c_str(), filename.c_str()); | |||||
if (handle >= 0) { | if (handle >= 0) { | ||||
fprintf(stderr, "Loaded font %s\n", filename.c_str()); | fprintf(stderr, "Loaded font %s\n", filename.c_str()); | ||||
} | } | ||||
@@ -285,7 +284,7 @@ Font::Font(const std::string &filename) { | |||||
} | } | ||||
Font::~Font() { | Font::~Font() { | ||||
// There is no NanoVG deleteFont() function, so do nothing | |||||
// There is no NanoVG deleteFont() function yet, so do nothing | |||||
} | } | ||||
std::shared_ptr<Font> Font::load(const std::string &filename) { | std::shared_ptr<Font> Font::load(const std::string &filename) { | ||||
@@ -301,7 +300,7 @@ std::shared_ptr<Font> Font::load(const std::string &filename) { | |||||
//////////////////// | //////////////////// | ||||
Image::Image(const std::string &filename) { | Image::Image(const std::string &filename) { | ||||
handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||||
handle = nvgCreateImage(gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||||
if (handle > 0) { | if (handle > 0) { | ||||
fprintf(stderr, "Loaded image %s\n", filename.c_str()); | fprintf(stderr, "Loaded image %s\n", filename.c_str()); | ||||
} | } | ||||
@@ -312,7 +311,7 @@ Image::Image(const std::string &filename) { | |||||
Image::~Image() { | Image::~Image() { | ||||
// TODO What if handle is invalid? | // TODO What if handle is invalid? | ||||
nvgDeleteImage(vg, handle); | |||||
nvgDeleteImage(gVg, handle); | |||||
} | } | ||||
std::shared_ptr<Image> Image::load(const std::string &filename) { | std::shared_ptr<Image> Image::load(const std::string &filename) { | ||||
@@ -350,83 +349,4 @@ std::shared_ptr<SVG> SVG::load(const std::string &filename) { | |||||
} | } | ||||
//////////////////// | |||||
// drawSVG | |||||
//////////////////// | |||||
NVGcolor getNVGColor(int color) { | |||||
return nvgRGBA((color >> 0) & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff); | |||||
// return nvgRGBA((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, (color) & 0xff); | |||||
} | |||||
void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
for (NSVGshape *shape = svg->shapes; shape; shape = shape->next) { | |||||
// printf(" new shape: id \"%s\", fillrule %d\n", shape->id, shape->fillRule); | |||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | |||||
continue; | |||||
nvgSave(vg); | |||||
nvgGlobalAlpha(vg, shape->opacity); | |||||
nvgStrokeWidth(vg, shape->strokeWidth); | |||||
// strokeDashOffset, strokeDashArray, strokeDashCount not supported | |||||
// strokeLineJoin, strokeLineCap not supported | |||||
// Build path | |||||
nvgBeginPath(vg); | |||||
for (NSVGpath *path = shape->paths; path; path = path->next) { | |||||
// printf(" new path: %d points, %s\n", path->npts, path->closed ? "closed" : "notclosed"); | |||||
nvgMoveTo(vg, path->pts[0], path->pts[1]); | |||||
for (int i = 1; i < path->npts; i += 3) { | |||||
float *p = &path->pts[2*i]; | |||||
nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]); | |||||
// nvgLineTo(vg, p[4], p[5]); | |||||
} | |||||
if (path->closed) | |||||
nvgClosePath(vg); | |||||
if (path->next) | |||||
nvgPathWinding(vg, NVG_HOLE); | |||||
} | |||||
// Fill shape | |||||
if (shape->fill.type) { | |||||
switch (shape->fill.type) { | |||||
case NSVG_PAINT_COLOR: { | |||||
NVGcolor color = getNVGColor(shape->fill.color); | |||||
nvgFillColor(vg, color); | |||||
// printf(" fill color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); | |||||
} break; | |||||
case NSVG_PAINT_LINEAR_GRADIENT: { | |||||
NSVGgradient *g = shape->fill.gradient; | |||||
// printf(" lin grad: %f\t%f\n", g->fx, g->fy); | |||||
} break; | |||||
} | |||||
nvgFill(vg); | |||||
} | |||||
// Stroke shape | |||||
if (shape->stroke.type) { | |||||
switch (shape->stroke.type) { | |||||
case NSVG_PAINT_COLOR: { | |||||
NVGcolor color = getNVGColor(shape->stroke.color); | |||||
nvgFillColor(vg, color); | |||||
// printf(" stroke color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); | |||||
} break; | |||||
case NSVG_PAINT_LINEAR_GRADIENT: { | |||||
NSVGgradient *g = shape->stroke.gradient; | |||||
// printf(" lin grad: %f\t%f\n", g->fx, g->fy); | |||||
} break; | |||||
} | |||||
nvgStroke(vg); | |||||
} | |||||
nvgRestore(vg); | |||||
} | |||||
} | |||||
} // namespace rack | } // namespace rack |
@@ -0,0 +1,88 @@ | |||||
#include "widgets.hpp" | |||||
#include "gui.hpp" | |||||
#include <GL/glew.h> | |||||
#include "../ext/nanovg/src/nanovg_gl.h" | |||||
#include "../ext/nanovg/src/nanovg_gl_utils.h" | |||||
namespace rack { | |||||
struct FramebufferWidget::Internal { | |||||
NVGLUframebuffer *fb = NULL; | |||||
~Internal() { | |||||
setFramebuffer(NULL); | |||||
} | |||||
void setFramebuffer(NVGLUframebuffer *fb) { | |||||
if (this->fb) | |||||
nvgluDeleteFramebuffer(this->fb); | |||||
this->fb = fb; | |||||
} | |||||
}; | |||||
FramebufferWidget::FramebufferWidget() { | |||||
internal = new Internal(); | |||||
} | |||||
FramebufferWidget::~FramebufferWidget() { | |||||
if (scene) { | |||||
delete scene; | |||||
} | |||||
delete internal; | |||||
} | |||||
/** A margin in pixels around the scene in the framebuffer */ | |||||
static const int margin = 1; | |||||
void FramebufferWidget::step() { | |||||
if (!scene) | |||||
return; | |||||
scene->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; | |||||
internal->setFramebuffer(NULL); | |||||
NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, width, height, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||||
assert(fb); | |||||
internal->setFramebuffer(fb); | |||||
// TODO Support screens with pixelRatio != 1.0 (e.g. Retina) by using the actual size of the framebuffer, etc. | |||||
const float pixelRatio = 1.0; | |||||
nvgluBindFramebuffer(fb); | |||||
glViewport(0.0, 0.0, width, height); | |||||
glClearColor(0.0, 0.0, 0.0, 0.0); | |||||
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); | |||||
nvgEndFrame(gVg); | |||||
nvgluBindFramebuffer(NULL); | |||||
dirty = false; | |||||
} | |||||
} | |||||
void FramebufferWidget::draw(NVGcontext *vg) { | |||||
if (!internal->fb) | |||||
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, -margin, width, height, 0.0, internal->fb->image, 1.0); | |||||
nvgFillPaint(vg, paint); | |||||
nvgFill(vg); | |||||
} | |||||
} // namespace rack |
@@ -17,18 +17,18 @@ void Light::draw(NVGcontext *vg) { | |||||
nvgStrokeColor(vg, colorOutline); | nvgStrokeColor(vg, colorOutline); | ||||
nvgStroke(vg); | nvgStroke(vg); | ||||
// nvgGlobalCompositeOperation(vg, NVG_LIGHTER); | |||||
// NVGpaint paint; | |||||
// NVGcolor icol = color; | |||||
// icol.a = 0.2; | |||||
// NVGcolor ocol = color; | |||||
// ocol.a = 0.0; | |||||
// float oradius = radius + 20.0; | |||||
// paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); | |||||
// nvgFillPaint(vg, paint); | |||||
// nvgBeginPath(vg); | |||||
// nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); | |||||
// nvgFill(vg); | |||||
nvgGlobalCompositeOperation(vg, NVG_LIGHTER); | |||||
NVGpaint paint; | |||||
NVGcolor icol = color; | |||||
icol.a = 0.1; | |||||
NVGcolor ocol = color; | |||||
ocol.a = 0.0; | |||||
float oradius = radius + 30.0; | |||||
paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); | |||||
nvgFillPaint(vg, paint); | |||||
nvgBeginPath(vg); | |||||
nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); | |||||
nvgFill(vg); | |||||
} | } | ||||
@@ -181,7 +181,7 @@ void ModuleWidget::onMouseDown(int button) { | |||||
menu->pushChild(menuLabel); | menu->pushChild(menuLabel); | ||||
ResetParamsMenuItem *resetItem = new ResetParamsMenuItem(); | ResetParamsMenuItem *resetItem = new ResetParamsMenuItem(); | ||||
resetItem->text = "Reset parameters"; | |||||
resetItem->text = "Initialize parameters"; | |||||
resetItem->moduleWidget = this; | resetItem->moduleWidget = this; | ||||
menu->pushChild(resetItem); | menu->pushChild(resetItem); | ||||
@@ -0,0 +1,95 @@ | |||||
#include "widgets.hpp" | |||||
namespace rack { | |||||
static NVGcolor getNVGColor(int color) { | |||||
return nvgRGBA((color >> 0) & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff); | |||||
// return nvgRGBA((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, (color) & 0xff); | |||||
} | |||||
static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
for (NSVGshape *shape = svg->shapes; shape; shape = shape->next) { | |||||
// printf(" new shape: id \"%s\", fillrule %d\n", shape->id, shape->fillRule); | |||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | |||||
continue; | |||||
nvgSave(vg); | |||||
nvgGlobalAlpha(vg, shape->opacity); | |||||
nvgStrokeWidth(vg, shape->strokeWidth); | |||||
// strokeDashOffset, strokeDashArray, strokeDashCount not supported | |||||
// strokeLineJoin, strokeLineCap not supported | |||||
// Build path | |||||
nvgBeginPath(vg); | |||||
for (NSVGpath *path = shape->paths; path; path = path->next) { | |||||
// printf(" new path: %d points, %s\n", path->npts, path->closed ? "closed" : "notclosed"); | |||||
nvgMoveTo(vg, path->pts[0], path->pts[1]); | |||||
for (int i = 1; i < path->npts; i += 3) { | |||||
float *p = &path->pts[2*i]; | |||||
nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]); | |||||
// nvgLineTo(vg, p[4], p[5]); | |||||
} | |||||
if (path->closed) | |||||
nvgClosePath(vg); | |||||
if (path->next) | |||||
nvgPathWinding(vg, NVG_HOLE); | |||||
} | |||||
// Fill shape | |||||
if (shape->fill.type) { | |||||
switch (shape->fill.type) { | |||||
case NSVG_PAINT_COLOR: { | |||||
NVGcolor color = getNVGColor(shape->fill.color); | |||||
nvgFillColor(vg, color); | |||||
// printf(" fill color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); | |||||
} break; | |||||
case NSVG_PAINT_LINEAR_GRADIENT: { | |||||
// NSVGgradient *g = shape->fill.gradient; | |||||
// printf(" lin grad: %f\t%f\n", g->fx, g->fy); | |||||
} break; | |||||
} | |||||
nvgFill(vg); | |||||
} | |||||
// Stroke shape | |||||
if (shape->stroke.type) { | |||||
switch (shape->stroke.type) { | |||||
case NSVG_PAINT_COLOR: { | |||||
NVGcolor color = getNVGColor(shape->stroke.color); | |||||
nvgFillColor(vg, color); | |||||
// printf(" stroke color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); | |||||
} break; | |||||
case NSVG_PAINT_LINEAR_GRADIENT: { | |||||
// NSVGgradient *g = shape->stroke.gradient; | |||||
// printf(" lin grad: %f\t%f\n", g->fx, g->fy); | |||||
} break; | |||||
} | |||||
nvgStroke(vg); | |||||
} | |||||
nvgRestore(vg); | |||||
} | |||||
} | |||||
void SVGWidget::step() { | |||||
// Automatically wrap box size to SVG page size | |||||
if (svg) | |||||
box.size = Vec(svg->handle->width, svg->handle->height); | |||||
else | |||||
box.size = Vec(); | |||||
} | |||||
void SVGWidget::draw(NVGcontext *vg) { | |||||
drawSVG(vg, svg->handle); | |||||
} | |||||
} // namespace rack |
@@ -0,0 +1,40 @@ | |||||
#include "scene.hpp" | |||||
namespace rack { | |||||
TransformWidget::TransformWidget() { | |||||
reset(); | |||||
} | |||||
void TransformWidget::reset() { | |||||
nvgTransformIdentity(transform); | |||||
} | |||||
void TransformWidget::translate(Vec delta) { | |||||
float t[6]; | |||||
nvgTransformTranslate(t, delta.x, delta.y); | |||||
nvgTransformPremultiply(transform, t); | |||||
} | |||||
void TransformWidget::rotate(float angle) { | |||||
float t[6]; | |||||
nvgTransformRotate(t, angle); | |||||
nvgTransformPremultiply(transform, t); | |||||
} | |||||
void TransformWidget::scale(Vec s) { | |||||
float t[6]; | |||||
nvgTransformScale(t, s.x, s.y); | |||||
nvgTransformPremultiply(transform, t); | |||||
} | |||||
void TransformWidget::draw(NVGcontext *vg) { | |||||
// No need to save the state because that is done in the parent | |||||
nvgTransform(vg, transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]); | |||||
Widget::draw(vg); | |||||
} | |||||
} // namespace rack |
@@ -32,7 +32,7 @@ static void drawWire(NVGcontext *vg, Vec pos1, Vec pos2, NVGcolor color, float t | |||||
// Wire | // Wire | ||||
if (opacity > 0.0) { | if (opacity > 0.0) { | ||||
nvgSave(vg); | nvgSave(vg); | ||||
nvgGlobalAlpha(vg, opacity); | |||||
nvgGlobalAlpha(vg, powf(opacity, 1.5)); | |||||
float dist = pos1.minus(pos2).norm(); | float dist = pos1.minus(pos2).norm(); | ||||
Vec slump; | Vec slump; | ||||