Files
panopainter/src/canvas.cpp

421 lines
13 KiB
C++

#include "pch.h"
#include "log.h"
#include "canvas.h"
#include "legacy_canvas_render_shell_services.h"
#include "legacy_ui_gl_dispatch.h"
#include "legacy_canvas_stroke_erase_services.h"
#include "legacy_gl_renderbuffer_dispatch.h"
#include "legacy_canvas_stroke_commit_services.h"
#include "legacy_canvas_stroke_composite_services.h"
#include "legacy_canvas_stroke_edge_services.h"
#include "legacy_canvas_stroke_execution_services.h"
#include "legacy_canvas_stroke_runtime_services.h"
#include "legacy_canvas_stroke_shader_services.h"
#include "legacy_canvas_object_draw_services.h"
#include "legacy_canvas_projection_services.h"
#include "legacy_canvas_stroke_services.h"
#include "legacy_ui_overlay_services.h"
#include "app_core/document_canvas.h"
#include "texture.h"
#include "node_progress_bar.h"
#include "paint_renderer/compositor.h"
#include "renderer_gl/opengl_capabilities.h"
#include "util.h"
#include <array>
#include <condition_variable>
#include <deque>
#include <mutex>
#include <stop_token>
#include <thread>
#include <algorithm>
#include <cstdint>
#include <numeric>
#ifdef __APPLE__
#include <Foundation/Foundation.h>
#endif
namespace {
GLint current_canvas_stroke_internal_format()
{
const auto renderer_features = ShaderManager::render_device_features();
if (renderer_features.float32_linear_filtering)
return static_cast<GLint>(pp::renderer::gl::rgba32f_internal_format());
if (renderer_features.float16_render_targets)
return static_cast<GLint>(pp::renderer::gl::rgba16f_internal_format());
return static_cast<GLint>(pp::renderer::gl::rgba8_internal_format());
}
GLint rgba8_internal_format()
{
return static_cast<GLint>(pp::renderer::gl::rgba8_internal_format());
}
pp::renderer::RenderDeviceFeatures canvas_render_device_features() noexcept
{
return ShaderManager::render_device_features();
}
pp::paint_renderer::CanvasStrokeRasterizationPlan canvas_stroke_rasterization_plan(
int width,
int height) noexcept
{
return pp::panopainter::plan_legacy_canvas_stroke_rasterization(
canvas_render_device_features(),
width,
height);
}
pp::paint_renderer::CanvasStrokeFeedbackPlan canvas_destination_feedback_plan(
int width,
int height) noexcept
{
return canvas_stroke_rasterization_plan(width, height).feedback;
}
pp::paint_renderer::CanvasStrokeMaterialPlan canvas_stroke_material_plan(
const Brush& brush,
bool destination_feedback_needed) noexcept
{
return pp::panopainter::plan_legacy_canvas_stroke_material(
pp::paint_renderer::CanvasStrokeMaterialRequest {
.destination_feedback_needed = destination_feedback_needed,
.pattern_enabled = brush.m_pattern_enabled,
.pattern_eachsample = brush.m_pattern_eachsample,
.wet_blend = brush.m_tip_wet > 0.F,
.mix_blend = brush.m_tip_mix > 0.F,
.noise_enabled = brush.m_tip_noise > 0.F,
.dual_brush_enabled = brush.m_dual_enabled,
.dual_blend_mode = brush.m_dual_blend_mode,
.pattern_blend_mode = brush.m_pattern_blend_mode,
.dual_alpha = brush.m_dual_opacity,
});
}
pp::renderer::Extent2D canvas_stroke_extent(int width, int height) noexcept
{
return pp::renderer::Extent2D {
.width = static_cast<std::uint32_t>(std::max(width, 0)),
.height = static_cast<std::uint32_t>(std::max(height, 0)),
};
}
pp::paint_renderer::CanvasBlendGatePlan draw_merge_blend_gate_plan(
int width,
int height,
const std::vector<std::shared_ptr<Layer>>& layers,
const Brush* brush) noexcept
{
std::vector<int> layer_blend_modes;
layer_blend_modes.reserve(layers.size());
for (const auto& layer : layers) {
if (!layer) {
continue;
}
layer_blend_modes.push_back(layer->m_blend_mode);
}
const auto plan = pp::paint_renderer::plan_canvas_blend_gate(
canvas_render_device_features(),
pp::paint_renderer::CanvasBlendGateRequest {
.extent = pp::renderer::Extent2D {
.width = static_cast<std::uint32_t>(std::max(width, 0)),
.height = static_cast<std::uint32_t>(std::max(height, 0)),
},
.layer_blend_modes = layer_blend_modes,
.has_stroke_blend_mode = brush != nullptr,
.stroke_blend_mode = brush ? brush->m_blend_mode : 0,
});
if (plan) {
return plan.value();
}
pp::paint_renderer::CanvasBlendGatePlan fallback;
fallback.shader_blend = true;
fallback.complex_blend = true;
fallback.compatibility_fallback = true;
return fallback;
}
GLenum depth_test_state()
{
return static_cast<GLenum>(pp::renderer::gl::depth_test_state());
}
GLenum scissor_test_state()
{
return static_cast<GLenum>(pp::renderer::gl::scissor_test_state());
}
GLenum blend_state()
{
return static_cast<GLenum>(pp::renderer::gl::blend_state());
}
GLint texture_filter_linear()
{
return static_cast<GLint>(pp::renderer::gl::linear_texture_filter());
}
GLint texture_filter_linear_mipmap_linear()
{
return static_cast<GLint>(pp::renderer::gl::linear_mipmap_linear_texture_filter());
}
GLint texture_filter_nearest()
{
return static_cast<GLint>(pp::renderer::gl::nearest_texture_filter());
}
GLint texture_wrap_repeat()
{
return static_cast<GLint>(pp::renderer::gl::repeat_texture_wrap());
}
GLint texture_wrap_clamp_to_border()
{
return static_cast<GLint>(pp::renderer::gl::clamp_to_border_texture_wrap());
}
pp::renderer::gl::OpenGlPixelFormat texture_format_for_image_channels(int channel_count)
{
return pp::renderer::gl::texture_format_for_channel_count(static_cast<std::uint32_t>(channel_count));
}
void set_active_texture_unit(std::uint32_t unit_index)
{
pp::legacy::ui_gl::activate_texture_unit(unit_index, "Canvas");
}
void unbind_texture_2d()
{
pp::legacy::ui_gl::unbind_texture_2d("Canvas");
}
void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
pp::legacy::ui_gl::apply_viewport(x, y, width, height, "Canvas");
}
pp::renderer::gl::OpenGlViewportRect query_canvas_viewport()
{
return pp::legacy::ui_gl::query_viewport_rect("Canvas");
}
std::array<float, 4> query_canvas_clear_color()
{
return pp::legacy::ui_gl::query_clear_color("Canvas");
}
void apply_canvas_clear_color(std::array<float, 4> color)
{
pp::legacy::ui_gl::set_clear_color(color, "Canvas");
}
void apply_canvas_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
pp::legacy::ui_gl::apply_scissor_rect(x, y, width, height, "Canvas");
}
void apply_canvas_capability(std::uint32_t state, bool enabled)
{
pp::legacy::ui_gl::set_capability(state, enabled, "Canvas");
}
bool query_canvas_capability(std::uint32_t state)
{
return pp::legacy::ui_gl::query_capability(state, "Canvas");
}
GLuint allocate_canvas_depth_renderbuffer(int width, int height)
{
return static_cast<GLuint>(pp::legacy::gl_renderbuffer::allocate_depth_renderbuffer(
width,
height,
"OpenGL canvas depth renderbuffer allocation"));
}
void attach_canvas_depth_renderbuffer(GLuint renderbuffer)
{
pp::legacy::gl_renderbuffer::attach_depth_renderbuffer(
static_cast<std::uint32_t>(renderbuffer),
"OpenGL canvas depth renderbuffer attachment");
}
void delete_canvas_renderbuffer(GLuint renderbuffer)
{
pp::legacy::gl_renderbuffer::delete_renderbuffer(
static_cast<std::uint32_t>(renderbuffer),
"OpenGL canvas renderbuffer delete");
}
}
Canvas* Canvas::I;
std::vector<CanvasMode*> Canvas::modes[] = {
{ new CanvasModePen, new CanvasModeBasicCamera }, // brush
{ new CanvasModePen, new CanvasModeBasicCamera }, // eraser
{ new CanvasModeLine, new CanvasModeBasicCamera }, // line
{ new CanvasModeCamera, new CanvasModeBasicCamera }, // parallax
{ new CanvasModeGrid, new CanvasModeBasicCamera }, // grids
{ new CanvasModeTransform, new CanvasModeBasicCamera }, // import
{ new CanvasModeTransform, new CanvasModeBasicCamera }, // cut
{ new CanvasModeTransform, new CanvasModeBasicCamera }, // copy
{ new CanvasModeFill, new CanvasModeBasicCamera }, // fill
{ new CanvasModeMaskFree, new CanvasModeBasicCamera }, // mask-free
{ new CanvasModeMaskLine, new CanvasModeBasicCamera }, // mask-poly
{ new CanvasModeFloodFill, new CanvasModeBasicCamera }, // flood-fill
};
void Canvas::stroke_end() { pp::panopainter::legacy_canvas_stroke_end(*this); }
void Canvas::stroke_cancel() { pp::panopainter::legacy_canvas_stroke_cancel(*this); }
void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
{
pp::panopainter::legacy_canvas_stroke_draw_mix(*this, bb_min, bb_sz);
}
std::array<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vertex_t, 4>& B, bool project_3d /*= false*/, glm::mat4 mv /*= glm::mat4(1)*/) const
{
return pp::panopainter::legacy_canvas_stroke_draw_project(*this, B, project_3d, mv);
}
void Canvas::draw_merge_temporary_paint_branch(
int layer_index,
int plane_index,
std::shared_ptr<Layer> layer,
const Brush& brush,
const glm::mat4& ortho)
{
pp::panopainter::legacy_canvas_draw_merge_temporary_paint_branch(
*this,
layer_index,
plane_index,
std::move(layer),
brush,
ortho);
}
void Canvas::draw_merge_branch_orchestration(
int plane_index,
int layer_index,
const std::shared_ptr<Layer>& layer,
const Brush& brush,
const glm::mat4& ortho,
bool use_blend,
bool copy_blend_destination)
{
pp::panopainter::legacy_canvas_draw_merge_branch_orchestration(
*this,
plane_index,
layer_index,
layer,
brush,
ortho,
use_blend,
copy_blend_destination);
}
void Canvas::draw_merge_final_plane_composite(
const glm::mat4& ortho,
bool draw_checkerboard)
{
pp::panopainter::legacy_canvas_draw_merge_final_plane_composite(
*this,
ortho,
draw_checkerboard);
}
void Canvas::stroke_draw()
{
stroke_draw_live();
}
bool Canvas::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)
{
return pp::panopainter::legacy_canvas_point_trace(*this, loc, ray_origin, ray_dir, hit_pos, fb_pos, hit_normal, out_plane_id);
}
/*
bool Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& hit_pos, glm::vec2& hit_fb_pos, int plane_id)
{
auto ln = (loc / zw(m_box)) * 2.f - 1.f;
auto p = m_plane_unproject[plane_id] * glm::vec4(ln, 1, 1);
if (p.w <= 0)
{
return true;
}
return false;
}
*/
bool Canvas::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)
{
return pp::panopainter::legacy_canvas_point_trace_plane(*this, loc, ray_origin, ray_dir, hit_pos, hit_normal, hit_fb_pos, plane_id);
}
void Canvas::point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj,
glm::vec3& out_origin, glm::vec3& out_dir)
{
pp::panopainter::legacy_canvas_point_unproject(loc, vp, camera, proj, out_origin, out_dir);
}
void Canvas::point_unproject(glm::vec2 loc, glm::vec3& out_origin, glm::vec3& out_dir)
{
pp::panopainter::legacy_canvas_point_unproject(*this, loc, out_origin, out_dir);
}
glm::vec3 Canvas::point_trace(glm::vec2 loc)
{
return pp::panopainter::legacy_canvas_point_trace(*this, loc);
}
void Canvas::stroke_commit_timelapse()
{
pp::panopainter::legacy_canvas_stroke_commit_timelapse(*this);
}
void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SIXPLETTE(true)*/)
{
pp::panopainter::legacy_canvas_draw_merge(*this, draw_checkerboard, faces);
}
void Canvas::stroke_update(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_update(*this, point, pressure); }
void Canvas::stroke_start(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_start(*this, point, pressure); }
void Canvas::destroy()
{
pp::panopainter::legacy_canvas_destroy(*this);
}
bool Canvas::create(int width, int height)
{
return pp::panopainter::legacy_canvas_create(*this, width, height);
}
void Canvas::clear_context()
{
pp::panopainter::legacy_canvas_clear_context(*this);
}
void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer, int frame)
{
pp::panopainter::legacy_canvas_draw_objects_direct(*this, std::move(observer), layer, frame);
}
void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer, int frame, bool save_history)
{
pp::panopainter::legacy_canvas_draw_objects(*this, std::move(observer), layer, frame, save_history);
}
void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, int frame, bool save_history)
{
draw_objects(observer, layer(), frame, save_history);
}
void Canvas::project2Dpoints(std::vector<vertex_t>& vertices)
{
pp::panopainter::legacy_canvas_project_2d_points(*this, vertices);
}
glm::vec3 Canvas::project2Dpoint(glm::vec2 pt)
{
return pp::panopainter::legacy_canvas_project_2d_point(*this, pt);
}