diff --git a/.gitignore b/.gitignore index 2a7d08a..69d9247 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ android/.gradle/ android/.externalNativeBuild/ android/src/main/assets/ +android/.idea +android/android.iml +android/local.properties diff --git a/data/brushes/Round-Brush.png b/data/brushes/Round-Brush.png new file mode 100644 index 0000000..591b6fc Binary files /dev/null and b/data/brushes/Round-Brush.png differ diff --git a/data/brushes/Round-Hard.png b/data/brushes/Round-Hard.png new file mode 100644 index 0000000..0ec829d Binary files /dev/null and b/data/brushes/Round-Hard.png differ diff --git a/data/brushes/Square-Hard.png b/data/brushes/Square-Hard.png new file mode 100644 index 0000000..e2c57d4 Binary files /dev/null and b/data/brushes/Square-Hard.png differ diff --git a/data/thumbs/Round-Brush.png b/data/thumbs/Round-Brush.png new file mode 100644 index 0000000..591b6fc Binary files /dev/null and b/data/thumbs/Round-Brush.png differ diff --git a/data/thumbs/Round-Hard.png b/data/thumbs/Round-Hard.png new file mode 100644 index 0000000..0ec829d Binary files /dev/null and b/data/thumbs/Round-Hard.png differ diff --git a/data/thumbs/Square-Hard.png b/data/thumbs/Square-Hard.png new file mode 100644 index 0000000..e2c57d4 Binary files /dev/null and b/data/thumbs/Square-Hard.png differ diff --git a/engine/app_layout.cpp b/engine/app_layout.cpp index 1534e65..da4229e 100644 --- a/engine/app_layout.cpp +++ b/engine/app_layout.cpp @@ -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); } diff --git a/engine/brush.cpp b/engine/brush.cpp index b3d1cc5..aaaad8b 100644 --- a/engine/brush.cpp +++ b/engine/brush.cpp @@ -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); } diff --git a/engine/canvas.cpp b/engine/canvas.cpp index c0428c0..54a2333 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -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); diff --git a/engine/canvas.h b/engine/canvas.h index 3d2dc61..82f4e4e 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -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; diff --git a/engine/node_panel_brush.cpp b/engine/node_panel_brush.cpp index e68ce5c..86b48b0 100644 --- a/engine/node_panel_brush.cpp +++ b/engine/node_panel_brush.cpp @@ -2,6 +2,7 @@ #include "log.h" #include "node_panel_brush.h" #include "asset.h" +#include "texture.h" #ifdef __APPLE__ #include @@ -44,14 +45,15 @@ void NodePanelBrush::init() { init_template("tpl-panel-brushes"); //m_layers_container = find("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("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; diff --git a/engine/node_panel_brush.h b/engine/node_panel_brush.h index 3d154ac..a766615 100644 --- a/engine/node_panel_brush.h +++ b/engine/node_panel_brush.h @@ -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 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); }; diff --git a/engine/node_stroke_preview.cpp b/engine/node_stroke_preview.cpp index ca0d1b3..234c409 100644 --- a/engine/node_stroke_preview.cpp +++ b/engine/node_stroke_preview.cpp @@ -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(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 kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; diff --git a/engine/node_stroke_preview.h b/engine/node_stroke_preview.h index 7a1d625..4feba46 100644 --- a/engine/node_stroke_preview.h +++ b/engine/node_stroke_preview.h @@ -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; diff --git a/engine/texture.cpp b/engine/texture.cpp index bd9c5be..2aca1a7 100644 --- a/engine/texture.cpp +++ b/engine/texture.cpp @@ -5,12 +5,15 @@ std::map 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; diff --git a/engine/texture.h b/engine/texture.h index 880fe26..74ee20d 100644 --- a/engine/texture.h +++ b/engine/texture.h @@ -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 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();