diff --git a/android/src/main/cpp/main.cpp b/android/src/main/cpp/main.cpp index 6829468..b8e9406 100755 --- a/android/src/main/cpp/main.cpp +++ b/android/src/main/cpp/main.cpp @@ -223,7 +223,7 @@ static int engine_init_display(struct engine* engine) { glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts); for (int i = 0; i < n_exts; i++) { - ext_map.emplace(glGetStringi(GL_EXTENSIONS, i), true); + ext_map.emplace((char*)glGetStringi(GL_EXTENSIONS, i), true); } //const char* ext = (const char*) glGetString(GL_EXTENSIONS); diff --git a/engine/app.cpp b/engine/app.cpp index be4078b..50498ab 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -157,6 +157,44 @@ void App::initShaders() // STROKE static const char* shader_stroke_v = + SHADER_VERSION + "uniform mat4 mvp;\n" + "uniform vec2 tof;\n" + "uniform vec2 tsz;\n" + "in vec4 pos;\n" + "in vec2 uvs;\n" + "out vec2 uv1;\n" + "out vec2 uv2;\n" + "void main(){\n" + " uv1 = uvs;\n" + " uv2 = tof + tsz * uvs;\n" + " gl_Position = mvp * vec4(pos.xyz, 1.0);\n" + "}\n"; + static const char* shader_stroke_f = + SHADER_VERSION + "uniform mediump sampler2D tex;\n" + "uniform mediump sampler2D tex_bg;\n" + "uniform mediump vec4 col;\n" + "uniform mediump vec2 resolution;\n" + "uniform mediump float alpha;\n" + "in mediump vec2 uv1;\n" + "in mediump vec2 uv2;\n" + "out mediump vec4 frag;\n" + "void main(){\n" + " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n" + " mediump float brush = ( 1.0 - texture(tex, uv1).r ) * alpha;\n" + " mediump vec4 bg = texture(tex_bg, uv2);\n" + " mediump vec3 rgb = mix( bg.rgb, col.rgb, clamp( brush/(brush + bg.a), 0.0, 1.0 ) );\n" + " mediump float a = bg.a + (1.0 - bg.a) * brush;\n" + " frag = vec4(rgb, a);\n" + +// " mediump vec4 bg = texture(tex_bg, uv.xy);" +// " mediump float a = (1.0 - texture(tex, uv.xy).r) * alpha;" +// " frag = bg;// * vec4(col.rgb, a);\n" + "}\n"; + + // STROKE LAYER BLEND + static const char* shader_stroke_layer_v = SHADER_VERSION "uniform mat4 mvp;" "in vec4 pos;" @@ -166,16 +204,19 @@ void App::initShaders() " uv = vec3(uvs, pos.w);" " gl_Position = mvp * vec4(pos.xyz, 1.0);" "}"; - static const char* shader_stroke_f = + static const char* shader_stroke_layer_f = SHADER_VERSION - "uniform mediump sampler2D tex;" - "uniform mediump vec4 col;" - "uniform mediump float alpha;" + "uniform mediump sampler2D tex_fg;" // foreground + "uniform mediump sampler2D tex_bg;" // canvas + "uniform mediump float alpha;" // opacity "in mediump vec3 uv;" "out mediump vec4 frag;" "void main(){" - " mediump float a = (1.0 - texture(tex, uv.xy).r) * alpha;" - " frag = vec4(col.rgb, a);" + " mediump vec4 c1 = texture2D(tex_fg, uv);" + " mediump vec4 c2 = texture2D(tex_bg, uv);" + " mediump float t = clamp(c1.a / (c1.a + c2.a), 0.0, 1.0));" + " mediump vec3 rgb = mix(c1.rgb, c2.rgb, t);" + " frag = vec4(rgb, c1.a + c2.a);" "}"; LOG("initializing shaders"); @@ -195,6 +236,8 @@ void App::initShaders() LOG("Failed to create shader Atlas"); if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f)) LOG("Failed to create shader Stroke"); + if (!ShaderManager::create(kShader::StrokeLayer, shader_stroke_layer_v, shader_stroke_layer_f)) + LOG("Failed to create shader StrokeLayer"); LOG("shaders initialized"); } @@ -475,13 +518,6 @@ void App::init() glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); #endif - //int n; - //glGetIntegerv(GL_NUM_EXTENSIONS, &n); - //for (int i = 0; i < n; i++) - //{ - // const unsigned char* s = glGetStringi(GL_EXTENSIONS, i); - // LOG("GL ext %03d: %s\n", i, s); - //} LOG("GL version: %s", glGetString(GL_VERSION)); LOG("GL vendor: %s", glGetString(GL_VENDOR)); LOG("GL renderer: %s", glGetString(GL_RENDERER)); diff --git a/engine/brush.cpp b/engine/brush.cpp index df62c41..553296c 100644 --- a/engine/brush.cpp +++ b/engine/brush.cpp @@ -75,7 +75,7 @@ bool ui::BrushMesh::create() { { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D }; glGenBuffers(3, buffers); - if (!buffers) + if (!(buffers[0] && buffers[1] && buffers[2])) return false; static instance_t inst{ glm::mat4(), .1f }; @@ -170,6 +170,7 @@ ui::StrokeSample ui::Stroke::randomize_sample(const glm::vec2& pos, float pressu } std::vector ui::Stroke::compute_samples() { + if (m_keypoints.empty()) return {}; int nsamples = (int)glm::floor((m_keypoints.back().dist - m_dist) / m_step); std::vector samples; samples.reserve(nsamples); // preallocate the estimate number of samples diff --git a/engine/canvas.cpp b/engine/canvas.cpp index 41c9e9f..4231a88 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -11,7 +11,7 @@ void ui::Canvas::clear() glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - glClearColor(1, 1, 1, 1); + glClearColor(1, 1, 1, 0); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, m_width, m_height); @@ -25,6 +25,8 @@ void ui::Canvas::clear() void ui::Canvas::stroke_end() { m_current_stroke = nullptr; + //stroke_commit(); + m_show_tmp = false; } void ui::Canvas::stroke_draw() { @@ -39,17 +41,20 @@ void ui::Canvas::stroke_draw() glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glViewport(0, 0, m_width, m_height); - glEnable(GL_BLEND); - auto proj = glm::ortho(0.f, (float)m_width, (float)m_height, 0.f, -1.f, 1.f); + auto proj = glm::ortho(0.f, (float)m_width, 0.f, (float)m_height, -1.f, 1.f); auto m_brush = m_current_stroke->m_brush; auto samples = m_current_stroke->compute_samples(); auto& tex = TextureManager::get(m_brush.m_tex_id); tex.bind(); m_sampler.bind(0); + m_sampler_bg.bind(1); + glActiveTexture(GL_TEXTURE1); + m_tex.bind(); if (m_use_instanced) { + glEnable(GL_BLEND); m_mesh.shader.use(); m_mesh.shader.u_vec4(kShaderUniform::Col, m_brush.m_tip_color); m_mesh.shader.u_int(kShaderUniform::Tex, 0); @@ -57,9 +62,12 @@ void ui::Canvas::stroke_draw() } else { - ShaderManager::use("stroke"); + glDisable(GL_BLEND); + ShaderManager::use(ui::kShader::Stroke); ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_int(kShaderUniform::TexBG, 1); + ShaderManager::u_vec2(kShaderUniform::Resolution, { m_width, m_height }); for (const auto& s : samples) { auto mvp = proj * @@ -67,16 +75,113 @@ void ui::Canvas::stroke_draw() glm::scale(glm::vec3(s.size, s.size, 1)) * glm::eulerAngleZ(s.angle); + //ShaderManager::u_mat4(kShaderUniform::MVP, mvp); + //ShaderManager::u_float(kShaderUniform::Alpha, s.flow); + //m_plane.draw_fill(); + + glm::vec4 P[4] { + mvp * glm::vec4(glm::vec2(-.5f, -.5f), 0, 1.f), // A - bottom-left + mvp * glm::vec4(glm::vec2(-.5f, +.5f), 0, 1.f), // B - top-left + mvp * glm::vec4(glm::vec2(+.5f, +.5f), 0, 1.f), // C - top-right + mvp * glm::vec4(glm::vec2(+.5f, -.5f), 0, 1.f), // D - bottom-right + }; + //auto mvp_inv = glm::translate(glm::vec3(0, m_height, 0)) * glm::inverse(proj * glm::scale(glm::vec3(1, -1, 1))); + auto mvp_inv = glm::inverse(proj); + glm::vec4 P2[4]{ + mvp_inv * P[0], + mvp_inv * P[1], + mvp_inv * P[2], + mvp_inv * P[3], + }; + + glm::vec2 bb_min(m_width, m_height); + glm::vec2 bb_max(0, 0); + for (int i = 0; i < 4; i++) + { + bb_min = glm::max({ 0, 0 }, glm::min(bb_min, P2[i].xy())); + bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, P2[i].xy())); + } + auto bb_sz = bb_max - bb_min; + // TODO: use floor and ceil to round to pixel + + glm::vec2 pad(0); + glm::ivec2 tex_pos = glm::clamp(glm::floor(bb_min) - pad , { 0, 0 }, { m_width, m_height }); + glm::ivec2 tex_sz = glm::clamp(glm::floor(bb_sz ) + pad, { 0, 0 }, { m_width, m_height }); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, + tex_pos.x, tex_pos.y, + tex_pos.x, tex_pos.y, + tex_sz.x, tex_sz.y); + +// glCopyTexSubImage2D(GL_TEXTURE_2D, 0, +// 0, 0, +// 0, 0, +// m_width, m_height); + + //glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + // (int)floor(bb_min.x), (int)floor(bb_min.y), + // (int)ceil(bb_sz.x), (int)ceil(bb_sz.y), 0); + +// auto tof = bb_min / glm::vec2(m_width, m_height); +// auto tsz = bb_sz / glm::vec2(m_width, m_height); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); +// ShaderManager::u_vec2(kShaderUniform::Tof, tof); +// ShaderManager::u_vec2(kShaderUniform::Tsz, tsz); ShaderManager::u_float(kShaderUniform::Alpha, s.flow); + //m_plane.update_vertices(P); m_plane.draw_fill(); } } + m_tex.unbind(); + glActiveTexture(GL_TEXTURE0); m_sampler.unbind(); + m_sampler_bg.unbind(); tex.unbind(); glDisable(GL_BLEND); + m_fb.unbindFramebuffer(); + + +// m_fb.bindFramebuffer(); +// { +// m_tmp.bindTexture(); +// m_sampler.bind(0); +// ShaderManager::use(ui::kShader::Texture); +// ShaderManager::u_int(kShaderUniform::Tex, 0); +// ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); +// m_plane.draw_fill(); +// m_sampler.unbind(); +// m_tmp.unbindTexture(); +// } +// m_fb.unbindFramebuffer(); + + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); + +} +void ui::Canvas::stroke_commit() +{ + 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); + GLboolean blend = glIsEnabled(GL_BLEND); + if (!blend) glEnable(GL_BLEND); + + m_tmp.bindTexture(); + m_sampler.bind(0); + ShaderManager::use(ui::kShader::Texture); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); + m_plane.draw_fill(); + m_sampler.unbind(); + m_tmp.unbindTexture(); + + if (!blend) glDisable(GL_BLEND); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); @@ -93,6 +198,17 @@ void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush& m_strokes.back().start(brush); m_strokes.back().add_point(point, pressure); m_current_stroke = &m_strokes.back(); + + m_tmp.bindFramebuffer(); + m_tmp.clear({ 1, 1, 1, 0 }); + m_tmp.unbindFramebuffer(); + m_show_tmp = true; + +// m_fb.bindFramebuffer(); +// m_tex.bind(); +// glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); +// m_tex.unbind(); +// m_fb.unbindFramebuffer(); } void ui::Canvas::layer_add(std::string name) { @@ -103,18 +219,20 @@ void ui::Canvas::resize(int width, int height) { m_width = width; m_height = height; - m_tmp->create(width, height, "tmp"); + m_tmp.create(width, height); m_fb.create(width, height); + m_tex.create(width, height); } bool ui::Canvas::create(int width, int height) { m_width = width; m_height = height; - m_tmp = std::make_unique(); - m_tmp->create(width, height, "tmp"); + m_tmp.create(width, height); m_fb.create(width, height); + m_tex.create(width, height); m_sampler.create(); + m_sampler_bg.create(); m_plane.create<1>(1, 1); m_mesh.create(); return true; -} \ No newline at end of file +} diff --git a/engine/canvas.h b/engine/canvas.h index 6dd3f5b..7259cfb 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -17,11 +17,14 @@ public: int m_width; int m_height; bool m_use_instanced = false; + bool m_show_tmp = false; std::vector m_layers; std::vector m_strokes; - std::unique_ptr m_tmp; + RTT m_tmp; RTT m_fb; + Texture2D m_tex; Sampler m_sampler; + Sampler m_sampler_bg; bool create(int width, int height); void resize(int width, int height); @@ -30,6 +33,7 @@ public: void stroke_update(glm::vec2 point, float pressure); void stroke_draw(); void stroke_end(); + void stroke_commit(); void clear(); }; diff --git a/engine/layout.h b/engine/layout.h index 8757fae..352e389 100644 --- a/engine/layout.h +++ b/engine/layout.h @@ -1878,21 +1878,36 @@ public: glViewport(c.x, c.y, c.z, c.w); glm::vec2 sz = { m_canvas->m_width, m_canvas->m_height }; - auto mvp = glm::ortho(0.f, box.z, box.w, 0.f, -1.f, 1.f) * + auto mvp = glm::ortho(0.f, box.z, 0.f, box.w, -1.f, 1.f) * //glm::translate(glm::vec3((m_size - sz) * 0.5f, 0)) * // center glm::translate(glm::vec3(0)) * // corner glm::scale(glm::vec3(sz * zoom, 1)) * glm::translate(glm::vec3(.5f, .5f, 0.f)); // pivot - m_canvas->m_fb.bindTexture(); m_canvas->m_sampler.bind(0); ui::ShaderManager::use(kShader::Texture); ui::ShaderManager::u_int(kShaderUniform::Tex, 0); ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp); + + bool blend = glIsEnabled(GL_BLEND); + + glEnable(GL_BLEND); + m_canvas->m_fb.bindTexture(); NodeBorder::m_plane.draw_fill(); - m_canvas->m_sampler.unbind(); m_canvas->m_fb.unbindTexture(); + if (m_canvas->m_show_tmp) + { + glEnable(GL_BLEND); + m_canvas->m_tmp.bindTexture(); + NodeBorder::m_plane.draw_fill(); + m_canvas->m_tmp.unbindTexture(); + } + + blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + + m_canvas->m_sampler.unbind(); + glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); diff --git a/engine/rtt.cpp b/engine/rtt.cpp index 88ddc86..212e2d4 100644 --- a/engine/rtt.cpp +++ b/engine/rtt.cpp @@ -69,6 +69,8 @@ bool RTT::create(int width, int height, int tex/* = -1*/) // glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); // glBindRenderbuffer(GL_RENDERBUFFER, 0); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldFboID); + // Create a framebuffer object glGenFramebuffers(1, &fboID); glBindFramebuffer(GL_FRAMEBUFFER, fboID); @@ -94,22 +96,34 @@ bool RTT::create(int width, int height, int tex/* = -1*/) // Check FBO status GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) - LOG("createColorBuffer failed because: %s", err2str(status)); + LOG("RTT::create failed because: %s", err2str(status)); // Switch back to window-system-provided framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, oldFboID); + oldFboID = 0; return status == GL_FRAMEBUFFER_COMPLETE; } void RTT::bindFramebuffer() { +#ifdef DEBUG + if (bound) + { + LOG("framebuffer bound twice!"); + __debugbreak(); + } +#endif // _DEBUG + bound = true; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldFboID); glBindFramebuffer(GL_FRAMEBUFFER, fboID); } void RTT::unbindFramebuffer() { - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, oldFboID); + oldFboID = 0; + bound = false; } void RTT::clear(glm::vec4 color) diff --git a/engine/rtt.h b/engine/rtt.h index 0536838..b1bb34e 100644 --- a/engine/rtt.h +++ b/engine/rtt.h @@ -2,6 +2,8 @@ class RTT { + bool bound = false; + GLint oldFboID = 0; GLuint fboID; GLuint rboID; GLuint texID; diff --git a/engine/shader.cpp b/engine/shader.cpp index d6f7d27..3db4ae6 100644 --- a/engine/shader.cpp +++ b/engine/shader.cpp @@ -131,6 +131,7 @@ GLint ui::Shader::GetAttribLocation(const char* name) } bool ShaderManager::create(kShader id, const char* vertex, const char* fragment) { + m_shaders[id].name = id; return m_shaders[id].create(vertex, fragment); } diff --git a/engine/shader.h b/engine/shader.h index bf5f21b..5f1a286 100644 --- a/engine/shader.h +++ b/engine/shader.h @@ -7,25 +7,13 @@ enum class kShaderUniform : uint16_t { MVP = const_hash("mvp"), Tex = const_hash("tex"), + TexFG = const_hash("tex_fg"), + TexBG = const_hash("tex_bg"), Col = const_hash("col"), Tof = const_hash("tof"), Tsz = const_hash("tsz"), Alpha = const_hash("alpha"), -}; - -class Shader -{ - std::map m_umap; - GLuint prog; -public: - bool create(const char* vertex, const char* fragment); - void use(); - void u_vec4(kShaderUniform id, const glm::vec4& v); - void u_vec2(kShaderUniform id, const glm::vec2& v); - void u_mat4(kShaderUniform id, const glm::mat4& m); - void u_int(kShaderUniform id, int i); - void u_float(kShaderUniform id, float f); - GLint GetAttribLocation(const char* name); + Resolution = const_hash("resolution"), }; enum class kShader : uint16_t @@ -38,6 +26,23 @@ enum class kShader : uint16_t Font = const_hash("font"), Atlas = const_hash("atlas"), Stroke = const_hash("stroke"), + StrokeLayer = const_hash("stroke-layer"), +}; + +class Shader +{ + std::map m_umap; + GLuint prog; +public: + kShader name; + bool create(const char* vertex, const char* fragment); + void use(); + void u_vec4(kShaderUniform id, const glm::vec4& v); + void u_vec2(kShaderUniform id, const glm::vec2& v); + void u_mat4(kShaderUniform id, const glm::mat4& m); + void u_int(kShaderUniform id, int i); + void u_float(kShaderUniform id, float f); + GLint GetAttribLocation(const char* name); }; class ShaderManager diff --git a/engine/shape.cpp b/engine/shape.cpp index ccbde24..f278fca 100644 --- a/engine/shape.cpp +++ b/engine/shape.cpp @@ -149,7 +149,29 @@ void Plane::create_impl(float w, float h, int div, GLushort *idx, Shape::vertex_ *idx++ = div; // D *idx++ = 0; // A } +void ui::Plane::update_vertices(const glm::vec4* data) +{ + static vertex_t vertices[4]; + vertices[0] = { data[0],{ 0, 0 } }; // A + vertices[1] = { data[1],{ 0, 1 } }; // B + vertices[2] = { data[2],{ 1, 1 } }; // C + vertices[3] = { data[3],{ 1, 0 } }; // D + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + static GLushort idx[6 + 8]{ + 0, 1, 2, + 0, 2, 3, + 0, 1, + 1, 2, + 2, 3, + 3, 0, + }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} void Circle::create_impl(float radius, int div, GLushort* idx, vertex_t* vertices) { count[0] = div * 3; diff --git a/engine/shape.h b/engine/shape.h index 453b2d8..ab93ceb 100644 --- a/engine/shape.h +++ b/engine/shape.h @@ -59,6 +59,7 @@ public: create_impl(w, h, div, idx, vertices); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } + void update_vertices(const glm::vec4* data); /* bool create(att::Divisions divisions, att::Width w, att::Height h) { diff --git a/engine/texture.h b/engine/texture.h index 227ea14..e60ac59 100644 --- a/engine/texture.h +++ b/engine/texture.h @@ -9,7 +9,7 @@ class Texture2D GLint m_format; GLint m_iformat; public: - bool create(int width, int height, GLint internal_format, GLint format = GL_RGBA, const uint8_t* data = nullptr); + bool create(int width, int height, GLint internal_format = GL_RGBA8, GLint format = GL_RGBA, const uint8_t* data = nullptr); bool create(const ui::Image& img); void assign(GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA); bool load(std::string filename);