#pragma once #include "rtt.h" #include "shader.h" #include "texture.h" #include "serializer.h" class Brush : public Serializer::Type { std::shared_ptr m_tip_img; std::shared_ptr m_pattern_img; std::shared_ptr m_dual_img; 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_pattern_texture; std::string m_pattern_path; std::string m_pattern_thumb_path; glm::vec4 m_tip_color = { 0, 0, 0, 1 }; glm::vec2 m_tip_scale = { 1.f, 1.f }; glm::vec2 m_dual_scale = { 1.f, 1.f }; float m_tip_size = 50; float m_tip_spacing = .05; float m_tip_flow = 1.f; float m_tip_opacity = 1.f; float m_tip_angle = 0; float m_tip_angle_smooth = 0.2; float m_tip_mix = 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_init = false; bool m_tip_angle_follow = false; bool m_tip_flow_pressure = false; bool m_tip_opacity_pressure = false; bool m_tip_size_pressure = false; float m_jitter_scale = 0; float m_jitter_angle = 0; float m_jitter_scatter = 0; bool m_jitter_scatter_bothaxis = false; float m_jitter_flow = 0; float m_jitter_opacity = 0; float m_jitter_hue = 0; float m_jitter_sat = 0; float m_jitter_val = 0; bool m_jitter_hsv_eachsample = false; float m_jitter_aspect = 0; bool m_jitter_aspect_bothaxis = false; int m_blend_mode = 0; bool m_tip_invert = false; bool m_tip_flipx = false; bool m_tip_flipy = false; bool m_pattern_enabled = false; bool m_dual_enabled = false; int m_dual_blend_mode = 1; bool m_dual_randflip = false; float m_dual_size = .75; float m_dual_spacing = .25; float m_dual_scatter = 0; bool m_dual_scatter_bothaxis = 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 = 1.f; float m_dual_opacity = 1.f; float m_dual_rotate = .25f; float m_dual_angle = 0; float m_dual_aspect = 0.5f; int m_dual_count = 1; int m_pattern_blend_mode = 1; bool m_pattern_eachsample = false; bool m_pattern_invert = false; bool m_pattern_flipx = false; bool m_pattern_flipy = false; float m_pattern_scale = .25f; float m_pattern_brightness = 0.5f; float m_pattern_contrast = 0.5f; bool m_pattern_rand_offset = false; float m_pattern_depth = 1.f; bool load_tip(const std::string& path, const std::string& thumb); bool load_dual(const std::string& path, const std::string& thumb); bool load_pattern(const std::string& path, const std::string& thumb); bool load(); bool preload(); void unload(); bool valid(); void relocate_paths(std::string base); std::string replace_path(std::string path, std::string new_base); virtual bool read(BinaryStreamReader& r) override; virtual void write(BinaryStreamWriter& w) const override; }; 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 opacity = 0; float angle = 0; bool valid() const { return !( glm::isnan(angle) || glm::isinf(angle) || glm::isnan(flow) || glm::isinf(flow) || glm::isnan(opacity) || glm::isinf(opacity) || glm::isnan(size) || glm::isinf(size) || glm::any(glm::isnan(col)) || glm::any(glm::isnan(pos)) || glm::any(glm::isnan(scale)) || glm::any(glm::isnan(origin)) ); } }; 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_dir_init = 0; float m_curve = 0; float m_dist = 0; float m_step = 0; float m_max_size = FLT_MAX; bool m_filter_points = true; glm::vec3 m_tip_color; 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::mt19937 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); void randomize_prng(); float rnd_nor() { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1] float rnd_neg() { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1] float rnd_rad() { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi] glm::vec3 rnd_vec() { float rad = rnd_rad(); return glm::vec3(cosf(rad), sinf(rad), 0); }; // normalized direction vector float rnd_bneg() { return prng() % 2 == 0 ? -1.f : 1.f; }; // -1 or 1 };