From ade95724e8e3cf33ad23e6353d8ddf082a1a308c Mon Sep 17 00:00:00 2001 From: omigamedev Date: Wed, 29 Mar 2017 00:50:51 +0100 Subject: [PATCH] change android icon, move code so .cpp and implement stroke preview using Stroke class for accurate preview. --- android/CMakeLists.txt | 2 + android/build.gradle | 4 +- android/src/main/cpp/main.cpp | 4 + .../main/res/drawable-hdpi/ic_launcher.png | Bin 9397 -> 22854 bytes .../main/res/drawable-ldpi/ic_launcher.png | Bin 2729 -> 18529 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 5237 -> 19985 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 14383 -> 25472 bytes android/src/main/res/values/strings.xml | 2 +- engine/app.cpp | 27 ++- engine/brush.cpp | 215 +++++++++++++++++ engine/brush.h | 216 +----------------- engine/canvas.cpp | 116 ++++++++++ engine/canvas.h | 134 ++--------- engine/layout.h | 71 +++--- 14 files changed, 421 insertions(+), 370 deletions(-) diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 6bad1b6..5a65385 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -37,6 +37,8 @@ add_library( ../engine/shape.cpp ../engine/layout.cpp ../engine/app.cpp + ../engine/brush.cpp + ../engine/canvas.cpp ) target_include_directories(native-lib PRIVATE diff --git a/android/build.gradle b/android/build.gradle index f7915f8..e987447 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -28,7 +28,9 @@ android { // Sets a flag to enable format macro constants for the C++ compiler. //cppFlags "-D__STDC_FORMAT_MACROS" arguments '-DANDROID_PLATFORM=android-19', - '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static' + '-DANDROID_TOOLCHAIN=clang', + '-DANDROID_STL=gnustl_static', + '-DCMAKE_BUILD_TYPE=Debug' } } ndk { diff --git a/android/src/main/cpp/main.cpp b/android/src/main/cpp/main.cpp index c28a950..42b3796 100755 --- a/android/src/main/cpp/main.cpp +++ b/android/src/main/cpp/main.cpp @@ -167,6 +167,10 @@ static void engine_draw_frame(struct engine* engine) { if (engine->display == NULL) return; + glClearColor(.1f, .1f, .1f, 1.f); + glViewport(0, 0, (GLsizei)engine->width, (GLsizei)engine->height); + glClear(GL_COLOR_BUFFER_BIT); + App::I.update(0); eglSwapBuffers(engine->display, engine->surface); diff --git a/android/src/main/res/drawable-hdpi/ic_launcher.png b/android/src/main/res/drawable-hdpi/ic_launcher.png index 96a442e5b8e9394ccf50bab9988cb2316026245d..791726496d84dccf483ceed770abdffd0013e874 100644 GIT binary patch literal 22854 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 Bb~*q6 literal 9397 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 F{sqI^?j`^L delta 2722 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 diff --git a/android/src/main/res/drawable-mdpi/ic_launcher.png b/android/src/main/res/drawable-mdpi/ic_launcher.png index 359047dfa4ed206e41e2354f9c6b307e713efe32..af7a0c426bbbcb0a06537220cdf6510326db33ea 100644 GIT binary patch literal 19985 zcmeI42{@GB`|uxGilj{&5mQ2p8O97Irp2Df*DhPLVZ@AP#*!scQkJBmRA@oTk`UR6 zlI)34DZ40T-}Qe+o9}%4&ilLG|Np(->wT}8%b4dr_qp%;IiGXx`<%yhjdO+u+N=14 z_y7P{rK_W11b)I9?-e}YZ}Vl-LEs0ktBxfN0IanbZw|rB>iYm-_dU9%1>HjbAR13` zk;4)wIHH`Viz}eI$I}&yKT4#F;fM|-vKr(|elbLhL{Nj6BlY3>uIfZbl8zUZXzXQR zg7-R#S0O<5VE9x$(V(#lk&YGfba5ur(4K0LMW1N!n$Zk{h%I)ZA60|wV+0ek&^HuQ zr%;JvNI4V~4@W79DXGZGBbAgCm1M;f;POZq905bXp$Z7JA{?!#Aok-0!SI1ss#JnK z+DJp|M>tSYgE-RZu4ov{!^1<)10hGDI>6*rR8(Ma1(<>Y6zl<|9VgSVo=`Gv`*$Zl z{b&$rcq+-2PNI;-7=E!hiW^-G0$~LD_4+X`7uR2b$h05aK!Pw&tSd}j4i5X15dr_p z#?_7Lym)H_JdEf}bRm-IG|*1|k9Mw(6gq|GNcoqLztsO47@QM*{a>;Fc)u<#e+*5d zYr2C7ejxp$CC%ixD-mWyq*2_cc%r5|IF#*w8a=lK#bvq4{pM zxD>w6kSaJkeQ{NZNtu(#1d0buS{24@@_W92dn0OK z=|l{;9#K#@3aWrIkw-GtBT`-lE{}%8mpJ*}lgWoZg+Q`D{tq80v^)a*S?Xg+Pi7xr z{t&Qq?0@8CY2PJ&2zazTh3bN(V@NJo2O`Xs?4Sz!-MFOZuT_Osr#Mrn;B65x2vyjh znwMBDt~9hRnMTKw@kCt>4A>w?A`#FitUL}+P((se3Sbs0;lV4M0$ve{A|O#H1*9?_ zheR$y)Yo5X{twq06ucWFTR_*}3k!jQ2i^a^EzlWC2?2*g5jY$iDlbn&K~)f7AR=4= zi&RD-6tOD50xfa&k3fg0B(UybotN}sjD^4$3rbN@8Hpf3;Y7SL6hTmgL$UITM5vN7 z!XAl5+T*}-^K&eJaPyB)IwTsnT8}ST#o$_AtZc@_V}Eb`scz=$S>>q8v*Ub0+ z7K}RT?6%k+1z&u@8<|S5))6e&<*%&;i z6EP?x5&>>(%+1Ueh9pm-vxNo;+(T)My#S%81jbslVpjdp)bekpAkp8ZOH@Bhzfahb zWA$PU{oV>5+QH`q*slkMf0Wl>yU_pS#qTlwPkLqY#SX#*gtf@V#hMS3mW_)E2y2mz zi!~o6EgKgT5Y{3a7i&IDS~e~wAgo0;F4laQv}{~VKv;`xT&(#pY1z1#fUp+XxLEUH z(z0WBk<6_N+Nz2B?1cbH7#>JWsla`H(2?%SEjf*uOCM_Em z6A;!S8y9OnOj7VgkZiWaDDZhe^xE#RP=4$i~H*50jRSiwOv8k&TNrA0{mu7ZVWHA{!TL zK1^CRE+!zXMV7euet!j*NCv-m>j8ejc7=S+9ROIjT32JAiQ~zd!^cgGU+1*v^788H zh++A$f@_gthHKIdxg|9W=Tug6rv@M1;NYB!i8y@U=qY4Zt-5LX!vM};zQg-8HF&r~ z4`TVncbp6*obdZF9;B$8RrIJm$IOg8)b8-*OMYK>JFO10InZqRw~f}fo@d_-5HT{U z8g@|1$+$MPO!A}!PTKsw4gl;}W=l3m`1%=Iqe6Di-q0>*5O`8HN0le0d4@v6FC+oC zG!7Jx^^?Y$<{W5t%}P@P9Pqfv&3nIee46rcx@k8P8o}XLFTL}L-;=IP?yPg@EwgDv z3i{iJ;oD@>x(z(Q-ZdJ#TWTjHsw&f7Rkq!4esQ@?OJV180O)>qfa-mvPDZMOLc3C@gOM-1xAm@e}u!`s&BuB$vGsN^K|=RvDbM$$zAoW)i^xxLTxm zrR2Lg~O!A5vA0x#<+sfn>VP;3UfW43p#N@=&^BJlT zdiYv_`nBQyM6_cnv)vTaH}eQs<`Ex%LobUaLS)cvkaR&0)Sdz-{9wfpx4Tx}np+2*0J zJ=gLT@mBr7-n0fB7nM87Becz(1F>t`kmp(u=+S5VW61OH;n|zR^`8q8#w_&|?GgAf zft6r8YB~Tib34T8Vq*~w?X7S==>V(_~KX<-^q@b`jRgj>0WBh zt~ z6~rX99w%<->f>4-`wVh+r?T8FYFPs!C~#ep3Q*Dw#L48GXzlKE#*b^=eT`R&(K)o6 z5pn@1BzVEb1U-HCQs3%+4!HW(t3*p);ZxR|o0|Z@fwm1QR5;u>a-%aty7AQ9x2q%L zUqj4S#@4iZkK4~_SI7YO6osB#yuAI2Q?Bak)BRfpMN(y*YI}NNTQ*$zx+`bQR0{B4 zS(b;E!Q$)JEVqvq1x6byN0(=W9FX+8=zpylmD6%cvi7$5LTB%zF4=p6MOw*aF3MgL zhHo1(E4mLP@J4EU3Jt&Z@T81j>w>51eEr=E+Tx~kWBlsaxl{gS((5->pXUbp``5kE zkoa62dF}Xa0iYz{K!@Gi0{5+L>mM#ZbR(7ILYut_JTf~v<~#6^Zkcm&zr@2?tT+e3;Njy`XjzO@M1El;$_pd$)JAkWiQkEQq(Dt#n1&5j|eJX z&VIpX)UuNPD%|_^6B>tE}Kv}lP7 zd2j#+SB>p)IIz=Aq^77Coa;&N?Ngf%bM3Df41C(CmAAT5q$K^sD&UAd!exFuU#05q za_;y$CoXaNzz&T}<^sS4&^rQ*GOEOp6y16qoP%>AqPH^iGW$i}h;m-Qu}Mx_J|i9c%Z1LsEnJ$@83QcyL{)XnvA7x=Ao`hrkrs z$MtZ>;6MrK&E(tl2jb~~zsp~UX5p@3YOWC}1BUQk-zJ(bff3a(+M+g5Qi%{s-B|3n zU*|=6Ft8_Rm(05!`TbzjxYGb+2I_}$XIKMoP>CPHn59}iFE zX2t7Sp40a!^_PpjfoiW9Kw>>V5jsbPdi-L?d;hK88~06zv9!C8k`}meFLrvh3U_PRe=9{iR={0(QU6F zYuZ-k#Ye3R49JtkS!d`#5MagERSeYml%<%Ejez#AlH(s#N+cLZ3oZIYjbsI8;RLcQO+57rP#O&WVwc6dcyQkAcv88>Y$#8kT!k(Oy+=cV! zdZ&7ujCU>sFKpB$W-*fUeqt0>0=czV(v+g*i>%Le{T3Y90P=S69VNM;o!iE5tg6(z zgWpU)T(ucCdEFzpD$yD$bt9k9c~#K*`n^kk*}jKw1;nVQzi|7!m-d3I4bMnL%@z|8zD|qfSeH?EI;B#NW*yuR{*UZdR zI6^!G07NgFpBCclCb(FJXs)q=PJTI5?#EeZ8=llN!jlV6c|X=Sdb2=55GbiWElUNh z06;3KXG;82uh~WbsH!Xy1okF0e6XVmt+JI*R4H5w+{{()pOWAnD;HFLDZO(Ugad{k zz|zuI?hXGd?Ng%J#+T)rlsN!pORHu8aBZ8Ca|(+(B6T)UI&wKs-!7fK@MP}fd+BTr zApPbY=QB(Iv*!bz*+kx8z_*=*+&1Q^ol#j$t z3tqbe%R-B!6=sHT07u57lK09!ESGLCdVevD*9h<@M+WOiMDVArxq49-0syiB_hv^w zuHyF>4SDK>dAf-^2KVGkon3S44o=`gJ2g_qln^@oDZwe#Am~jouA_joZ{gz^p#{IP z$hjxEGOKSW2Z^2K(cbZa2!c;4$amOP@@1XXCX;k+VW<8l>Gs~ry9+$6D+S131QD1h z$J=p}8;kl=P=)8nREVEIv7jK zq`{@Fy=k)0I@dS zvcfbDpgYp^^Z?yUmWOkO;i~qG{WFo?PZlO-uRmOw?w(Hh#x+Ca;&-p{$bRUB@2@jC z{06RC{aWVIeu(?7%eyjyF3pF;epDPy$P6Fij6LQ!W+ias zY9>BnRZVCJCva7$|B5YmGakibrpo40IEDVKC}Ky~#GLEg+m52r;1=Y`juY*E_Rfbz z2J9>D?GP5$`|G+<>Q2|dnCffI*`@1p1BA+jZb+K%s!Vva)oxdG3vpk9wmrhWkr4?0!UueKl76C4*5_?((!LWPcP!hN*7Py`X_MPRxXgR5;(JMx z51gF0G(*;)`@IXh%|-PSUuS3M>IWb43WzPkt$lPSLGzuV_N>A-^S--`M~2msLEyhF ziqJT)=ZblHzs1BnvY=MZ3h<8*Rj10$W$hFMj=X8rD;6m_PT>U4+nctvkPaclSH4d* zpkN{+sGSDwyYQ;Ewio6`#jI$D^~jc*{nxUL@1kmAGf2J9*E*!HxDE9(7A{e|t0atl zxa-S?Jz0)!=o`0^o8$b8HakUc;?I}J!`l(a_efH|th2S$E-uB% z39E*G&GnH3zWRmClP5Vty9&noKdO$EMviBB&WzuEnh5}A`n-BsiP1&Lodr2kEed}mKF!kQO}=oRhCAE^i4q3=3Ol7 zo0`wWFKCohq2j7_ng-~~VD_VGuNL%t9gxW6)lwz~S>RL<$0!EARNR)+e}R8KkI$i*Z#2oMc)Yu{_;GCd@F3&Ryp~_f zuA*mEXy_p?lctu=K#qFJ6G{%6Ev^p^o+`zd6zL8c~2owQCqWt=@pQ&m1 z-Vh&&8@=L%qpya!tNXjv``*JwHWbT@NV}m82gm$oeQ?o{gA=|3!(+i`Jcwqk0mF9$ z8*2R#tfglEKr6mB^N;$38~=Lq#6f{h+{8zZ@Y!S+E>@%Wf)pS6Kx99U=ov;1}E1 zdzq~!fDd%V#?Cbz!^?}D97nv~z4Ayt8R28#`eAvFkdHiv)ai9e(+S0~S~lPzax$Z+ zp)by5!c{6(sNaM93@l6ig6m6z=87Ntw}0T%te)d@*U{An_lW2!r%n%L-f|;7`oZ9I__iRWtiiH)x^-zD}K)5at}_;ENJ`DvZjRGR_+#~YxlX`qp||46`p E0nIj>f&c&j literal 5237 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 diff --git a/android/src/main/res/drawable-xhdpi/ic_launcher.png b/android/src/main/res/drawable-xhdpi/ic_launcher.png index 71c6d760f05183ef8a47c614d8d13380c8528499..1afe1a5737431accc2ba8eabfc2d11c3efe6a676 100644 GIT binary patch literal 25472 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 C({dI7 literal 14383 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 diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index bafe393..7815956 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - LayoutEngine + PanoPainter diff --git a/engine/app.cpp b/engine/app.cpp index 2fb52f3..56887f3 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -259,7 +259,8 @@ void App::initLayout() stroke->create(); stroke->loaded(); - canvas->m_brush = stroke->m_canvas->m_brush; + if (canvas) + canvas->m_brush = stroke->m_canvas->m_brush; brushes->on_brush_changed = [this](Node* target, int index) { auto tid = brushes->get_texture_id(index); @@ -273,13 +274,15 @@ void App::initLayout() color->on_color_changed = [this](Node* target, glm::vec4 color) { stroke->m_canvas->m_brush.m_tip_color = color; stroke->m_canvas->draw_stroke(); - canvas->m_brush = stroke->m_canvas->m_brush; + if (canvas) + canvas->m_brush = stroke->m_canvas->m_brush; if (on_color_change) on_color_change(color); }; stroke->on_stroke_change = [this](Node*target) { - canvas->m_brush = stroke->m_canvas->m_brush; + if (canvas) + canvas->m_brush = stroke->m_canvas->m_brush; if (on_stroke_change) on_stroke_change(); }; @@ -349,16 +352,20 @@ void App::initLayout() { button->on_click = [this,button](Node*) { //exit(0); - canvas->m_canvas->m_use_instanced = !canvas->m_canvas->m_use_instanced; - //button->color_normal = canvas->m_canvas->m_use_instanced ? glm::vec4(1, 0, 0, 1) : glm::vec4(0, 1, 0, 1); - button->m_text->set_text(canvas->m_canvas->m_use_instanced ? "INST" : "NORM"); + if (canvas) + { + canvas->m_canvas->m_use_instanced = !canvas->m_canvas->m_use_instanced; + //button->color_normal = canvas->m_canvas->m_use_instanced ? glm::vec4(1, 0, 0, 1) : glm::vec4(0, 1, 0, 1); + button->m_text->set_text(canvas->m_canvas->m_use_instanced ? "INST" : "NORM"); + } }; } if (auto* button = layout[main_id]->find("btn-close")) { button->on_click = [this](Node*) { //exit(0); - canvas->m_canvas->clear(); + if (canvas) + canvas->m_canvas->clear(); }; } if (auto* button = layout[main_id]->find("btn-popup")) @@ -491,9 +498,9 @@ void App::update(float dt) if (canvas && canvas->m_canvas) canvas->m_canvas->stroke_draw(); - glClearColor(.1f, .1f, .1f, 1.f); - glViewport(0, 0, (GLsizei)width, (GLsizei)height); - glClear(GL_COLOR_BUFFER_BIT); + //glClearColor(.1f, .1f, .1f, 1.f); + //glViewport(0, 0, (GLsizei)width, (GLsizei)height); + //glClear(GL_COLOR_BUFFER_BIT); #ifndef __ANDROID__ layout.reload(); diff --git a/engine/brush.cpp b/engine/brush.cpp index 15d3c66..50cd019 100644 --- a/engine/brush.cpp +++ b/engine/brush.cpp @@ -3,3 +3,218 @@ +void ui::BrushMesh::draw(const std::vector& samples, const glm::mat4& proj) +{ + std::vector attributes; + attributes.reserve(samples.size()); + for (const auto& s : samples) + { + auto mvp = proj * + glm::translate(glm::vec3(s.pos, 0)) * + glm::scale(glm::vec3(s.size, s.size, 1)) * + glm::eulerAngleZ(s.angle); + attributes.emplace_back(instance_t{ mvp, s.flow }); + } +#ifdef USE_VBO + glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); + glBufferData(GL_ARRAY_BUFFER, (int)(sizeof(instance_t) * attributes.size()), attributes.data(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindVertexArray(vao); + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, (int)samples.size()); + glBindVertexArray(0); +#else + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + + // Likewise, we can do the same with the model matrix. Note that a + // matrix input to the vertex shader consumes N consecutive input + // locations, where N is the number of columns in the matrix. So... + // we have four vertex attributes to set up. + glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); + glBufferData(GL_ARRAY_BUFFER, (int)(sizeof(instance_t) * attributes.size()), attributes.data(), GL_STATIC_DRAW); + // Loop over each column of the matrix... + for (int i = 0; i < 4; i++) + { + // Set up the vertex attribute + glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), + (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); + // Enable it + glEnableVertexAttribArray(loc_mvp + i); + // Make it instanced + glVertexAttribDivisor(loc_mvp + i, 1); + } + glEnableVertexAttribArray(loc_flow); + glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), + (GLvoid*)offsetof(instance_t, flow)); + glVertexAttribDivisor(loc_flow, 1); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, (int)samples.size()); + //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + for (int i = 0; i < 4; i++) + glDisableVertexAttribArray(loc_mvp + i); + glDisableVertexAttribArray(loc_flow); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif // USE_VBO +} +bool ui::BrushMesh::create() +{ + static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 }; + static vertex_t vertices[4]{ + { { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C + { { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | | + { { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | | + { { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D + }; + glGenBuffers(3, buffers); + if (!buffers) + return false; + + static instance_t inst{ glm::mat4(), .1f }; + glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // STROKE - INSTANCED + static const char* shader_stroke_inst_v = + SHADER_VERSION + "in vec4 pos;" + "in vec2 uvs;" + "in mat4 a_mvp;" + "in float a_flow;" + "out vec3 uv;" + "out float alpha;" + "void main(){" + " uv = vec3(uvs, pos.w);" + " alpha = a_flow;" + " gl_Position = a_mvp * vec4(pos.xyz, 1.0);" + "}"; + static const char* shader_stroke_inst_f = + SHADER_VERSION + "uniform sampler2D tex;" + "uniform vec4 col;" + "in float alpha;" + "in vec3 uv;" + "out vec4 frag;" + "void main(){" + " float a = (1.0 - texture(tex, uv.xy).r) * alpha;" + " frag = vec4(col.rgb, a);" + "}"; + + + if (!shader.create(shader_stroke_inst_v, shader_stroke_inst_f)) + LOG("Failed to create shader Texture"); + + loc_flow = shader.GetAttribLocation("a_flow"); + loc_mvp = shader.GetAttribLocation("a_mvp"); + +#if USE_VBO + glGenVertexArrays(1, &vao); + if (!vao) + return false; + glBindVertexArray(vao); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); + // Loop over each column of the matrix... + for (int i = 0; i < 4; i++) + { + // Set up the vertex attribute + glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), + (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); + // Enable it + glEnableVertexAttribArray(loc_mvp + i); + // Make it instanced + glVertexAttribDivisor(loc_mvp + i, 1); + } + glEnableVertexAttribArray(loc_flow); + glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), + (GLvoid*)offsetof(instance_t, flow)); + glVertexAttribDivisor(loc_flow, 1); + glBindVertexArray(0); +#endif + + return true; +} +ui::StrokeSample ui::Stroke::randomize_sample(const glm::vec2& pos, float pressure) +{ + 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 + + StrokeSample s; + s.angle = (m_brush.m_tip_angle + rnd_nor() * m_brush.m_jitter_angle) * (float)(M_PI * 2.0); + s.pos = pos + (rnd_vec() * m_brush.m_jitter_spread * 100.f); + s.size = 100.f * m_brush.m_tip_size * (1.f - rnd_nor() * m_brush.m_jitter_scale); + s.flow = m_brush.m_tip_flow * (1.f - rnd_nor() * m_brush.m_jitter_flow); + return s; +} +std::vector ui::Stroke::compute_samples() +{ + 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 + while (m_keypoints.back().dist > (m_dist + m_step)) + { + m_dist += m_step; + while (m_dist > m_keypoints[m_last_kp + 1].dist) + m_last_kp++; + const auto& A = m_keypoints[m_last_kp]; + const auto& B = m_keypoints[m_last_kp + 1]; // NOTE: this should be true when while is true + float t = (m_dist - A.dist) / (B.dist - A.dist); // NOTE: must be A != B + auto pos = glm::lerp(A.pos, B.pos, t); + float pressure = glm::lerp(A.pressure, B.pressure, t); + auto s = randomize_sample(pos, pressure); + samples.push_back(s); + } + return std::move(samples); +} +bool ui::Stroke::has_sample() +{ + return m_keypoints.empty() ? false : // no keypoints + (m_keypoints.back().dist > (m_dist + m_step)); // check if next kp is closer than spacing +} +void ui::Stroke::reset(bool clear_keypoints /*= false*/) +{ + m_last_kp = 0; + m_dist = 0.f; + if (clear_keypoints) + m_keypoints.clear(); +} +void ui::Stroke::add_point(glm::vec2 pos, float pressure) +{ + float dist = m_keypoints.empty() ? 0.f : + m_keypoints.back().dist + glm::distance(m_keypoints.back().pos, pos); + m_keypoints.emplace_back(); + m_keypoints.back().pos = pos; + m_keypoints.back().pressure = pressure; + m_keypoints.back().dist = dist; +} +void ui::Stroke::start(const ui::Brush& brush) +{ + m_last_kp = 0; + m_dist = 0.f; + m_step = glm::max(brush.m_tip_spacing * brush.m_tip_size * 30, 0.1f); + m_brush = brush; + prng.seed(0); +} \ No newline at end of file diff --git a/engine/brush.h b/engine/brush.h index ed2afa1..7a7caf0 100644 --- a/engine/brush.h +++ b/engine/brush.h @@ -40,158 +40,8 @@ public: int loc_flow; int loc_mvp; - bool create() - { - static GLushort idx[6] { 0, 1, 2, 0, 2, 3 }; - static vertex_t vertices[4]{ - { { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C - { { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | | - { { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | | - { { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D - }; - glGenBuffers(3, buffers); - if (!buffers) - return false; - - static instance_t inst{ glm::mat4(), .1f }; - glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - // STROKE - INSTANCED - static const char* shader_stroke_inst_v = - SHADER_VERSION - "in vec4 pos;" - "in vec2 uvs;" - "in mat4 a_mvp;" - "in float a_flow;" - "out vec3 uv;" - "out float alpha;" - "void main(){" - " uv = vec3(uvs, pos.w);" - " alpha = a_flow;" - " gl_Position = a_mvp * vec4(pos.xyz, 1.0);" - "}"; - static const char* shader_stroke_inst_f = - SHADER_VERSION - "uniform sampler2D tex;" - "uniform vec4 col;" - "in float alpha;" - "in vec3 uv;" - "out vec4 frag;" - "void main(){" - " float a = (1.0 - texture(tex, uv.xy).r) * alpha;" - " frag = vec4(col.rgb, a);" - "}"; - - - if (!shader.create(shader_stroke_inst_v, shader_stroke_inst_f)) - LOG("Failed to create shader Texture"); - - loc_flow = shader.GetAttribLocation("a_flow"); - loc_mvp = shader.GetAttribLocation("a_mvp"); - -#if USE_VBO - glGenVertexArrays(1, &vao); - if (!vao) - return false; - glBindVertexArray(vao); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); - // Loop over each column of the matrix... - for (int i = 0; i < 4; i++) - { - // Set up the vertex attribute - glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), - (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); - // Enable it - glEnableVertexAttribArray(loc_mvp + i); - // Make it instanced - glVertexAttribDivisor(loc_mvp + i, 1); - } - glEnableVertexAttribArray(loc_flow); - glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), - (GLvoid*)offsetof(instance_t, flow)); - glVertexAttribDivisor(loc_flow, 1); - glBindVertexArray(0); -#endif - - return true; - } - void draw(const std::vector& samples, const glm::mat4& proj) - { - std::vector attributes; - attributes.reserve(samples.size()); - for (const auto& s : samples) - { - auto mvp = proj * - glm::translate(glm::vec3(s.pos, 0)) * - glm::scale(glm::vec3(s.size, s.size, 1)) * - glm::eulerAngleZ(s.angle); - attributes.emplace_back(instance_t{ mvp, s.flow }); - } -#ifdef USE_VBO - glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); - glBufferData(GL_ARRAY_BUFFER, (int)(sizeof(instance_t) * attributes.size()), attributes.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindVertexArray(vao); - glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, (int)samples.size()); - glBindVertexArray(0); -#else - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - - // Likewise, we can do the same with the model matrix. Note that a - // matrix input to the vertex shader consumes N consecutive input - // locations, where N is the number of columns in the matrix. So... - // we have four vertex attributes to set up. - glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); - glBufferData(GL_ARRAY_BUFFER, (int)(sizeof(instance_t) * attributes.size()), attributes.data(), GL_STATIC_DRAW); - // Loop over each column of the matrix... - for (int i = 0; i < 4; i++) - { - // Set up the vertex attribute - glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), - (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); - // Enable it - glEnableVertexAttribArray(loc_mvp + i); - // Make it instanced - glVertexAttribDivisor(loc_mvp + i, 1); - } - glEnableVertexAttribArray(loc_flow); - glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), - (GLvoid*)offsetof(instance_t, flow)); - glVertexAttribDivisor(loc_flow, 1); - - glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, (int)samples.size()); - //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - for (int i = 0; i < 4; i++) - glDisableVertexAttribArray(loc_mvp + i); - glDisableVertexAttribArray(loc_flow); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -#endif // USE_VBO - } + bool create(); + void draw(const std::vector& samples, const glm::mat4& proj); }; class Stroke @@ -211,62 +61,12 @@ public: std::vector m_samples; int m_last_kp; std::minstd_rand prng; - void start(glm::vec2 pos, float pressure, const ui::Brush& brush) - { - m_last_kp = 0; - m_dist = 0.f; - m_step = glm::max(brush.m_tip_spacing * brush.m_tip_size * 30, 0.1f); - m_brush = brush; - add_point(pos, pressure); - } - void add_point(glm::vec2 pos, float pressure) - { - float dist = m_keypoints.empty() ? 0.f : - m_keypoints.back().dist + glm::distance(m_keypoints.back().pos, pos); - m_keypoints.emplace_back(); - m_keypoints.back().pos = pos; - m_keypoints.back().pressure = pressure; - m_keypoints.back().dist = dist; - } - bool has_sample() - { - return m_keypoints.empty() ? false : // no keypoints - (m_keypoints.back().dist > (m_dist + m_step)); // check if next kp is closer than spacing - } - std::vector compute_samples() - { - 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 - while (m_keypoints.back().dist > (m_dist + m_step)) - { - m_dist += m_step; - while (m_dist > m_keypoints[m_last_kp + 1].dist) - m_last_kp++; - const auto& A = m_keypoints[m_last_kp]; - const auto& B = m_keypoints[m_last_kp + 1]; // NOTE: this should be true when while is true - float t = (m_dist - A.dist) / (B.dist - A.dist); // NOTE: must be A != B - auto pos = glm::lerp(A.pos, B.pos, t); - float pressure = glm::lerp(A.pressure, B.pressure, t); - auto s = randomize_sample(pos, pressure); - samples.push_back(s); - } - return std::move(samples); - } - StrokeSample randomize_sample(const glm::vec2& pos, float pressure) - { - 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 - - StrokeSample s; - s.angle = (m_brush.m_tip_angle + rnd_nor() * m_brush.m_jitter_angle) * (float)(M_PI * 2.0); - s.pos = pos + (rnd_vec() * m_brush.m_jitter_spread * 100.f); - s.size = 100.f * m_brush.m_tip_size * (1.f - rnd_nor() * m_brush.m_jitter_scale); - s.flow = m_brush.m_tip_flow * (1.f - rnd_nor() * m_brush.m_jitter_flow); - return s; - } + void start(const ui::Brush& brush); + void add_point(glm::vec2 pos, float pressure); + void reset(bool clear_keypoints = false); + bool has_sample(); + std::vector compute_samples(); + StrokeSample randomize_sample(const glm::vec2& pos, float pressure); }; class Layer diff --git a/engine/canvas.cpp b/engine/canvas.cpp index 8f50fbd..1862899 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -3,3 +3,119 @@ +void ui::Canvas::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(); +} +void ui::Canvas::stroke_end() +{ + m_current_stroke = nullptr; +} +void ui::Canvas::stroke_draw() +{ + if (!(m_current_stroke && m_current_stroke->has_sample())) + return; + + 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 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 samples = m_current_stroke->compute_samples(); + auto& tex = TextureManager::get(m_brush.m_tex_id); + tex.bind(); + m_sampler.bind(0); + + if (m_use_instanced) + { + m_mesh.shader.use(); + m_mesh.shader.u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + m_mesh.shader.u_int(kShaderUniform::Tex, 0); + m_mesh.draw(samples, proj); + } + else + { + ShaderManager::use("stroke"); + ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + ShaderManager::u_int(kShaderUniform::Tex, 0); + for (const auto& s : samples) + { + auto mvp = proj * + glm::translate(glm::vec3(s.pos, 0)) * + 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(); + } + } + + 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 ui::Canvas::stroke_update(glm::vec2 point, float pressure) +{ + m_current_stroke->add_point(point, pressure); +} +void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush) +{ + m_strokes.emplace_back(); + m_strokes.back().start(brush); + m_strokes.back().add_point(point, pressure); + m_current_stroke = &m_strokes.back(); +} +void ui::Canvas::layer_add(std::string name) +{ + m_layers.emplace_back(); + m_layers.back().create(m_width, m_height, name); +} +void ui::Canvas::resize(int width, int height) +{ + m_width = width; + m_height = height; + m_tmp->create(width, height, "tmp"); + m_fb.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_fb.create(width, height); + m_sampler.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 1279edf..6dd3f5b 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -9,136 +9,28 @@ NS_START class Canvas { + Stroke* m_current_stroke = nullptr; + Plane m_plane; + BrushMesh m_mesh; + int m_current_layer_idx = 0; public: int m_width; int m_height; + bool m_use_instanced = false; 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; - BrushMesh m_mesh; - bool m_use_instanced = false; - 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); - m_mesh.create(); - return true; - } - void resize(int width, int height) - { - m_width = width; - m_height = height; - m_tmp->create(width, height, "tmp"); - m_fb.create(width, height); - } - 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().start(point, pressure, brush); - m_current_stroke = &m_strokes.back(); - } - void stroke_update(glm::vec2 point, float pressure) - { - m_current_stroke->add_point(point, pressure); - } - void stroke_draw() - { - if (!(m_current_stroke && m_current_stroke->has_sample())) - return; - 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 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 samples = m_current_stroke->compute_samples(); - auto& tex = TextureManager::get(m_brush.m_tex_id); - tex.bind(); - m_sampler.bind(0); - - if (m_use_instanced) - { - m_mesh.shader.use(); - m_mesh.shader.u_vec4(kShaderUniform::Col, m_brush.m_tip_color); - m_mesh.shader.u_int(kShaderUniform::Tex, 0); - m_mesh.draw(samples, proj); - } - else - { - ShaderManager::use("stroke"); - ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); - ShaderManager::u_int(kShaderUniform::Tex, 0); - for (const auto& s : samples) - { - auto mvp = proj * - glm::translate(glm::vec3(s.pos, 0)) * - 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(); - } - } - - 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(); - } + bool create(int width, int height); + void resize(int width, int height); + void layer_add(std::string name); + void stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush); + void stroke_update(glm::vec2 point, float pressure); + void stroke_draw(); + void stroke_end(); + void clear(); }; NS_END diff --git a/engine/layout.h b/engine/layout.h index 6ba3dee..1ca9e3c 100644 --- a/engine/layout.h +++ b/engine/layout.h @@ -1676,8 +1676,11 @@ class NodeStrokePreview : public NodeBorder { RTT m_rtt; Sampler m_sampler; + ui::BrushMesh m_mesh; public: ui::Brush m_brush; + ui::Stroke m_stroke; + std::vector m_bez_points; virtual Node* clone_instantiate() const override { return new NodeStrokePreview(); } virtual void clone_copy(Node* dest) const override { @@ -1694,6 +1697,7 @@ public: } void init_controls() { + m_mesh.create(); m_sampler.create(); TextureManager::load("data/Icons/Round-Hard.png"); m_brush.m_tex_id = const_hash("data/Icons/Round-Hard.png"); @@ -1710,46 +1714,47 @@ public: double w = (double)m_rtt.getWidth(); double h = (double)m_rtt.getHeight(); - std::vector kp = { {30, 30}, {30, h-30}, {w-30, 30}, { w-30, h-30 } }; glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); glEnable(GL_BLEND); glm::mat4 proj = glm::ortho(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1); - auto& t = TextureManager::get(m_brush.m_tex_id); - float alpha = 0; - std::minstd_rand prng; - ShaderManager::use("stroke"); - ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); - ShaderManager::u_int(kShaderUniform::Tex, 0); - t.bind(); + m_stroke.reset(); + m_stroke.start(m_brush); + auto samples = m_stroke.compute_samples(); + auto& tex = TextureManager::get(m_brush.m_tex_id); + tex.bind(); m_sampler.bind(0); - while (alpha < 1.f) + + if (true) { - 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 = BezierCurve::Bezier2D(kp, alpha) + (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); - ShaderManager::u_mat4(kShaderUniform::MVP, mvp); - ShaderManager::u_float(kShaderUniform::Alpha, flow); - m_plane.draw_fill(); + m_mesh.shader.use(); + m_mesh.shader.u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + m_mesh.shader.u_int(kShaderUniform::Tex, 0); + m_mesh.draw(samples, proj); } +// else +// { +// ShaderManager::use("stroke"); +// ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); +// ShaderManager::u_int(kShaderUniform::Tex, 0); +// for (const auto& s : samples) +// { +// auto mvp = proj * +// glm::translate(glm::vec3(s.pos, 0)) * +// 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(); +// } +// } + m_sampler.unbind(); - t.unbind(); + tex.unbind(); glDisable(GL_BLEND); glViewport(vp[0], vp[1], vp[2], vp[3]); @@ -1771,6 +1776,14 @@ public: } virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override { + float pad = 30.f; + float w = new_size.x; + float h = new_size.y; + std::vector kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; + m_stroke.start(m_brush); + for (int i = 0; i < 20; i++) + m_stroke.add_point(BezierCurve::Bezier2D(kp, i / 20.f), 1.f); + m_rtt.destroy(); m_rtt.create((int)new_size.x, (int)new_size.y); draw_stroke();