add support for high res brush textures, implement mipmaps on brush
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,3 +11,6 @@ android/.gradle/
|
||||
android/.externalNativeBuild/
|
||||
|
||||
android/src/main/assets/
|
||||
android/.idea
|
||||
android/android.iml
|
||||
android/local.properties
|
||||
|
||||
BIN
data/brushes/Round-Brush.png
Normal file
BIN
data/brushes/Round-Brush.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
BIN
data/brushes/Round-Hard.png
Normal file
BIN
data/brushes/Round-Hard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/brushes/Square-Hard.png
Normal file
BIN
data/brushes/Square-Hard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
BIN
data/thumbs/Round-Brush.png
Normal file
BIN
data/thumbs/Round-Brush.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
BIN
data/thumbs/Round-Hard.png
Normal file
BIN
data/thumbs/Round-Hard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/thumbs/Square-Hard.png
Normal file
BIN
data/thumbs/Square-Hard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
@@ -129,8 +129,8 @@ void App::init_sidebar()
|
||||
}
|
||||
|
||||
brushes->on_brush_changed = [this](Node* target, int index) {
|
||||
auto tid = brushes->get_texture_id(index);
|
||||
stroke->m_canvas->m_brush.m_tex_id = tid;
|
||||
stroke->m_canvas->m_brush.m_tex_id = brushes->get_texture_id(index);
|
||||
stroke->m_canvas->m_brush.id = brushes->get_brush_id(index);
|
||||
stroke->m_canvas->draw_stroke();
|
||||
canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
if (on_brush_select)
|
||||
@@ -375,7 +375,7 @@ void App::init_menu_edit()
|
||||
|
||||
void App::brush_update()
|
||||
{
|
||||
brushes->set_texture_id(canvas->m_brush.m_tex_id);
|
||||
brushes->select_brush(canvas->m_brush.id);
|
||||
stroke->set_params(canvas->m_brush);
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ void ui::Stroke::add_point(glm::vec2 pos, float pressure)
|
||||
pressure = pressure * glm::pow(m_curve, 2.f);
|
||||
|
||||
if (m_brush.m_tip_size_pressure)
|
||||
m_step = glm::max(m_brush.m_tip_spacing * m_brush.m_tip_size * 100 * pressure, 1.f);
|
||||
m_step = glm::max(glm::pow(m_brush.m_tip_spacing * 4.f, 2.f) * glm::pow(m_brush.m_tip_size, 3.f) * 800.f * pressure, 1.f);
|
||||
|
||||
float dist = m_keypoints.empty() ? 0.f :
|
||||
m_keypoints.back().dist + glm::distance(m_keypoints.back().pos, pos);
|
||||
@@ -244,7 +244,7 @@ void ui::Stroke::start(const ui::Brush& brush)
|
||||
m_curve_angles.clear();
|
||||
m_last_kp = 0;
|
||||
m_dist = 0.f;
|
||||
m_step = glm::max(brush.m_tip_spacing * brush.m_tip_size * 100, 1.f);
|
||||
m_brush = brush;
|
||||
m_step = glm::max(glm::pow(m_brush.m_tip_spacing * 4.f, 2.f) * glm::pow(m_brush.m_tip_size, 3.f) * 800.f, 1.f);
|
||||
prng.seed(0);
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ void ui::Canvas::stroke_draw()
|
||||
auto samples = m_current_stroke->compute_samples();
|
||||
auto& tex = TextureManager::get(m_brush.m_tex_id);
|
||||
tex.bind();
|
||||
m_sampler.bind(0);
|
||||
m_sampler_brush.bind(0);
|
||||
m_sampler_bg.bind(1);
|
||||
m_sampler_mask.bind(2);
|
||||
|
||||
@@ -233,7 +233,7 @@ void ui::Canvas::stroke_draw()
|
||||
ShaderManager::u_vec2(kShaderUniform::Resolution, { m_width, m_height });
|
||||
for (const auto& s : samples)
|
||||
{
|
||||
glm::vec2 dx(s.size, 0), dy(0, s.size);
|
||||
glm::vec2 dx(s.size * 0.5f, 0), dy(0, s.size * 0.5f);
|
||||
glm::vec2 off[4] = {
|
||||
- dx - dy, // A - bottom-left
|
||||
- dx + dy, // B - top-left
|
||||
@@ -317,7 +317,7 @@ void ui::Canvas::stroke_draw()
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_sampler.unbind();
|
||||
m_sampler_brush.unbind();
|
||||
m_sampler_bg.unbind();
|
||||
m_sampler_mask.unbind();
|
||||
tex.unbind();
|
||||
@@ -695,6 +695,8 @@ bool ui::Canvas::create(int width, int height)
|
||||
m_tex2[i].create(width, height); // TODO: destroy before recreating
|
||||
}
|
||||
m_sampler.create(GL_NEAREST);
|
||||
m_sampler_brush.create();
|
||||
m_sampler_brush.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
|
||||
m_sampler_bg.create(GL_NEAREST);
|
||||
m_sampler_mask.create(GL_NEAREST);
|
||||
m_plane.create<1>(1, 1);
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
static glm::vec3 m_plane_tangent[6];
|
||||
static glm::mat4 m_plane_transform[6];
|
||||
Sampler m_sampler;
|
||||
Sampler m_sampler_brush;
|
||||
Sampler m_sampler_bg;
|
||||
Sampler m_sampler_mask;
|
||||
glm::vec2 m_cam_rot;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "log.h"
|
||||
#include "node_panel_brush.h"
|
||||
#include "asset.h"
|
||||
#include "texture.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Foundation/Foundation.h>
|
||||
@@ -44,14 +45,15 @@ void NodePanelBrush::init()
|
||||
{
|
||||
init_template("tpl-panel-brushes");
|
||||
//m_layers_container = find<NodeBorder>("layers-container");
|
||||
static auto icons = Asset::list_files("data/Icons", true, ".*\\.png$");
|
||||
static auto icons = Asset::list_files("data/thumbs", true, ".*\\.png$");
|
||||
|
||||
if ((m_container = find<NodeBorder>("brushes")))
|
||||
{
|
||||
int count = 0;
|
||||
for (auto& i : icons)
|
||||
{
|
||||
std::string path = "data/Icons/" + i;
|
||||
std::string path = "data/thumbs/" + i;
|
||||
std::string path_hi = "data/brushes/" + i;
|
||||
NodeButtonBrush* brush = new NodeButtonBrush;
|
||||
m_container->add_child(brush);
|
||||
brush->init();
|
||||
@@ -59,6 +61,8 @@ void NodePanelBrush::init()
|
||||
brush->loaded();
|
||||
brush->set_icon(path.c_str());
|
||||
brush->m_brushID = count++;
|
||||
brush->high_path = path_hi;
|
||||
brush->high_id = const_hash(path_hi.c_str());
|
||||
m_brushes.push_back(brush);
|
||||
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
||||
}
|
||||
@@ -79,17 +83,23 @@ void NodePanelBrush::handle_click(Node* target)
|
||||
|
||||
uint16_t NodePanelBrush::get_texture_id(int index) const
|
||||
{
|
||||
return m_brushes[index]->img->m_tex_id;
|
||||
TextureManager::load(m_brushes[index]->high_path.c_str(), true);
|
||||
return m_brushes[index]->high_id;
|
||||
}
|
||||
|
||||
int NodePanelBrush::get_brush_id(int index) const
|
||||
{
|
||||
return m_brushes[index]->m_brushID;
|
||||
}
|
||||
|
||||
// select the current brush based on the texture id
|
||||
void NodePanelBrush::set_texture_id(int texid)
|
||||
void NodePanelBrush::select_brush(int brush_id)
|
||||
{
|
||||
if (m_current)
|
||||
m_current->m_selected = false;
|
||||
for (auto b : m_brushes)
|
||||
{
|
||||
if (b->img->m_tex_id == texid)
|
||||
if (b->m_brushID == brush_id)
|
||||
{
|
||||
b->m_selected = true;
|
||||
m_current = b;
|
||||
|
||||
@@ -8,6 +8,8 @@ class NodeButtonBrush : public NodeButtonCustom
|
||||
public:
|
||||
int m_brushID;
|
||||
bool m_selected = false;
|
||||
std::string high_path;
|
||||
uint16_t high_id;
|
||||
NodeImage* img;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
@@ -27,5 +29,6 @@ public:
|
||||
void handle_click(Node* target);
|
||||
std::vector<std::string> FindAllBrushes(const std::string& folder);
|
||||
uint16_t get_texture_id(int index) const;
|
||||
void set_texture_id(int texid);
|
||||
int get_brush_id(int index) const;
|
||||
void select_brush(int brush_id);
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ void NodeStrokePreview::init_controls()
|
||||
{
|
||||
m_mesh.create();
|
||||
m_sampler.create();
|
||||
m_sampler_brush.create(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
|
||||
TextureManager::load("data/Icons/Round-Hard.png");
|
||||
m_brush.m_tex_id = const_hash("data/Icons/Round-Hard.png");
|
||||
}
|
||||
@@ -66,7 +67,7 @@ void NodeStrokePreview::draw_stroke()
|
||||
glm::mat4 proj = glm::ortho<float>(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1);
|
||||
|
||||
auto b = m_brush;
|
||||
b.m_tip_size *= .7f; // reduce the size in the preview
|
||||
//b.m_tip_size *= .7f; // reduce the size in the preview
|
||||
m_stroke.reset();
|
||||
m_stroke.start(b);
|
||||
if (!m_stroke.m_keypoints.empty())
|
||||
@@ -74,7 +75,7 @@ void NodeStrokePreview::draw_stroke()
|
||||
auto samples = m_stroke.compute_samples();
|
||||
auto& tex = TextureManager::get(m_brush.m_tex_id);
|
||||
tex.bind();
|
||||
m_sampler.bind(0);
|
||||
m_sampler_brush.bind(0);
|
||||
|
||||
if (true)
|
||||
{
|
||||
@@ -100,7 +101,7 @@ void NodeStrokePreview::draw_stroke()
|
||||
// }
|
||||
//}
|
||||
|
||||
m_sampler.unbind();
|
||||
m_sampler_brush.unbind();
|
||||
tex.unbind();
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
@@ -126,6 +127,7 @@ void NodeStrokePreview::draw()
|
||||
void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size)
|
||||
{
|
||||
float pad = 30.f;
|
||||
new_size *= root()->m_zoom;
|
||||
float w = new_size.x;
|
||||
float h = new_size.y;
|
||||
std::vector<glm::vec2> kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } };
|
||||
|
||||
@@ -8,6 +8,7 @@ class NodeStrokePreview : public NodeBorder
|
||||
{
|
||||
RTT m_rtt;
|
||||
Sampler m_sampler;
|
||||
Sampler m_sampler_brush;
|
||||
ui::BrushMesh m_mesh;
|
||||
public:
|
||||
ui::Brush m_brush;
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
|
||||
std::map<uint16_t, Texture2D> TextureManager::m_textures;
|
||||
|
||||
bool TextureManager::load(const char* path)
|
||||
bool TextureManager::load(const char* path, bool generate_mipmaps)
|
||||
{
|
||||
uint16_t id = const_hash(path);
|
||||
if (m_textures.count(id) == 0 || !m_textures[id].ready())
|
||||
{
|
||||
return m_textures[id].load(path);
|
||||
if (!m_textures[id].load(path))
|
||||
return false;
|
||||
if (generate_mipmaps)
|
||||
m_textures[id].create_mipmaps();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -54,6 +57,13 @@ bool Texture2D::create(const ui::Image& img)
|
||||
return create(img.width, img.height, iformats[img.comp - 1], formats[img.comp - 1], img.data());
|
||||
}
|
||||
|
||||
void Texture2D::create_mipmaps()
|
||||
{
|
||||
bind();
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
unbind();
|
||||
}
|
||||
|
||||
void Texture2D::assign(GLuint tex, int w/* = -1*/, int h/* = -1*/, GLuint internal_format/* = GL_RGBA8*/, GLuint format/* = GL_RGBA*/)
|
||||
{
|
||||
m_tex = tex;
|
||||
@@ -102,6 +112,13 @@ void Sampler::set(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*
|
||||
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter);
|
||||
#endif // USE_SAMPLER
|
||||
}
|
||||
void Sampler::set_filter(GLint filter_min, GLint filter_mag)
|
||||
{
|
||||
#if USE_SAMPLER
|
||||
glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter_min);
|
||||
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag);
|
||||
#endif // USE_SAMPLER
|
||||
}
|
||||
void Sampler::bind(int unit)
|
||||
{
|
||||
current_unit = unit;
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
void unbind() const { glBindTexture(GL_TEXTURE_2D, 0); }
|
||||
void update(const uint8_t* data);
|
||||
bool ready() const { return m_tex != 0; }
|
||||
void create_mipmaps();
|
||||
glm::vec2 size() const;
|
||||
};
|
||||
|
||||
@@ -28,6 +29,7 @@ class Sampler
|
||||
public:
|
||||
bool create(GLint filter = GL_LINEAR, GLint wrap = GL_CLAMP_TO_EDGE);
|
||||
void set(GLint filter = GL_LINEAR, GLint wrap = GL_CLAMP_TO_EDGE);
|
||||
void set_filter(GLint filter_min, GLint filter_mag);
|
||||
void bind(int unit);
|
||||
void unbind();
|
||||
bool ready() const { return id != 0; }
|
||||
@@ -37,7 +39,7 @@ class TextureManager
|
||||
{
|
||||
public:
|
||||
static std::map<uint16_t, Texture2D> m_textures;
|
||||
static bool load(const char* path);
|
||||
static bool load(const char* path, bool generate_mipmpas = false);
|
||||
static void assign(uint16_t id, GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA);
|
||||
static Texture2D& get(uint16_t id);
|
||||
static void invalidate();
|
||||
|
||||
Reference in New Issue
Block a user