#pragma once #include "rtt.h" #include "texture.h" #include "shader.h" #include "shape.h" #include "brush.h" NS_START class Canvas { public: int m_width; int m_height; std::vector m_layers; std::vector m_strokes; std::unique_ptr m_tmp; Stroke* m_current_stroke = nullptr; int m_current_layer_idx = 0; RTT m_fb; Sampler m_sampler; Plane m_plane; std::minstd_rand prng; bool create(int width, int height) { m_width = width; m_height = height; m_tmp = std::make_unique(); m_tmp->create(width, height, "tmp"); m_fb.create(width, height); m_sampler.create(); m_plane.create<1>(1, 1); return true; } void layer_add(std::string name) { m_layers.emplace_back(); m_layers.back().create(m_width, m_height, name); } void stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush) { prng.seed(0); m_strokes.emplace_back(); m_strokes.back().m_brush = brush; m_strokes.back().add_point(point, pressure); m_current_stroke = &m_strokes.back(); } void stroke_update(glm::vec2 point, float pressure) { m_current_stroke->add_point(point, pressure); m_fb.bindFramebuffer(); GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glViewport(0, 0, m_width, m_height); glEnable(GL_BLEND); //auto mvp = glm::ortho(0.f, (float)m_width, (float)m_height, 0.f, -1.f, 1.f) * // glm::translate(glm::vec3(point, 0)) * // glm::scale(glm::vec3(30, 30, 1)); //auto& tex = TextureManager::get(m_current_stroke->m_brush.m_tex_id); //tex.bind(); //m_sampler.bind(0); //ui::ShaderManager::use(kShader::Stroke); //ui::ShaderManager::u_int(kShaderUniform::Tex, 0); //ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp); //ui::ShaderManager::u_float(kShaderUniform::Alpha, 1.f); //ui::ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); //m_plane.draw_fill(); //m_sampler.unbind(); //tex.unbind(); auto proj = glm::ortho(0.f, (float)m_width, (float)m_height, 0.f, -1.f, 1.f); auto m_brush = m_current_stroke->m_brush; auto rnd_nor = [&] { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1] auto rnd_neg = [&] { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1] auto rnd_rad = [&] { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi] auto rnd_vec = [&] { float rad = rnd_rad(); return glm::vec2(cosf(rad), sinf(rad)); }; // normalized direction vector float angle = (m_brush.m_tip_angle + rnd_nor() * m_brush.m_jitter_angle) * (float)(M_PI * 2.0); glm::vec2 pos = point + (rnd_vec() * m_brush.m_jitter_spread * 100.f); float size = 100.f * m_brush.m_tip_size * (1.f - rnd_nor() * m_brush.m_jitter_scale); float flow = m_brush.m_tip_flow * (1.f - rnd_nor() * m_brush.m_jitter_flow); //alpha += glm::max(m_brush.m_tip_spacing * .2f, .01f); auto mvp = proj * //glm::translate(glm::vec3(i * 40 * m_tip_spacing, m_rtt.getHeight() / 2, 0)) * glm::translate(glm::vec3(pos, 0)) * glm::scale(glm::vec3(size, size, 1)) * glm::eulerAngleZ(angle); auto& tex = TextureManager::get(m_brush.m_tex_id); tex.bind(); m_sampler.bind(0); ShaderManager::use("stroke"); ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_float(kShaderUniform::Alpha, flow); m_plane.draw_fill(); m_sampler.unbind(); tex.unbind(); glDisable(GL_BLEND); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); m_fb.unbindFramebuffer(); } void stroke_end() { m_current_stroke = nullptr; } void clear() { m_fb.bindFramebuffer(); GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, m_width, m_height); // glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); m_fb.unbindFramebuffer(); } }; NS_END