#pragma once #include "rtt.h" #include "texture.h" #include "shader.h" #include "shape.h" #include "brush.h" #include "action.h" #include "canvas_modes.h" NS_START class Layer { public: RTT m_rtt[6]; glm::vec4 m_dirty_box[6]; bool m_dirty_face[6]; bool m_visible = true; bool m_alpha_locked = false; float m_opacity = 1.f; bool m_hightlight = false; std::string m_name; int w, h; struct Snapshot { std::unique_ptr image[6]; glm::vec4 m_dirty_box[6]; bool m_dirty_face[6]; void create(int w, int h) { for (int i = 0; i < 6; i++) image[i] = std::make_unique(w*h*4); } }; bool create(int width, int height, std::string name); void clear(const glm::vec4& c); Snapshot snapshot(std::string data_path); void restore(const Snapshot& snap); void destroy(); }; class Canvas { public: Plane m_plane; Plane m_plane_brush; BrushMesh m_mesh; bool m_dirty = false; bool m_commit_delayed = false; static Canvas* I; bool m_alpha_lock = false; bool m_touch_lock = true; glm::mat4 m_mv; glm::mat4 m_proj; glm::vec4 m_box; glm::vec2 m_pan; int m_width; int m_height; bool m_use_instanced = false; int m_current_layer_idx = 0; std::unique_ptr m_current_stroke; bool m_show_tmp = false; std::vector m_layers; std::vector m_order; glm::vec4 m_dirty_box[6]; bool m_dirty_face[6]; Layer m_smask; // selection mask bool m_smask_active = false; RTT m_tmp[6]; Texture2D m_brush_mix; Texture2D m_tex[6]; Texture2D m_tex2[6]; bool m_pick_ready[6]; std::unique_ptr m_pick_data[6]; static glm::vec3 m_plane_origin[6]; static glm::vec3 m_plane_normal[6]; 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; Sampler m_sampler_stencil; glm::vec2 m_cam_rot; glm::vec3 m_cam_pos; float m_cam_fov = 85; enum class kCanvasMode { Draw, Erase, Line, Camera, Grid, Fill, COUNT }; kCanvasMode m_state{ kCanvasMode::Draw }; static std::vector modes[]; std::vector* m_mode = nullptr; kCanvasMode m_current_mode = kCanvasMode::Draw; static void set_mode(kCanvasMode mode) { if (I->m_mode) for (auto& m : *I->m_mode) m->leave(); I->m_mode = &modes[(int)mode]; I->m_state = mode; I->m_current_mode = mode; if (I->m_mode) for (auto& m : *I->m_mode) m->enter(); } std::vector m_layers_snapshot; Canvas() { I = this; } bool create(int width, int height); void resize(int width, int height); void layer_remove(int idx); void layer_add(std::string name); void layer_order(int idx, int pos); void layer_merge(int source_idx, int dest_idx); void stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush); void stroke_update(glm::vec2 point, float pressure); void stroke_draw(); void stroke_end(); void stroke_cancel(); void stroke_commit(); void clear(const glm::vec4& color = { 1, 1, 1, 0 }); void pick_start(); void pick_update(int plane); glm::vec4 pick_get(glm::vec2 canvas_loc); void snapshot_save(std::string data_path); void snapshot_restore(); void snap_history(const std::vector& planes); void clear_context(); void export_equirectangular(std::string data_path); void export_equirectangular_thread(std::string data_path); void export_anim(std::string data_path); void project_save(std::string data_path); void project_save_thread(std::string data_path); void project_open(std::string data_path); void project_open_thread(std::string data_path); void inject_xmp(std::string jpg_path); ui::Image thumbnail_generate(int w, int h); ui::Image thumbnail_read(std::string data_path); void preview_generate(); void draw_objects(std::function); void draw_objects(std::function, Layer& layer); bool ray_intersect(glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin, glm::vec3 plane_normal, glm::vec3 plane_tangent, glm::vec3 &out_hit); void point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj, glm::vec3 &out_origin, glm::vec3 &out_dir); bool point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id); bool point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id); }; class ActionStroke : public Action { public: std::unique_ptr m_stroke; std::unique_ptr m_image[6]; glm::ivec4 m_old_box[6]; bool m_old_dirty[6]; glm::ivec4 m_box[6]; bool m_dirty[6]; bool clear_layer = false; int m_layer_idx; Canvas* m_canvas; virtual void run() override { } virtual void undo() override { if (clear_layer) m_canvas->m_layers[m_layer_idx].clear({ 0, 0, 0, 0 }); for (int i = 0; i < 6; i++) { // empty data if (!m_image[i]) continue; m_canvas->m_layers[m_layer_idx].m_dirty_box[i] = m_old_box[i]; m_canvas->m_layers[m_layer_idx].m_dirty_face[i] = m_old_dirty[i]; m_canvas->m_layers[m_layer_idx].m_rtt[i].bindTexture(); glm::vec2 box_sz = m_box[i].zw() - m_box[i].xy(); glTexSubImage2D(GL_TEXTURE_2D, 0, (int)m_box[i].x, (int)m_box[i].y, (int)box_sz.x, (int)box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get()); m_canvas->m_layers[m_layer_idx].m_rtt[i].unbindTexture(); } } virtual size_t memory() override { size_t mem = 0; for (int i = 0; i < 6; i++) { glm::ivec2 sz = m_box[i].zw() - m_box[i].xy(); mem += sz.x * sz.y * 4 + sizeof(*this); } return mem; } virtual ~ActionStroke() { } }; NS_END