Browse Source

Rewrite LightWidget rendering by using more physical light blending function. Add ability to decrease rack brightness by tinting all but the lights a shade of gray. Add settings and menu items to adjust rack brightness and light halo.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
9d81a3e4d0
6 changed files with 140 additions and 27 deletions
  1. +1
    -1
      dep/nanovg
  2. +2
    -0
      include/settings.hpp
  3. +36
    -20
      src/app/LightWidget.cpp
  4. +81
    -4
      src/app/MenuBar.cpp
  5. +4
    -0
      src/app/RackWidget.cpp
  6. +16
    -2
      src/settings.cpp

+ 1
- 1
dep/nanovg

@@ -1 +1 @@
Subproject commit 1f9c8864fc556a1be4d4bf1d6bfe20cde25734b4
Subproject commit d201a1cf69940450960db87c8e30b883087c9d01

+ 2
- 0
include/settings.hpp View File

@@ -44,6 +44,8 @@ extern bool invertZoom;
extern float cableOpacity;
/** Straightness of cables in the range [0, 1]. Unitless and arbitrary. */
extern float cableTension;
extern float rackBrightness;
extern float haloBrightness;
/** Allows rack to hide and lock the cursor position when dragging knobs etc. */
extern bool allowCursorLock;
enum KnobMode {


+ 36
- 20
src/app/LightWidget.cpp View File

@@ -1,5 +1,6 @@
#include <app/LightWidget.hpp>
#include <color.hpp>
#include <settings.hpp>


namespace rack {
@@ -7,26 +8,15 @@ namespace app {


void LightWidget::draw(const DrawArgs& args) {
drawLight(args);
TransparentWidget::draw(args);
drawHalo(args);
}

void LightWidget::drawLight(const DrawArgs& args) {
float radius = std::min(box.size.x, box.size.y) / 2.0;

nvgBeginPath(args.vg);
nvgCircle(args.vg, radius, radius, radius);

// Background
if (bgColor.a > 0.0) {
nvgFillColor(args.vg, bgColor);
nvgFill(args.vg);
}

// Foreground
if (color.a > 0.0) {
nvgFillColor(args.vg, color);
// TODO Set color in TGrayModuleLightWidget instead.
nvgFillColor(args.vg, color::mult(bgColor, 0.5));
// nvgFillColor(args.vg, bgColor);
nvgFill(args.vg);
}

@@ -36,21 +26,47 @@ void LightWidget::drawLight(const DrawArgs& args) {
nvgStrokeColor(args.vg, borderColor);
nvgStroke(args.vg);
}

// Child widgets
// TODO Upload new graphics instead of use this hack.
// nvgGlobalAlpha(args.vg, 0.5);
TransparentWidget::draw(args);

// Dynamic light and halo
nvgGlobalAlpha(args.vg, 1.0);
// Use the formula `lightColor * (1 - dest) + dest` for blending
nvgGlobalCompositeBlendFunc(args.vg, NVG_ONE_MINUS_DST_COLOR, NVG_ONE);
drawLight(args);
drawHalo(args);
}

void LightWidget::drawLight(const DrawArgs& args) {
// Foreground
if (color.a > 0.0) {
float radius = std::min(box.size.x, box.size.y) / 2.0;
nvgBeginPath(args.vg);
nvgCircle(args.vg, radius, radius, radius);

nvgFillColor(args.vg, color);
nvgFill(args.vg);
}
}

void LightWidget::drawHalo(const DrawArgs& args) {
const float halo = settings::haloBrightness;
if (halo == 0.f)
return;

float radius = std::min(box.size.x, box.size.y) / 2.0;
float oradius = radius + 20.0;
float oradius = std::min(radius * 5.f, 30.f);

nvgBeginPath(args.vg);
nvgRect(args.vg, radius - oradius, radius - oradius, 2 * oradius, 2 * oradius);

NVGpaint paint;
NVGcolor icol = color::mult(color, 0.2);
NVGcolor ocol = nvgRGB(0, 0, 0);
paint = nvgRadialGradient(args.vg, radius, radius, radius, oradius, icol, ocol);
NVGcolor icol = color::mult(color, halo);
NVGcolor ocol = nvgRGBA(0, 0, 0, 0);
NVGpaint paint = nvgRadialGradient(args.vg, radius, radius, radius, oradius, icol, ocol);
nvgFillPaint(args.vg, paint);
nvgGlobalCompositeOperation(args.vg, NVG_LIGHTER);
nvgFill(args.vg);
}



+ 81
- 4
src/app/MenuBar.cpp View File

@@ -189,6 +189,8 @@ struct FileButton : MenuButton {
revertItem->disabled = (APP->patch->path == "");
menu->addChild(revertItem);

menu->addChild(new ui::MenuSeparator);

QuitItem* quitItem = new QuitItem;
quitItem->text = "Quit";
quitItem->rightText = RACK_MOD_CTRL_NAME "+Q";
@@ -248,7 +250,7 @@ struct EditButton : MenuButton {

struct ZoomQuantity : Quantity {
void setValue(float value) override {
settings::zoom = value;
settings::zoom = math::clamp(value, getMinValue(), getMaxValue());
}
float getValue() override {
return settings::zoom;
@@ -275,7 +277,6 @@ struct ZoomQuantity : Quantity {
return "%";
}
};

struct ZoomSlider : ui::Slider {
ZoomSlider() {
quantity = new ZoomQuantity;
@@ -308,7 +309,6 @@ struct CableOpacityQuantity : Quantity {
return "%";
}
};

struct CableOpacitySlider : ui::Slider {
CableOpacitySlider() {
quantity = new CableOpacityQuantity;
@@ -335,7 +335,6 @@ struct CableTensionQuantity : Quantity {
return 2;
}
};

struct CableTensionSlider : ui::Slider {
CableTensionSlider() {
quantity = new CableTensionQuantity;
@@ -345,6 +344,76 @@ struct CableTensionSlider : ui::Slider {
}
};

struct RackBrightnessQuantity : Quantity {
void setValue(float value) override {
settings::rackBrightness = math::clamp(value, getMinValue(), getMaxValue());
}
float getValue() override {
return settings::rackBrightness;
}
float getDefaultValue() override {
return 1.0;
}
float getDisplayValue() override {
return getValue() * 100;
}
void setDisplayValue(float displayValue) override {
setValue(displayValue / 100);
}
std::string getUnit() override {
return "%";
}
std::string getLabel() override {
return "Room brightness";
}
int getDisplayPrecision() override {
return 3;
}
};
struct RackBrightnessSlider : ui::Slider {
RackBrightnessSlider() {
quantity = new RackBrightnessQuantity;
}
~RackBrightnessSlider() {
delete quantity;
}
};

struct HaloBrightnessQuantity : Quantity {
void setValue(float value) override {
settings::haloBrightness = math::clamp(value, getMinValue(), getMaxValue());
}
float getValue() override {
return settings::haloBrightness;
}
float getDefaultValue() override {
return 0.0;
}
float getDisplayValue() override {
return getValue() * 100;
}
void setDisplayValue(float displayValue) override {
setValue(displayValue / 100);
}
std::string getUnit() override {
return "%";
}
std::string getLabel() override {
return "Light bloom";
}
int getDisplayPrecision() override {
return 3;
}
};
struct HaloBrightnessSlider : ui::Slider {
HaloBrightnessSlider() {
quantity = new HaloBrightnessQuantity;
}
~HaloBrightnessSlider() {
delete quantity;
}
};

struct TooltipsItem : ui::MenuItem {
void onAction(const ActionEvent& e) override {
settings::tooltips ^= true;
@@ -470,6 +539,14 @@ struct ViewButton : MenuButton {
cableTensionSlider->box.size.x = 200.0;
menu->addChild(cableTensionSlider);

RackBrightnessSlider* rackBrightnessSlider = new RackBrightnessSlider;
rackBrightnessSlider->box.size.x = 200.0;
menu->addChild(rackBrightnessSlider);

HaloBrightnessSlider* haloBrightnessSlider = new HaloBrightnessSlider;
haloBrightnessSlider->box.size.x = 200.0;
menu->addChild(haloBrightnessSlider);

FrameRateItem* frameRateItem = new FrameRateItem;
frameRateItem->text = "Frame rate";
frameRateItem->rightText = RIGHT_ARROW;


+ 4
- 0
src/app/RackWidget.cpp View File

@@ -97,6 +97,10 @@ void RackWidget::step() {
}

void RackWidget::draw(const DrawArgs& args) {
// Darken all children by user setting
float b = std::pow(settings::rackBrightness, 1.f);
nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1));

// Resize and reposition the RackRail to align on the grid.
math::Rect railBox;
railBox.pos = args.clipBox.pos.div(BUS_BOARD_GRID_SIZE).floor().mult(BUS_BOARD_GRID_SIZE);


+ 16
- 2
src/settings.cpp View File

@@ -27,6 +27,8 @@ float zoom = 0.25;
bool invertZoom = false;
float cableOpacity = 0.5;
float cableTension = 0.5;
float rackBrightness = 1.0;
float haloBrightness = 0.0;
bool allowCursorLock = true;
KnobMode knobMode = KNOB_MODE_LINEAR;
bool knobScroll = false;
@@ -103,6 +105,10 @@ json_t* toJson() {

json_object_set_new(rootJ, "cableTension", json_real(cableTension));

json_object_set_new(rootJ, "rackBrightness", json_real(rackBrightness));

json_object_set_new(rootJ, "haloBrightness", json_real(haloBrightness));

json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock));

json_object_set_new(rootJ, "knobMode", json_integer((int) knobMode));
@@ -219,6 +225,14 @@ void fromJson(json_t* rootJ) {
if (cableTensionJ)
cableTension = json_number_value(cableTensionJ);

json_t* rackBrightnessJ = json_object_get(rootJ, "rackBrightness");
if (rackBrightnessJ)
rackBrightness = json_number_value(rackBrightnessJ);

json_t* haloBrightnessJ = json_object_get(rootJ, "haloBrightness");
if (haloBrightnessJ)
haloBrightness = json_number_value(haloBrightnessJ);

json_t* allowCursorLockJ = json_object_get(rootJ, "allowCursorLock");
if (allowCursorLockJ)
allowCursorLock = json_boolean_value(allowCursorLockJ);
@@ -365,8 +379,8 @@ void save(std::string path) {
return;
DEFER({std::fclose(file);});

// Because settings include doubles, it should use 17 decimal digits of precision instead of just 9 for float32.
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(17));
// 11 is enough precision to handle double UNIX time values to 0.1 seconds.
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(11));
json_decref(rootJ);
}



Loading…
Cancel
Save