From b2cf576bf396aed8743d267a356d06c7f966462c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Jun 2018 09:12:16 +0200 Subject: [PATCH] 1st installment of gizmos --- lib/Slic3r/GUI/Plater.pm | 1 + resources/icons/overlay/rotate_hover.png | Bin 0 -> 3808 bytes resources/icons/overlay/rotate_off.png | Bin 0 -> 4514 bytes resources/icons/overlay/rotate_on.png | Bin 0 -> 3441 bytes resources/icons/overlay/scale_hover.png | Bin 0 -> 6474 bytes resources/icons/overlay/scale_off.png | Bin 0 -> 7232 bytes resources/icons/overlay/scale_on.png | Bin 0 -> 5293 bytes xs/CMakeLists.txt | 8 +- xs/src/slic3r/GUI/3DScene.cpp | 11 +- xs/src/slic3r/GUI/3DScene.hpp | 3 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 448 ++++++++++++++++------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 126 +++++-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 21 +- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 + xs/src/slic3r/GUI/GLGizmo.cpp | 109 ++++++ xs/src/slic3r/GUI/GLGizmo.hpp | 78 ++++ xs/src/slic3r/GUI/GLTexture.cpp | 138 +++++++ xs/src/slic3r/GUI/GLTexture.hpp | 36 ++ xs/xsp/GUI_3DScene.xsp | 7 + 19 files changed, 812 insertions(+), 177 deletions(-) create mode 100644 resources/icons/overlay/rotate_hover.png create mode 100644 resources/icons/overlay/rotate_off.png create mode 100644 resources/icons/overlay/rotate_on.png create mode 100644 resources/icons/overlay/scale_hover.png create mode 100644 resources/icons/overlay/scale_off.png create mode 100644 resources/icons/overlay/scale_on.png create mode 100644 xs/src/slic3r/GUI/GLGizmo.cpp create mode 100644 xs/src/slic3r/GUI/GLGizmo.hpp create mode 100644 xs/src/slic3r/GUI/GLTexture.cpp create mode 100644 xs/src/slic3r/GUI/GLTexture.hpp diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 18545db3e..66d2f2f7b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -132,6 +132,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); + Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); diff --git a/resources/icons/overlay/rotate_hover.png b/resources/icons/overlay/rotate_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..56d4fd27754854df64a57aabf1268686b755ab8c GIT binary patch literal 3808 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 wZ<&*^uXsgO1L;I{TmS$7 literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/rotate_on.png b/resources/icons/overlay/rotate_on.png new file mode 100644 index 0000000000000000000000000000000000000000..e2db5120c1c63416f73edbe3461243cd20ac8a46 GIT binary patch literal 3441 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 literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/scale_off.png b/resources/icons/overlay/scale_off.png new file mode 100644 index 0000000000000000000000000000000000000000..1ae999bbe8c609cab1edc0033f8a25f49bb1562e GIT binary patch literal 7232 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 H4}<>)4z|#; literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/scale_on.png b/resources/icons/overlay/scale_on.png new file mode 100644 index 0000000000000000000000000000000000000000..62e805f12df414b6e45ee066d068fd7b1550743a GIT binary patch literal 5293 zcmV;e6jJMnP)3`{VGTPZBq0#U4w)=7bIyDJcxK432S_-d&wP?OGjq;) zp7&XP&+mC&MGGC;3(9xS^Sy!VHLAacYZSVNf(}3u5Fh@UA2nOs=o#ykM~*Fd07^xkirT(IjvIJO@~ z-UD5(gNBn(unE#LVZvjmBfH__XX2?Yizi4Y6P>AI7^l6gbx=Ojr*qxe4goK?xO9uK zE=6!o-M}F*!^kuZeJ5H^!Da}WSZp9E7N-3#bk2f3t6=f3Aru6c8)B2>|Ej~X%2o^< z+#o_9SSRoEH-a66zPCW1o1yanSoK%={m6S!6^ByT@#!FHt9l!T9_-BIU$){smyd$8 zpRWhRfxil_zYC`P5*q#R+EmC}6N_e)GU#ub z4AA$fchr2nEFySwcM%eR1N;kB3cw$`)I`Y94q;z-Ygx@uWPCj@o?3>O#{ta=yL zzbisnfV&xYw$3@M&m#Zg!Fyy1}Qc=?0jpw@Oi&jRKIEmn0kakA=XesKQ$Hx#2_1XGEe}COF|2tDju(kymLC`b*S5bd!j=|AAkknq%YDmlXOddyZLVI~V5*RNS1I{p$Nx+jhVINHH5canW zP$mR4=s*jA>8QgAzjV~g0ODdHE*WBzA*mfCro-VpIGzudEvfgF$&k=SGRAj)35o6C zvsd7F{*yM80KWJI_F1lQP74Tm)dcP6$CRn!Nlp<3EPMn8-VS?LLuFAIcD11JHjv&4 zGWtWC&XAA}iESm=rF8*M0=PV2#(+Cc(Bje;VRsJD2&D(WRS4INgz>Y%lL+O7u;#5e z^JMjG$ICIp$0z5tfQV+amrU*Gdz8yHfd03_w|)VYC9vpG_~LZ|H@X#s?{x!=dlF>?0Pmmxn_Zl+qn$2<0t7!#Lr`e*(ss40cC&SC|To4ez|op5?M z*BXxRhrj(qJ}=!XimfVv%Hl~lIuCd$x^S|hS3l%cLqJc#A(h%%IG7_Qf1*@Y-c)jv zmOc!icsu00D@9Pl38+6I_pCY$o+P;a@zXW$@<3dwxMH+LHr7MVVu>YpJq=m6!J%AO z`9`WP+%q}gRV!kAIyWM-6}|cfdb)-oDx-&(S6wyCe*m0d>!vyb$ln6hzmh=Pa1!ck zpgtf0@Q!E1r$L^{odBiXO#ZvQu`V9yW>JsrqBC3B>D z(q4KghcS2(Jpm+yZ#3{`f?Nz-2YNlHlQNI5J9J;HdcBnR8WhFZFhOUFI z4!VmjmQ>g|``j!=5J0d2{{0fHdJ}5H3h*S!OP>ehE_)ST9uHssRpd#}07GSh{sBPp z+aVDxG*el?I4n{rIS6aBVf9<^<^Kt=xLOK`hPu7J0e3tr_#+g6?Mp?F6UUn3P;3|* z=nGUjmK&zN2rul0sV_icdMY;G-1+?k>r@a_FcD3ATyE&n2g(Xy-u;q()KwNf2z2cO zV}A^p!=(H@vJ?LHFgUhkgt{vEZc0aq1Ns8MDzy`|zgAo*y&I%-g37Y?{bFk;081!ge(w1e5xSEiye8(VAl$W@jz-iRF%hLF$%y6^Y-m?l zfBHh0z|PMF-Pu7ncHrW~i=m+VFz7y9FvjMO!C4iPXxYOpL+OEpuoylChTkcK<(m1h zYfbZ_zY|_P6G%#hHklH5TpnS0?GOa(1=3a)OCeRS<)O_e>7gGG;#FnpT)pm-bXSaV^<&<+cq zkRTLzm+PQI9}yygBP_S9QlL%(SH&S=eGhIBZm+t$$vTc7fZUIS2>ogS?E2#TE@;qv z_f%m8Z(Smab$OudFqoJ?PYdXbiNWp_QtJomB)RiH9x&pxaPM^IJYPaqRBO@lv9o0}cj>sp2Dz zq4Tf4Mws9m?h#t7^MLSqM;^T4q!cm@wSwL(m*9AJg8Yq7m7EM>6)XM>>)w{OSx7fo zL`&k20MKUy-1!ppz6p-(LjUGQTh-Sbv4CV86#d6SkC9Na1NN>0|H-pgKm*C`VbYV5 zt38P#_{nf1WA{opxDHBoi!wruBCIC?QoBg=WZ>-*jN(#+*}3C6=sFnw`JA|(qgx() z8R4?}*nY8q!+9t>sDK2ey=V**p85)UTrWQQ{u8iq>Dd7&HW?mVC?%$8iAMIUf=}m& zNe4qsN>k^_5-w8^-pvofsOi#204UlD**}JTIhWC~Y-X^IPZp)u9D{~hjUp(IYun5n=`;4<|?}m zg75tXdJdzh0p7w#Vq2lyQaheX)5`Hus~`?ff+8+e(qLVUn07>St{9l~1WbHPe7VsN ze}7DTx50mTjdqP7vz{Yi)_h5y0Be1)_<1NfaAl2^GlTZELq&$6-<3w}A0|1vuDZqm zYS3s5m$bAyWeJ@W0W+o{(R19xO%&7zPf2&T&VTs=Z*5iFW-Qbe}(Gf zS5A_sFi1ImKr&0GETO*vO7}q7k(ME145WfA-X=)*>-VAjkPW)P01g8VrdvQvatF!9 z5&WIrP0S`jP_B9lDi60@j#fa>5%hUY)`?3NqICCqa2#7hfd%Zx zWGuNKx~Z#@l(uU{xY|mBo}*-S0JbfW3plhaMP>m<3t{&c(te9i6YQSW22NC5nX)wy zmEq!8BU z$S!%Go+;_lxx(ve1dMgck{H)(G_>sj4k2KdWoxy;QaXSFhJ9Ds-oXa==oz7;yA75L zRUd(ZTnGkQhb1x*3{qYwO^?L3(5XMpj}b zactUh%}BXvpkA6H0l%z-6Ot*6y6V;?ME&(m*&sFUr_FVnxueKadI!z;U4F=+z-bs*ea0l+Z@{g60*Pzb76h%!dBA3c>Q( zAHi|_8rGd6MT1%`vn(d6;%)KF|6x=9?>VyRN}*uuD?lM#p@x&9fP{2#yX4-N<90UU z`+aYb%u-(i+m}J{?rLqRWuPUTRfjxFL62g@!K0tR_(!CJR9lONqJTCQgxi4_@e4wP8>Bn6R%gNsJQOKgy!!$1QOB!>VZ}6 zY-f8|X1RQ%LF#}Hi(HQGmKy(~r{U`l!<14Y44N#lp*0i`MA(`Ry}}KVLaEv=m6^7I z_(U1wS@yCFajl&PWrx5ZS3x#F{Y;al)>ybfYGvzO?d6}?c0bCFq*8uF5a2Bj$rrA_ z3)a5_4ToButVoFyt|6_9tX|PJC@pM$NvshU&FCp)OQ1o7->^sup`cx@Ew$K~uX6!n zv+YyxgJ>%N-b1nE)C^GRUSV+wo9N&>z!N8$Zb|bW@qOqqLf)6`l&a0SL?s#~^cgDw ze9{xp?ph&SLpIt}OF~+|cZRG@3W%DhJfvZvhCEb+xO`2bG6<;Xb2C`#%5nOsrbu6D zhNg9qbNkjvAFsKquN1i5;-)9dg!`*3mhoN71VtX`g)u$~^ux`V5;J@((_Y$P@H&n4 zapZmgL#DvsZ%d){onH%F-nH`b2c~rR!P|6#8Lva;P&vPIIpi#as+#6CW*wkShOE3{ z2!cIXE?J~#E7*1&ir+;h#7e6?K*PBeaK>Ei5}*qPzadbMUi28MeJ^Q(WDbFEKO^aC z_X^?pE*bDx!W7T=i&SXJkihrF>##ScdBR7Of89jzz9|T@!7oaP^c57p4a*Xit>LT^ zYb2MYE-9 zGn*l}<(6J1G%rnO1Gl1uS`vDj*5vIM^R4Ca&AsN>C zV9Uo)Q6wo%oguUfsqT7x18$rS{l|#~DMLuug^xh)`!eukTtILTW@p+9k~O}36V@+; zZUdzq7h$O3O%2Z)mpUwc0VT{v^C*@JC%rrB_Qz2-PL~EK2#@HmlnzftF;o@`b#BE# zT32Y-15!H+G>uDX>JH_7CKI0p8-;DYP^M_p5sn{)o`Ym|x$9uqwiFirR7PZ7ZfS)p z<^V58CdF+pn7&J}fb%G!gGSBLT0f~YaVoW=nD2-?q2D)~3K)=#(dd_zPzqde;UUMU zqi{29dLME>l&EbxO$@URm&TEETlam~B`jt@J)O!=`_rSItEQgP^EG@jNh3 z`6#{6$~Q{^XI>!WHPRH$WCI=}NEWeSBDLEPG40M-(s+n)h|x!*U)bM*O@b@(H%dD# z;55w*M5uU=2d=q6GQ+_fQPzL2gzh6`U(#z+rGJs!5vog|rW_3N;Pfps&Dl59tORCz z73S%irX~RQXotxJNl`AKZCAnZ>6wDNT^_jZcH#A`7$H=b|4Ec!SQ5GGPY6j76DNda zT#ER!V+$2O;!fD{3A{QLZhHj!--_P76193RRFp-AE~>SumMHW)%Ez*EE!@TU*o&9d zK7HDDG$lA;k5(#AxtW62LpW9x%h96KH$IgglICYxXX(%9EQd5VjQtVZ{fb2XU0=xO z<7Z1YIaw}PKs(|-mRmT16|YH6e|Q%

YJ89@wnmUpBgRx6MUgFzN<}K)YU|w$3_Um8D@OFwQ`?1yXQ`Keav1it_W+4ubt_N@c5T z$bq4gB{LjY2Nk7F9HHzc^Mh=i1(w^|c-zW$c3i5tx2OWncXRE #include -#include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include #include @@ -23,6 +28,9 @@ #include #include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; @@ -237,105 +245,107 @@ void Rect::set_bottom(float bottom) m_bottom = bottom; } -GLCanvas3D::GLTextureData::GLTextureData() - : m_id(0) - , m_width(0) - , m_height(0) - , m_source("") -{ -} - -GLCanvas3D::GLTextureData::~GLTextureData() -{ - reset(); -} - -bool GLCanvas3D::GLTextureData::load_from_file(const std::string& filename) -{ - reset(); - - // Load a PNG with an alpha channel. - wxImage image; - if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) - { - reset(); - return false; - } - - m_width = image.GetWidth(); - m_height = image.GetHeight(); - int n_pixels = m_width * m_height; - - if (n_pixels <= 0) - { - reset(); - return false; - } - - // Get RGB & alpha raw data from wxImage, pack them into an array. - unsigned char* img_rgb = image.GetData(); - if (img_rgb == nullptr) - { - reset(); - return false; - } - - unsigned char* img_alpha = image.GetAlpha(); - - std::vector data(n_pixels * 4, 0); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - // sends data to gpu - ::glGenTextures(1, &m_id); - ::glBindTexture(GL_TEXTURE_2D, m_id); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - ::glBindTexture(GL_TEXTURE_2D, 0); - - m_source = filename; - return true; -} - -void GLCanvas3D::GLTextureData::reset() -{ - if (m_id != 0) - ::glDeleteTextures(1, &m_id); - - m_id = 0; - m_width = 0; - m_height = 0; - m_source = ""; -} - -unsigned int GLCanvas3D::GLTextureData::get_id() const -{ - return m_id; -} - -int GLCanvas3D::GLTextureData::get_width() const -{ - return m_width; -} - -int GLCanvas3D::GLTextureData::get_height() const -{ - return m_height; -} - -const std::string& GLCanvas3D::GLTextureData::get_source() const -{ - return m_source; -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//GLCanvas3D::GLTextureData::GLTextureData() +// : m_id(0) +// , m_width(0) +// , m_height(0) +// , m_source("") +//{ +//} +// +//GLCanvas3D::GLTextureData::~GLTextureData() +//{ +// reset(); +//} +// +//bool GLCanvas3D::GLTextureData::load_from_file(const std::string& filename) +//{ +// reset(); +// +// // Load a PNG with an alpha channel. +// wxImage image; +// if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) +// { +// reset(); +// return false; +// } +// +// m_width = image.GetWidth(); +// m_height = image.GetHeight(); +// int n_pixels = m_width * m_height; +// +// if (n_pixels <= 0) +// { +// reset(); +// return false; +// } +// +// // Get RGB & alpha raw data from wxImage, pack them into an array. +// unsigned char* img_rgb = image.GetData(); +// if (img_rgb == nullptr) +// { +// reset(); +// return false; +// } +// +// unsigned char* img_alpha = image.GetAlpha(); +// +// std::vector data(n_pixels * 4, 0); +// for (int i = 0; i < n_pixels; ++i) +// { +// int data_id = i * 4; +// int img_id = i * 3; +// data[data_id + 0] = img_rgb[img_id + 0]; +// data[data_id + 1] = img_rgb[img_id + 1]; +// data[data_id + 2] = img_rgb[img_id + 2]; +// data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; +// } +// +// // sends data to gpu +// ::glGenTextures(1, &m_id); +// ::glBindTexture(GL_TEXTURE_2D, m_id); +// ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +// ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +// ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); +// ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); +// ::glBindTexture(GL_TEXTURE_2D, 0); +// +// m_source = filename; +// return true; +//} +// +//void GLCanvas3D::GLTextureData::reset() +//{ +// if (m_id != 0) +// ::glDeleteTextures(1, &m_id); +// +// m_id = 0; +// m_width = 0; +// m_height = 0; +// m_source = ""; +//} +// +//unsigned int GLCanvas3D::GLTextureData::get_id() const +//{ +// return m_id; +//} +// +//int GLCanvas3D::GLTextureData::get_width() const +//{ +// return m_width; +//} +// +//int GLCanvas3D::GLTextureData::get_height() const +//{ +// return m_height; +//} +// +//const std::string& GLCanvas3D::GLTextureData::get_source() const +//{ +// return m_source; +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLCanvas3D::Camera::Camera() : type(Ortho) @@ -908,7 +918,10 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje ::glLoadIdentity(); _render_tooltip_texture(canvas, bar_rect, reset_rect); - _render_reset_texture(canvas, reset_rect); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + _render_reset_texture(reset_rect); +// _render_reset_texture(canvas, reset_rect); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _render_active_object_annotations(canvas, volume, print_object, bar_rect); _render_profile(print_object, bar_rect); @@ -1036,10 +1049,16 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap; float b = reset_bottom + gap; - canvas.render_texture(m_tooltip_texture.get_id(), l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t); +// canvas.render_texture(m_tooltip_texture.get_id(), l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } -void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) const +//void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { if (m_reset_texture.get_id() == 0) { @@ -1048,7 +1067,10 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, return; } - canvas.render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +// canvas.render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const @@ -1181,6 +1203,140 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f; +const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f; + +GLCanvas3D::Gizmos::Gizmos() + : m_enabled(false) + , m_current(None) +{ +} + +GLCanvas3D::Gizmos::~Gizmos() +{ + _reset(); +} + +bool GLCanvas3D::Gizmos::init() +{ + GLGizmoBase* gizmo = new GLGizmoScale; + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) + return false; + + m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); + + gizmo = new GLGizmoRotate; + if (gizmo == nullptr) + { + _reset(); + return false; + } + + if (!gizmo->init()) + { + _reset(); + return false; + } + + m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); + + return true; +} + +bool GLCanvas3D::Gizmos::is_enabled() const +{ + return m_enabled; +} + +void GLCanvas3D::Gizmos::set_enabled(bool enable) +{ + m_enabled = enable; +} + +void GLCanvas3D::Gizmos::select(EType type) +{ + if (m_gizmos.find(type) != m_gizmos.end()) + m_current = type; +} + +void GLCanvas3D::Gizmos::reset_selection() +{ + m_current = None; +} + +void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const +{ + if (!m_enabled) + return; + + ::glDisable(GL_DEPTH_TEST); + + ::glPushMatrix(); + ::glLoadIdentity(); + + _render_overlay(canvas); + _render_current_gizmo(); + + ::glPopMatrix(); +} + +void GLCanvas3D::Gizmos::_reset() +{ + for (GizmosMap::value_type& gizmo : m_gizmos) + { + delete gizmo.second; + gizmo.second = nullptr; + } + + m_gizmos.clear(); +} + +void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const +{ + if (m_gizmos.empty()) + return; + + const Size& cnv_size = canvas.get_canvas_size(); + + float cnv_w = (float)cnv_size.get_width(); + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float total_h = 0.0f; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + total_h += (float)it->second->get_textures_height(); + if (std::distance(it, m_gizmos.end()) > 1) + total_h += OverlayGapY; + } + + float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom; + float top_y = 0.5f * total_h * inv_zoom; + float scaled_gap_y = OverlayGapY * inv_zoom; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + float tex_w = (float)it->second->get_textures_width() * inv_zoom; + float tex_h = (float)it->second->get_textures_height() * inv_zoom; + GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_w, top_y - tex_h, top_y); + top_y -= (tex_h + scaled_gap_y); + } +} + +void GLCanvas3D::Gizmos::_render_current_gizmo() const +{ + GizmosMap::const_iterator it = m_gizmos.find(m_current); + if (it == m_gizmos.end()) + return; + + it->second->render(); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -1228,8 +1384,6 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (m_initialized) return true; - std::cout << "init: " << (void*)m_canvas << " (" << (void*)this << ")" << std::endl; - ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ::glClearDepth(1.0f); @@ -1287,6 +1441,11 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!m_volumes.empty()) m_volumes.finalize_geometry(m_use_VBOs); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (m_gizmos.is_enabled() && !m_gizmos.init()) + return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_initialized = true; return true; @@ -1528,6 +1687,13 @@ void GLCanvas3D::enable_moving(bool enable) m_moving_enabled = enable; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::enable_gizmos(bool enable) +{ + m_gizmos.set_enabled(enable); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void GLCanvas3D::enable_shader(bool enable) { m_shader_enabled = enable; @@ -1645,34 +1811,39 @@ void GLCanvas3D::render() _render_warning_texture(); _render_legend_texture(); _render_layer_editing_overlay(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + _render_gizmo(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_canvas->SwapBuffers(); } -void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const -{ - ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - ::glDisable(GL_LIGHTING); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - ::glEnable(GL_TEXTURE_2D); - - ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); - - ::glBegin(GL_QUADS); - ::glTexCoord2d(0.0f, 1.0f); glVertex3f(left, bottom, 0.0f); - ::glTexCoord2d(1.0f, 1.0f); glVertex3f(right, bottom, 0.0f); - ::glTexCoord2d(1.0f, 0.0f); glVertex3f(right, top, 0.0f); - ::glTexCoord2d(0.0f, 0.0f); glVertex3f(left, top, 0.0f); - ::glEnd(); - - ::glBindTexture(GL_TEXTURE_2D, 0); - - ::glDisable(GL_TEXTURE_2D); - ::glDisable(GL_BLEND); - ::glEnable(GL_LIGHTING); -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const +//{ +// ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +// +// ::glDisable(GL_LIGHTING); +// ::glEnable(GL_BLEND); +// ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +// ::glEnable(GL_TEXTURE_2D); +// +// ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); +// +// ::glBegin(GL_QUADS); +// ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f); +// ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f); +// ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f); +// ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f); +// ::glEnd(); +// +// ::glBindTexture(GL_TEXTURE_2D, 0); +// +// ::glDisable(GL_TEXTURE_2D); +// ::glDisable(GL_BLEND); +// ::glEnable(GL_LIGHTING); +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { @@ -3130,7 +3301,10 @@ void GLCanvas3D::_render_warning_texture() const float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; - render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(tex_id, l, r, b, t); +// render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glPopMatrix(); ::glEnable(GL_DEPTH_TEST); @@ -3162,7 +3336,10 @@ void GLCanvas3D::_render_legend_texture() const float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; - render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(tex_id, l, r, b, t); +// render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glPopMatrix(); ::glEnable(GL_DEPTH_TEST); @@ -3248,6 +3425,13 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::_render_gizmo() const +{ + m_gizmos.render(*this); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + float GLCanvas3D::_get_layers_editing_cursor_z_relative() const { return m_layers_editing.get_cursor_z_relative(*this); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d694db4e2..6421e44ec 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -2,6 +2,9 @@ #define slic3r_GLCanvas3D_hpp_ #include "../../slic3r/GUI/3DScene.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include "../../slic3r/GUI/GLTexture.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class wxTimer; class wxSizeEvent; @@ -18,6 +21,10 @@ class ExPolygon; namespace GUI { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +class GLGizmoBase; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + class GeometryBuffer { std::vector m_vertices; @@ -102,26 +109,28 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; - struct GLTextureData - { - private: - unsigned int m_id; - int m_width; - int m_height; - std::string m_source; - - public: - GLTextureData(); - ~GLTextureData(); - - bool load_from_file(const std::string& filename); - void reset(); - - unsigned int get_id() const; - int get_width() const; - int get_height() const; - const std::string& get_source() const; - }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// struct GLTextureData +// { +// private: +// unsigned int m_id; +// int m_width; +// int m_height; +// std::string m_source; +// +// public: +// GLTextureData(); +// ~GLTextureData(); +// +// bool load_from_file(const std::string& filename); +// void reset(); +// +// unsigned int get_id() const; +// int get_width() const; +// int get_height() const; +// const std::string& get_source() const; +// }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: struct Camera @@ -170,8 +179,12 @@ public: Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; - mutable GLTextureData m_top_texture; - mutable GLTextureData m_bottom_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + mutable GLTexture m_top_texture; + mutable GLTexture m_bottom_texture; +// mutable GLTextureData m_top_texture; +// mutable GLTextureData m_bottom_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: Bed(); @@ -266,8 +279,12 @@ public: bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; - mutable GLTextureData m_tooltip_texture; - mutable GLTextureData m_reset_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + mutable GLTexture m_tooltip_texture; + mutable GLTexture m_reset_texture; +// mutable GLTextureData m_tooltip_texture; +// mutable GLTextureData m_reset_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: EState state; @@ -306,7 +323,10 @@ public: private: bool _is_initialized() const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; - void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void _render_reset_texture(const Rect& reset_rect) const; +// void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; }; @@ -340,6 +360,49 @@ public: bool is_start_position_3D_defined() const; }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + class Gizmos + { + static const float OverlayOffsetX; + static const float OverlayGapY; + + public: + enum EType : unsigned char + { + None, + Scale, + Rotate, + Num_Types + }; + + private: + bool m_enabled; + typedef std::map GizmosMap; + GizmosMap m_gizmos; + EType m_current; + + public: + Gizmos(); + ~Gizmos(); + + bool init(); + + bool is_enabled() const; + void set_enabled(bool enable); + + void select(EType type); + void reset_selection(); + + void render(const GLCanvas3D& canvas) const; + + private: + void _reset(); + + void _render_overlay(const GLCanvas3D& canvas) const; + void _render_current_gizmo() const; + }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -351,6 +414,9 @@ private: LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + Gizmos m_gizmos; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable GLVolumeCollection m_volumes; DynamicPrintConfig* m_config; @@ -455,6 +521,9 @@ public: void enable_legend_texture(bool enable); void enable_picking(bool enable); void enable_moving(bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void enable_gizmos(bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void allow_multisample(bool allow); @@ -467,7 +536,9 @@ public: void update_volumes_colors_by_extruder(); void render(); - void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); @@ -546,6 +617,9 @@ private: void _render_legend_texture() const; void _render_layer_editing_overlay() const; void _render_volumes(bool fake_colors) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void _render_gizmo() const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float _get_layers_editing_cursor_z_relative() const; int _get_layers_editing_first_selected_object_id(unsigned int objects_count) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index b021e65a8..8a84d4cb5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -159,8 +159,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas) canvas3D->bind_event_handlers(); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); - std::cout << "canvas added: " << (void*)canvas << " (" << (void*)canvas3D << ")" << std::endl; - return true; } @@ -174,8 +172,6 @@ bool GLCanvas3DManager::remove(wxGLCanvas* canvas) delete it->second; m_canvases.erase(it); - std::cout << "canvas removed: " << (void*)canvas << std::endl; - return true; } @@ -183,8 +179,6 @@ void GLCanvas3DManager::remove_all() { for (CanvasesMap::value_type& item : m_canvases) { - std::cout << "canvas removed: " << (void*)item.second << std::endl; - item.second->unbind_event_handlers(); delete item.second; } @@ -200,8 +194,6 @@ void GLCanvas3DManager::init_gl() { if (!m_gl_initialized) { - std::cout << "GLCanvas3DManager::init_gl()" << std::endl; - glewInit(); if (m_gl_info.detect()) { @@ -209,10 +201,6 @@ void GLCanvas3DManager::init_gl() m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0); m_gl_initialized = true; - - std::cout << "DETECTED OPENGL: " << m_gl_info.version << std::endl; - std::cout << "USE VBOS = " << (m_use_VBOs ? "YES" : "NO") << std::endl; - std::cout << "LAYER EDITING ALLOWED = " << (!m_use_legacy_opengl ? "YES" : "NO") << std::endl; } else throw std::runtime_error(std::string("Unable to initialize OpenGL driver\n")); @@ -439,6 +427,15 @@ void GLCanvas3DManager::enable_moving(wxGLCanvas* canvas, bool enable) it->second->enable_moving(enable); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_gizmos(enable); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 741c8e29b..9ec645c1a 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -110,6 +110,9 @@ public: void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); void enable_moving(wxGLCanvas* canvas, bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void enable_gizmos(wxGLCanvas* canvas, bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_shader(wxGLCanvas* canvas, bool enable); void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp new file mode 100644 index 000000000..06ceee881 --- /dev/null +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -0,0 +1,109 @@ +#include "GLGizmo.hpp" + +#include "../../libslic3r/utils.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +GLGizmoBase::GLGizmoBase() + : m_state(Off) +{ +} + +GLGizmoBase::~GLGizmoBase() +{ +} + +GLGizmoBase::EState GLGizmoBase::get_state() const +{ + return m_state; +} + +unsigned int GLGizmoBase::get_textures_id() const +{ + return m_textures[m_state].get_id(); +} + +int GLGizmoBase::get_textures_height() const +{ + return m_textures[Off].get_height(); +} + +int GLGizmoBase::get_textures_width() const +{ + return m_textures[Off].get_width(); +} + +bool GLGizmoBase::init() +{ + return on_init(); +} + +GLGizmoRotate::GLGizmoRotate() + : GLGizmoBase() + , m_angle_x(0.0f) + , m_angle_y(0.0f) + , m_angle_z(0.0f) +{ +} + +void GLGizmoRotate::render() const +{ + std::cout << "GLGizmoRotate::render()" << std::endl; +} + +bool GLGizmoRotate::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + std::string filename = path + "rotate_off.png"; + if (!m_textures[Off].load_from_file(filename)) + return false; + + filename = path + "rotate_hover.png"; + if (!m_textures[Hover].load_from_file(filename)) + return false; + + filename = path + "rotate_on.png"; + if (!m_textures[On].load_from_file(filename)) + return false; + + return true; +} + +GLGizmoScale::GLGizmoScale() + : GLGizmoBase() + , m_scale_x(1.0f) + , m_scale_y(1.0f) + , m_scale_z(1.0f) +{ +} + +void GLGizmoScale::render() const +{ + std::cout << "GLGizmoScale::render()" << std::endl; +} + +bool GLGizmoScale::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + std::string filename = path + "scale_off.png"; + if (!m_textures[Off].load_from_file(filename)) + return false; + + filename = path + "scale_hover.png"; + if (!m_textures[Hover].load_from_file(filename)) + return false; + + filename = path + "scale_on.png"; + if (!m_textures[On].load_from_file(filename)) + return false; + + return true; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp new file mode 100644 index 000000000..1da216ba9 --- /dev/null +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -0,0 +1,78 @@ +#ifndef slic3r_GLGizmo_hpp_ +#define slic3r_GLGizmo_hpp_ + +#include "../../slic3r/GUI/GLTexture.hpp" + +namespace Slic3r { +namespace GUI { + +class GLGizmoBase +{ +public: + enum EState + { + Off, + Hover, + On, + Num_States + }; + +protected: + EState m_state; + // textures are assumed to be all the same size in pixels + // no internal check is done + GLTexture m_textures[Num_States]; + +public: + GLGizmoBase(); + virtual ~GLGizmoBase(); + + bool init(); + + EState get_state() const; + + unsigned int get_textures_id() const; + int get_textures_height() const; + int get_textures_width() const; + + virtual void render() const = 0; + +protected: + virtual bool on_init() = 0; +}; + +class GLGizmoRotate : public GLGizmoBase +{ + float m_angle_x; + float m_angle_y; + float m_angle_z; + +public: + GLGizmoRotate(); + + void render() const; + +protected: + virtual bool on_init(); +}; + +class GLGizmoScale : public GLGizmoBase +{ + float m_scale_x; + float m_scale_y; + float m_scale_z; + +public: + GLGizmoScale(); + + void render() const; + +protected: + virtual bool on_init(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmo_hpp_ + diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp new file mode 100644 index 000000000..b9eb118a9 --- /dev/null +++ b/xs/src/slic3r/GUI/GLTexture.cpp @@ -0,0 +1,138 @@ +#include "GLTexture.hpp" + +#include + +#include + +#include + +namespace Slic3r { +namespace GUI { + +GLTexture::GLTexture() + : m_id(0) + , m_width(0) + , m_height(0) + , m_source("") +{ +} + +GLTexture::~GLTexture() +{ + reset(); +} + +bool GLTexture::load_from_file(const std::string& filename) +{ + reset(); + + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) + { + reset(); + return false; + } + + m_width = image.GetWidth(); + m_height = image.GetHeight(); + int n_pixels = m_width * m_height; + + if (n_pixels <= 0) + { + reset(); + return false; + } + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + { + reset(); + return false; + } + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + // sends data to gpu + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, m_id); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_source = filename; + return true; +} + +void GLTexture::reset() +{ + if (m_id != 0) + ::glDeleteTextures(1, &m_id); + + m_id = 0; + m_width = 0; + m_height = 0; + m_source = ""; +} + +unsigned int GLTexture::get_id() const +{ + return m_id; +} + +int GLTexture::get_width() const +{ + return m_width; +} + +int GLTexture::get_height() const +{ + return m_height; +} + +const std::string& GLTexture::get_source() const +{ + return m_source; +} + +void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) +{ + ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + ::glDisable(GL_LIGHTING); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + ::glEnable(GL_TEXTURE_2D); + + ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); + + ::glBegin(GL_QUADS); + ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f); + ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f); + ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f); + ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f); + ::glEnd(); + + ::glBindTexture(GL_TEXTURE_2D, 0); + + ::glDisable(GL_TEXTURE_2D); + ::glDisable(GL_BLEND); + ::glEnable(GL_LIGHTING); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/xs/src/slic3r/GUI/GLTexture.hpp new file mode 100644 index 000000000..3e3bf83f7 --- /dev/null +++ b/xs/src/slic3r/GUI/GLTexture.hpp @@ -0,0 +1,36 @@ +#ifndef slic3r_GLTexture_hpp_ +#define slic3r_GLTexture_hpp_ + +#include + +namespace Slic3r { +namespace GUI { + + struct GLTexture + { + private: + unsigned int m_id; + int m_width; + int m_height; + std::string m_source; + + public: + GLTexture(); + ~GLTexture(); + + bool load_from_file(const std::string& filename); + void reset(); + + unsigned int get_id() const; + int get_width() const; + int get_height() const; + const std::string& get_source() const; + + static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); + }; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLTexture_hpp_ + diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c7f3670fc..426395ef2 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -410,6 +410,13 @@ enable_moving(canvas, enable) CODE: _3DScene::enable_moving((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_gizmos(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_gizmos((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void enable_shader(canvas, enable) SV *canvas;