254 lines
9.0 KiB
C++
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);
|
|
};
|