Files
panopainter/src/canvas.h
2019-10-19 22:30:44 +02:00

254 lines
9.0 KiB
C++

#pragma once
#include "rtt.h"
#include "texture.h"
#include "shader.h"
#include "shape.h"
#include "brush.h"
#include "canvas_layer.h"
#include "canvas_actions.h"
#include "canvas_modes.h"
#include <stack>
#if __WEB__
#define CANVAS_RES 512
#else
#define CANVAS_RES 1536
#endif
struct PPIThumb
{
int width = 128;
int height = 128;
int comp = 4;
bool valid() const
{
return (width == 128 && height == 128 && comp == 4);
}
};
struct PPIDocVersion
{
int major = 0;
// version 1: initial
// version 2: added blend mode, alpha and visibility
// version 3: added animation frames
int minor = 3;
};
struct PPISoftVersion
{
int major = g_version_major;
int minor = g_version_minor;
int fix = g_version_fix;
int build = g_version_build;
};
struct PPIHeader
{
char magic[4]{ 'P', 'P', 'I', 0 };
PPIDocVersion doc_version;
PPISoftVersion soft_version;
PPIThumb thumb_header;
bool valid()
{
if (strcmp(magic, "PPI") != 0)
return false;
if (doc_version.major != 0 || (doc_version.minor < 1 && doc_version.minor > 2))
return false;
if (!thumb_header.valid())
return false;
return true;
}
};
class Canvas
{
struct StrokeFrame
{
glm::vec4 col;
float flow;
float opacity;
std::array<std::vector<vertex_t>, 6> shapes;
glm::vec4 m_mixer_rect;
};
public:
struct FloodData
{
std::shared_ptr<Layer> layer;
std::unique_ptr<bool[]> mask[6];
std::unique_ptr<glm::u8vec4[]> rgb[6] = SIXPLETTE(0);
std::array<bool, 6> dirty = SIXPLETTE(false);
glm::vec4 bb[6];
void apply();
};
Plane m_plane;
Plane m_plane_brush;
DynamicShape m_brush_shape;
bool m_unsaved = false;
bool m_newdoc = true;
bool m_dirty = false;
bool m_commit_delayed = false;
bool m_dirty_stroke = false;
std::stack<CameraData> m_camera_stack;
static Canvas* I;
NodeCanvas* m_node = nullptr;
bool m_alpha_lock = false;
bool m_touch_lock = false;
glm::mat4 m_mv{ 1 };
glm::mat4 m_proj{ 1 };
glm::vec4 m_box{ 0 };
glm::vec4 m_vp{ 0 };
glm::vec2 m_pan{ 0 };
glm::vec2 m_size{ 0 };
int m_width = 0;
int m_height = 0;
bool m_use_instanced = false;
int m_current_layer_idx = 0;
std::unique_ptr<Stroke> m_current_stroke;
std::unique_ptr<Stroke> m_dual_stroke;
bool m_show_tmp = false;
std::vector<std::shared_ptr<Layer>> m_layers;
int m_anim_frame = 0;
Layer m_layers_merge;
std::vector<glm::vec2> m_plane_shape[6]; // screen space projection of the plane
glm::mat4 m_plane_unproject[6] = SIXPLETTE(glm::mat4(1));
glm::vec3 m_plane_dir[6] = SIXPLETTE(glm::vec3(0));
glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0));
bool m_dirty_face[6] = SIXPLETTE(false);
Layer m_smask; // selection mask
bool m_smask_active = false;
// mode 0=none, 1=free, 2=line
int m_smask_mode = 0;
RTT m_tmp[6];
RTT m_tmp_dual[6];
RTT m_mixer;
float m_mixer_scale = 1;
Texture2D m_brush_mix;
Texture2D m_tex[6];
Texture2D m_tex2[6];
RTT m_merge_rtt;
Texture2D m_merge_tex;
bool m_pick_ready[6] = SIXPLETTE(false);
std::unique_ptr<glm::u8vec4[]> m_pick_data[6] = SIXPLETTE(nullptr);
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];
glm::vec2 m_pattern_offset{ 0 };
Sampler m_sampler;
Sampler m_sampler_nearest;
Sampler m_sampler_linear;
Sampler m_sampler_brush;
Sampler m_sampler_stencil;
Sampler m_sampler_mix;
glm::mat4 m_cam_rot = glm::mat4(1);
glm::vec3 m_cam_pos{ 0 };
float m_cam_fov = 85.f;
const float m_cam_fov_min = 5.f;
const float m_cam_fov_max = 150.f;
glm::vec2 m_cur_pos{ 0 };
std::shared_ptr<Brush> m_current_brush;
static std::vector<CanvasMode*> modes[];
std::vector<CanvasMode*>* m_mode = nullptr;
kCanvasMode m_current_mode = kCanvasMode::Draw;
std::function<void(kCanvasMode prev, kCanvasMode mode)> on_mode_changed;
static void set_mode(kCanvasMode mode)
{
auto prev = I->m_current_mode;
if (I->m_mode)
for (auto& m : *I->m_mode)
m->leave(mode);
I->m_mode = &modes[(int)mode];
I->m_current_mode = mode;
if (I->m_mode)
for (auto& m : *I->m_mode)
m->enter(prev);
if (I->on_mode_changed)
I->on_mode_changed(prev, mode);
}
std::vector<Layer::Snapshot> m_layers_snapshot;
Canvas() { I = this; }
~Canvas() { destroy(); }
void destroy();
bool create(int width, int height);
void resize(int width, int height);
Layer& layer() { return *m_layers[m_current_layer_idx]; }
std::shared_ptr<Layer> layer_with_id(uint32_t id);
void layer_remove(int idx);
void layer_add(std::string name, std::shared_ptr<Layer> layer = nullptr, int index = 0);
void layer_order(int idx, int pos);
void layer_merge(int source_idx, int dest_idx);
int anim_duration() const noexcept;
void anim_goto_frame(int frame) noexcept;
void anim_goto_next() noexcept;
void anim_goto_prev() noexcept;
void flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, FloodData& plane_data,
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& source_color);
void stroke_start(glm::vec3 point, float pressure);
void stroke_update(glm::vec3 point, float pressure);
void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz);
std::array<std::vector<vertex_t>, 6> stroke_draw_project(std::array<vertex_t, 4>& B, bool project_3d = false, glm::mat4 mv = glm::mat4(1)) const;
// return rect {origin, size}
glm::vec4 stroke_draw_samples(int i, std::vector<vertex_t>& P);
std::vector<StrokeFrame> stroke_draw_compute(Stroke& stroke) const;
void stroke_draw();
void stroke_end();
void stroke_cancel();
void stroke_commit();
void draw_merge(bool draw_checkerboard, std::array<bool, 6> faces = SIXPLETTE(true));
void clear(const glm::vec4& color = { 1, 1, 1, 0 });
void clear_all();
void pick_start();
void pick_update(int plane);
glm::vec4 pick_get(glm::vec2 canvas_loc);
void pick_end();
void snapshot_save();
void snapshot_restore();
void snap_history(const std::vector<int>& planes);
void clear_context();
void import_equirectangular(std::string file_path, std::shared_ptr<Layer> layer = nullptr);
void import_equirectangular_thread(std::string file_path, std::shared_ptr<Layer> layer = nullptr, int frame = -1);
void export_equirectangular(std::string file_path, std::function<void()> on_complete = nullptr);
void export_equirectangular_thread(std::string file_path);
void export_layers(std::string path, std::function<void()> on_complete = nullptr);
void export_layers_thread(std::string path);
void export_depth(std::string file_name, std::function<void()> on_complete = nullptr);
void export_depth_thread(std::string file_name);
void export_cube_faces(std::string file_name, std::function<void()> on_complete);
void export_cube_faces_thread(std::string file_name);
void project_save(std::function<void(bool)> on_complete = nullptr);
void project_save(std::string file_path, std::function<void(bool)> on_complete = nullptr);
bool project_save_thread(std::string file_path, bool show_progress);
void project_open(std::string file_path, std::function<void(bool)> on_complete = nullptr);
bool project_open_thread(std::string file_path);
void inject_xmp(std::string jpg_path);
Image thumbnail_generate(int w, int h);
Image thumbnail_read(std::string file_path);
void draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>, int frame, bool save_history);
void draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>, Layer& layer, int frame, bool save_history);
void draw_objects_direct(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>, Layer& layer, int frame);
void point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj,
glm::vec3 &out_origin, glm::vec3 &out_dir);
void point_unproject(glm::vec2 loc, glm::vec3 &out_origin, glm::vec3 &out_dir);
glm::vec3 point_trace(glm::vec2 loc);
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);
void project2Dpoints(std::vector<vertex_t>& vertices);
glm::vec3 project2Dpoint(glm::vec2 pt);
std::vector<glm::vec2> face_to_shape2D(int plane_index);
void push_camera();
void pop_camera();
CameraData get_camera();
void set_camera(const CameraData& c);
};