#pragma once #include "rtt.h" #include "shader.h" #include "texture.h" class Brush { public: //Brush() = default; //Brush(const Brush& brush) = default; std::string m_name; std::shared_ptr m_tip_texture; std::string m_brush_path; std::string m_brush_thumb_path; std::shared_ptr m_dual_texture; std::string m_dual_path; std::string m_dual_thumb_path; std::shared_ptr m_stencil_texture; std::string m_stencil_path; std::string m_stencil_thumb_path; glm::vec4 m_tip_color{0, 0, 0, 1}; float m_tip_size = .25f; float m_tip_spacing = .25; float m_tip_flow = 1; float m_tip_opacity = 1; float m_tip_angle = 0; float m_tip_angle_delay = 0; float m_tip_mix = 0; float m_tip_stencil = 0; float m_tip_wet = 0; float m_tip_noise = 0; float m_tip_hue = 0; float m_tip_sat = 0; float m_tip_val = 0; bool m_tip_angle_follow = false; bool m_tip_flow_pressure = false; bool m_tip_size_pressure = false; float m_jitter_scale = 0; float m_jitter_angle = 0; float m_jitter_spread = 0; float m_jitter_flow = 0; float m_jitter_hue = 0; float m_jitter_sat = 0; float m_jitter_val = 0; int m_blend_mode = 0; bool m_tip_invert = false; bool m_tip_flipx = false; bool m_tip_flipy = false; bool m_tex_enabled = false; bool m_dual_enabled = false; int m_dual_blend_mode = 0; bool m_dual_randflip = false; float m_dual_size = .25; float m_dual_spacing = .25; float m_dual_scatter = 0; bool m_dual_scatter_axis = false; bool m_dual_invert = false; bool m_dual_flipx = false; bool m_dual_flipy = false; bool m_tip_randflipx = false; bool m_tip_randflipy = false; float m_tip_aspect = 0.5f; float m_dual_flow = .75f; float m_dual_opacity = 1.f; float m_dual_rotate = .25f; bool load_texture(const std::string& path, const std::string& thumb); bool load_dual(const std::string& path, const std::string& thumb); bool load_stencil(const std::string& path, const std::string& thumb); bool load(); }; struct StrokeSample { glm::vec3 col = { 0, 0, 0 }; glm::vec3 pos = { 0, 0, 0 }; glm::vec3 origin = { 0, 0, 0 }; glm::vec2 scale = { 1, 1 }; float size = 0; float flow = 0; float angle = 0; bool valid() const { return !( glm::isnan(angle) || glm::isinf(angle) || glm::any(glm::isnan(col)) || glm::any(glm::isnan(pos)) || glm::any(glm::isnan(origin)) ); } }; class BrushMesh { public: GLuint buffers[3]{ 0 }; GLuint vao{ 0 }; struct instance_t { glm::mat4 mvp; float flow; }; int loc_flow = 0; int loc_mvp = 0; bool create(); void draw(const std::vector& samples, const glm::mat4& proj); }; class Stroke { public: struct Keypoint { glm::vec3 pos = { 0, 0, 0 }; float pressure = 0; float dist = 0; }; struct Camera { glm::mat4 rot; float fov = 0; }; int m_layer = 0; int m_dir_kp = 0; bool m_dir_valid = false; glm::vec2 m_dir_ref = { 1, 0 }; float m_dir_ref_angle = 0; float m_dir_dist = 0; float m_dir_step = 10; float m_dir_angle = 0; float m_curve = 0; float m_dist = 0; float m_step = 0; float m_max_size = FLT_MAX; bool m_filter_points = true; Camera m_camera; std::shared_ptr m_brush; cbuffer m_direction{ 1 }; cbuffer m_pressure_buff{ 10 }; cbuffer m_hsv_jitter{ 3 }; StrokeSample m_prev_sample; std::vector m_keypoints; std::vector> m_hold_points; std::vector m_samples; int m_last_kp; std::minstd_rand prng; void start(const std::shared_ptr& brush); void add_point(glm::vec3 pos, float pressure); void reset(bool clear_keypoints = false); bool has_sample(); std::vector compute_samples(); StrokeSample randomize_sample(const glm::vec3& pos, float pressure, float curve_angle); };