implement alpha locking per layer
This commit is contained in:
@@ -349,7 +349,7 @@
|
|||||||
</node>
|
</node>
|
||||||
</border>
|
</border>
|
||||||
<!-- toolbar -->
|
<!-- toolbar -->
|
||||||
<border id="toolbar" height="50" width="100%" pad="5" dir="row" color=".2">
|
<border id="toolbar" height="50" width="100%" pad="5" dir="row" color=".2 .2 .2 .6">
|
||||||
<button id="btn-open" width="50" height="100%" margin="0 5 0 0" text="Open"/>
|
<button id="btn-open" width="50" height="100%" margin="0 5 0 0" text="Open"/>
|
||||||
<button id="btn-save" width="50" height="100%" margin="0 5 0 0" text="Save"/>
|
<button id="btn-save" width="50" height="100%" margin="0 5 0 0" text="Save"/>
|
||||||
<button id="btn-export" width="50" height="100%" margin="0 5 0 0" text="Export"/>
|
<button id="btn-export" width="50" height="100%" margin="0 5 0 0" text="Export"/>
|
||||||
|
|||||||
@@ -195,6 +195,28 @@ void App::initShaders()
|
|||||||
" mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
|
" mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
|
||||||
" frag = vec4(rgb, alpha_tot);\n"
|
" frag = vec4(rgb, alpha_tot);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
// ALPHA LOCK
|
||||||
|
static const char* shader_stroke_lock_f =
|
||||||
|
SHADER_VERSION
|
||||||
|
"uniform mediump sampler2D tex;\n"
|
||||||
|
"uniform mediump sampler2D tex_bg;\n"
|
||||||
|
"uniform mediump sampler2D tex_mask;\n"
|
||||||
|
"uniform mediump vec4 col;\n"
|
||||||
|
"uniform mediump vec2 resolution;\n"
|
||||||
|
"uniform mediump float alpha;\n"
|
||||||
|
"in mediump vec2 uv;\n"
|
||||||
|
"out mediump vec4 frag;\n"
|
||||||
|
"void main(){\n"
|
||||||
|
" mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
|
||||||
|
" mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
|
||||||
|
" mediump vec4 fg = vec4(col.rgb, brush_alpha);\n"
|
||||||
|
" mediump vec4 bg = texture(tex_bg, uv2);\n"
|
||||||
|
" mediump vec4 msk = texture(tex_mask, uv2);\n"
|
||||||
|
" if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
|
||||||
|
" mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
|
||||||
|
" mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
|
||||||
|
" frag = vec4(rgb, min(alpha_tot, msk.a));\n"
|
||||||
|
"}\n";
|
||||||
// ERASER
|
// ERASER
|
||||||
static const char* shader_stroke_erase_f =
|
static const char* shader_stroke_erase_f =
|
||||||
SHADER_VERSION
|
SHADER_VERSION
|
||||||
@@ -310,6 +332,8 @@ void App::initShaders()
|
|||||||
LOG("Failed to create shader Atlas");
|
LOG("Failed to create shader Atlas");
|
||||||
if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f))
|
if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f))
|
||||||
LOG("Failed to create shader Stroke");
|
LOG("Failed to create shader Stroke");
|
||||||
|
if (!ShaderManager::create(kShader::StrokeLock, shader_stroke_v, shader_stroke_lock_f))
|
||||||
|
LOG("Failed to create shader StrokeLock");
|
||||||
if (!ShaderManager::create(kShader::StrokeErase, shader_stroke_v, shader_stroke_erase_f))
|
if (!ShaderManager::create(kShader::StrokeErase, shader_stroke_v, shader_stroke_erase_f))
|
||||||
LOG("Failed to create shader StrokeErase");
|
LOG("Failed to create shader StrokeErase");
|
||||||
if (!ShaderManager::create(kShader::StrokeLayer, shader_stroke_layer_v, shader_stroke_layer_f))
|
if (!ShaderManager::create(kShader::StrokeLayer, shader_stroke_layer_v, shader_stroke_layer_f))
|
||||||
@@ -432,6 +456,10 @@ void App::initLayout()
|
|||||||
canvas->m_canvas->m_layers[idx].m_opacity = value;
|
canvas->m_canvas->m_layers[idx].m_opacity = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
|
||||||
|
canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_alpha_locked = visible;
|
||||||
|
};
|
||||||
|
|
||||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-bucket"))
|
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-bucket"))
|
||||||
{
|
{
|
||||||
button->on_click = [this](Node*) {
|
button->on_click = [this](Node*) {
|
||||||
@@ -602,7 +630,7 @@ void App::initLayout()
|
|||||||
|
|
||||||
void App::initLog()
|
void App::initLog()
|
||||||
{
|
{
|
||||||
//LogRemote::I.start();
|
LogRemote::I.start();
|
||||||
}
|
}
|
||||||
void App::init()
|
void App::init()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ void ui::Canvas::stroke_draw()
|
|||||||
tex.bind();
|
tex.bind();
|
||||||
m_sampler.bind(0);
|
m_sampler.bind(0);
|
||||||
m_sampler_bg.bind(1);
|
m_sampler_bg.bind(1);
|
||||||
|
m_sampler_mask.bind(2);
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
@@ -92,6 +93,15 @@ void ui::Canvas::stroke_draw()
|
|||||||
ShaderManager::use(ui::kShader::StrokeErase);
|
ShaderManager::use(ui::kShader::StrokeErase);
|
||||||
//ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color);
|
//ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color);
|
||||||
}
|
}
|
||||||
|
else if(m_layers[m_current_layer_idx].m_alpha_locked)
|
||||||
|
{
|
||||||
|
ShaderManager::use(kShader::StrokeLock);
|
||||||
|
ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color);
|
||||||
|
ShaderManager::u_int(kShaderUniform::TexMask, 2); // alpha mask
|
||||||
|
glActiveTexture(GL_TEXTURE2);
|
||||||
|
m_layers[m_current_layer_idx].m_rtt[i].bindTexture();
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ShaderManager::use(ui::kShader::Stroke);
|
ShaderManager::use(ui::kShader::Stroke);
|
||||||
@@ -196,7 +206,15 @@ void ui::Canvas::stroke_draw()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_alpha_lock)
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE2);
|
||||||
|
m_layers[m_current_layer_idx].m_rtt[i].unbindTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
m_tex[i].unbind();
|
m_tex[i].unbind();
|
||||||
|
|
||||||
m_tmp[i].unbindFramebuffer();
|
m_tmp[i].unbindFramebuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +223,7 @@ void ui::Canvas::stroke_draw()
|
|||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
m_sampler.unbind();
|
m_sampler.unbind();
|
||||||
m_sampler_bg.unbind();
|
m_sampler_bg.unbind();
|
||||||
|
m_sampler_mask.unbind();
|
||||||
tex.unbind();
|
tex.unbind();
|
||||||
|
|
||||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||||
@@ -361,6 +380,7 @@ bool ui::Canvas::create(int width, int height)
|
|||||||
}
|
}
|
||||||
m_sampler.create();
|
m_sampler.create();
|
||||||
m_sampler_bg.create();
|
m_sampler_bg.create();
|
||||||
|
m_sampler_mask.create();
|
||||||
m_plane.create<1>(1, 1);
|
m_plane.create<1>(1, 1);
|
||||||
m_plane_brush.create<1>(1, 1);
|
m_plane_brush.create<1>(1, 1);
|
||||||
m_mesh.create();
|
m_mesh.create();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Layer
|
|||||||
public:
|
public:
|
||||||
RTT m_rtt[6];
|
RTT m_rtt[6];
|
||||||
bool m_visible = true;
|
bool m_visible = true;
|
||||||
bool m_locked = false;
|
bool m_alpha_locked = false;
|
||||||
float m_opacity = 1.f;
|
float m_opacity = 1.f;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
struct Snapshot
|
struct Snapshot
|
||||||
@@ -41,6 +41,7 @@ class Canvas
|
|||||||
public:
|
public:
|
||||||
static Canvas* I;
|
static Canvas* I;
|
||||||
bool m_erase = false;
|
bool m_erase = false;
|
||||||
|
bool m_alpha_lock = false;
|
||||||
glm::mat4 m_mv;
|
glm::mat4 m_mv;
|
||||||
glm::mat4 m_proj;
|
glm::mat4 m_proj;
|
||||||
glm::vec4 m_box;
|
glm::vec4 m_box;
|
||||||
@@ -63,6 +64,7 @@ public:
|
|||||||
static glm::mat4 m_plane_transform[6];
|
static glm::mat4 m_plane_transform[6];
|
||||||
Sampler m_sampler;
|
Sampler m_sampler;
|
||||||
Sampler m_sampler_bg;
|
Sampler m_sampler_bg;
|
||||||
|
Sampler m_sampler_mask;
|
||||||
glm::vec2 m_cam_rot;
|
glm::vec2 m_cam_rot;
|
||||||
float m_cam_fov = 85;
|
float m_cam_fov = 85;
|
||||||
|
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ kKey convert_key(int key)
|
|||||||
CASE(VK_NONCONVERT, kKey::Unknown);
|
CASE(VK_NONCONVERT, kKey::Unknown);
|
||||||
CASE(VK_ACCEPT, kKey::Unknown);
|
CASE(VK_ACCEPT, kKey::Unknown);
|
||||||
CASE(VK_MODECHANGE, kKey::Unknown);
|
CASE(VK_MODECHANGE, kKey::Unknown);
|
||||||
CASE(VK_SPACE, kKey::Unknown);
|
CASE(VK_SPACE, kKey::KeySpacebar);
|
||||||
CASE(VK_PRIOR, kKey::Unknown);
|
CASE(VK_PRIOR, kKey::Unknown);
|
||||||
CASE(VK_NEXT, kKey::Unknown);
|
CASE(VK_NEXT, kKey::Unknown);
|
||||||
CASE(VK_END, kKey::Unknown);
|
CASE(VK_END, kKey::Unknown);
|
||||||
|
|||||||
@@ -1192,6 +1192,7 @@ public:
|
|||||||
class NodeCheckBox : public Node
|
class NodeCheckBox : public Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
std::function<void(Node* target, bool checked)> on_value_changed;
|
||||||
NodeBorder* m_outer;
|
NodeBorder* m_outer;
|
||||||
NodeBorder* m_inner;
|
NodeBorder* m_inner;
|
||||||
bool checked = false;
|
bool checked = false;
|
||||||
@@ -1244,6 +1245,8 @@ public:
|
|||||||
break;
|
break;
|
||||||
case kEventType::MouseUpL:
|
case kEventType::MouseUpL:
|
||||||
checked = !checked;
|
checked = !checked;
|
||||||
|
if (on_value_changed)
|
||||||
|
on_value_changed(this, checked);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return kEventResult::Available;
|
return kEventResult::Available;
|
||||||
@@ -1263,6 +1266,7 @@ class NodeLayer : public NodeBorder
|
|||||||
public:
|
public:
|
||||||
std::function<void(NodeLayer* target)> on_selected;
|
std::function<void(NodeLayer* target)> on_selected;
|
||||||
std::function<void(NodeLayer* target, float opacity)> on_opacity_changed;
|
std::function<void(NodeLayer* target, float opacity)> on_opacity_changed;
|
||||||
|
std::function<void(NodeLayer* target, bool visible)> on_visibility_changed;
|
||||||
bool m_selected = false;
|
bool m_selected = false;
|
||||||
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
|
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
|
||||||
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
|
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
|
||||||
@@ -1319,6 +1323,10 @@ public:
|
|||||||
if (on_opacity_changed)
|
if (on_opacity_changed)
|
||||||
on_opacity_changed(this, value);
|
on_opacity_changed(this, value);
|
||||||
};
|
};
|
||||||
|
m_visibility->on_value_changed = [this](Node*, bool checked) {
|
||||||
|
if (on_visibility_changed)
|
||||||
|
on_visibility_changed(this, checked);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
virtual kEventResult handle_event(Event* e) override
|
virtual kEventResult handle_event(Event* e) override
|
||||||
{
|
{
|
||||||
@@ -1366,6 +1374,7 @@ class NodePanelLayer : public Node
|
|||||||
public:
|
public:
|
||||||
std::function<void(Node* target, int old_idx, int new_idx)> on_layer_change;
|
std::function<void(Node* target, int old_idx, int new_idx)> on_layer_change;
|
||||||
std::function<void(Node* target, int idx, float value)> on_layer_opacity_changed;
|
std::function<void(Node* target, int idx, float value)> on_layer_opacity_changed;
|
||||||
|
std::function<void(Node* target, int idx, bool visible)> on_layer_visibility_changed;
|
||||||
std::function<void(Node* target, int index)> on_layer_delete;
|
std::function<void(Node* target, int index)> on_layer_delete;
|
||||||
std::function<void(Node* target)> on_layer_add;
|
std::function<void(Node* target)> on_layer_add;
|
||||||
std::function<void(Node* target, int old_idx, int new_idx)> on_layer_order;
|
std::function<void(Node* target, int old_idx, int new_idx)> on_layer_order;
|
||||||
@@ -1439,6 +1448,7 @@ public:
|
|||||||
l->set_name(name);
|
l->set_name(name);
|
||||||
l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1);
|
l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1);
|
||||||
l->on_opacity_changed = std::bind(&NodePanelLayer::handle_layer_opacity, this, std::placeholders::_1, std::placeholders::_2);
|
l->on_opacity_changed = std::bind(&NodePanelLayer::handle_layer_opacity, this, std::placeholders::_1, std::placeholders::_2);
|
||||||
|
l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2);
|
||||||
m_layers.push_back(l);
|
m_layers.push_back(l);
|
||||||
}
|
}
|
||||||
void remove_layer(NodeLayer* layer)
|
void remove_layer(NodeLayer* layer)
|
||||||
@@ -1460,6 +1470,11 @@ public:
|
|||||||
if (on_layer_opacity_changed)
|
if (on_layer_opacity_changed)
|
||||||
on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value);
|
on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value);
|
||||||
}
|
}
|
||||||
|
void handle_layer_visibility(NodeLayer* target, bool visible)
|
||||||
|
{
|
||||||
|
if (on_layer_visibility_changed)
|
||||||
|
on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible);
|
||||||
|
}
|
||||||
void handle_layer_selected(NodeLayer* target)
|
void handle_layer_selected(NodeLayer* target)
|
||||||
{
|
{
|
||||||
if (m_current_layer)
|
if (m_current_layer)
|
||||||
@@ -1945,7 +1960,6 @@ class NodeCanvas : public Node
|
|||||||
float m_camera_fov;
|
float m_camera_fov;
|
||||||
float m_zoom_canvas = 1.f;
|
float m_zoom_canvas = 1.f;
|
||||||
float m_zoom_start;
|
float m_zoom_start;
|
||||||
bool method = true;
|
|
||||||
public:
|
public:
|
||||||
std::string data_path;
|
std::string data_path;
|
||||||
std::unique_ptr<ui::Canvas> m_canvas;
|
std::unique_ptr<ui::Canvas> m_canvas;
|
||||||
@@ -2033,7 +2047,6 @@ public:
|
|||||||
}
|
}
|
||||||
if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
|
if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
|
||||||
{
|
{
|
||||||
glEnable(GL_BLEND);
|
|
||||||
ui::ShaderManager::u_float(kShaderUniform::Alpha,
|
ui::ShaderManager::u_float(kShaderUniform::Alpha,
|
||||||
m_canvas->m_current_stroke->m_brush.m_tip_opacity * m_canvas->m_layers[layer_index].m_opacity);
|
m_canvas->m_current_stroke->m_brush.m_tip_opacity * m_canvas->m_layers[layer_index].m_opacity);
|
||||||
m_canvas->m_tmp[plane_index].bindTexture();
|
m_canvas->m_tmp[plane_index].bindTexture();
|
||||||
@@ -2112,10 +2125,9 @@ public:
|
|||||||
break;
|
break;
|
||||||
case kEventType::KeyDown:
|
case kEventType::KeyDown:
|
||||||
if (ke->m_key == kKey::KeyE)
|
if (ke->m_key == kKey::KeyE)
|
||||||
{
|
|
||||||
m_canvas->m_erase = true;
|
m_canvas->m_erase = true;
|
||||||
method = !method;
|
if (ke->m_key == kKey::KeySpacebar)
|
||||||
}
|
m_canvas->m_alpha_lock = true;
|
||||||
if (ke->m_key == kKey::AndroidVolumeUp)
|
if (ke->m_key == kKey::AndroidVolumeUp)
|
||||||
m_zoom_canvas *= 0.9f;
|
m_zoom_canvas *= 0.9f;
|
||||||
if (ke->m_key == kKey::AndroidVolumeDown)
|
if (ke->m_key == kKey::AndroidVolumeDown)
|
||||||
@@ -2124,6 +2136,8 @@ public:
|
|||||||
case kEventType::KeyUp:
|
case kEventType::KeyUp:
|
||||||
if (ke->m_key == kKey::KeyE)
|
if (ke->m_key == kKey::KeyE)
|
||||||
m_canvas->m_erase = false;
|
m_canvas->m_erase = false;
|
||||||
|
if (ke->m_key == kKey::KeySpacebar)
|
||||||
|
m_canvas->m_alpha_lock = false;
|
||||||
break;
|
break;
|
||||||
case kEventType::GestureStart:
|
case kEventType::GestureStart:
|
||||||
m_pan_start = m_pan;
|
m_pan_start = m_pan;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ enum class kShaderUniform : uint16_t
|
|||||||
Tex = const_hash("tex"),
|
Tex = const_hash("tex"),
|
||||||
TexFG = const_hash("tex_fg"),
|
TexFG = const_hash("tex_fg"),
|
||||||
TexBG = const_hash("tex_bg"),
|
TexBG = const_hash("tex_bg"),
|
||||||
|
TexMask = const_hash("tex_mask"),
|
||||||
Col = const_hash("col"),
|
Col = const_hash("col"),
|
||||||
Tof = const_hash("tof"),
|
Tof = const_hash("tof"),
|
||||||
Tsz = const_hash("tsz"),
|
Tsz = const_hash("tsz"),
|
||||||
@@ -27,6 +28,7 @@ enum class kShader : uint16_t
|
|||||||
Font = const_hash("font"),
|
Font = const_hash("font"),
|
||||||
Atlas = const_hash("atlas"),
|
Atlas = const_hash("atlas"),
|
||||||
Stroke = const_hash("stroke"),
|
Stroke = const_hash("stroke"),
|
||||||
|
StrokeLock = const_hash("stroke-lock"),
|
||||||
StrokeErase = const_hash("stroke-erase"),
|
StrokeErase = const_hash("stroke-erase"),
|
||||||
StrokeLayer = const_hash("stroke-layer"),
|
StrokeLayer = const_hash("stroke-layer"),
|
||||||
Checkerboard= const_hash("checkerboard"),
|
Checkerboard= const_hash("checkerboard"),
|
||||||
|
|||||||
Reference in New Issue
Block a user