From bd1e93d72d65878c7176fa20f472848233a19fb5 Mon Sep 17 00:00:00 2001 From: Matevz Tadel Date: Sat, 6 Nov 2021 00:21:44 -0700 Subject: [PATCH 1/2] [REve] Update to RenderCore release from github, deploy it as builtin tarball. - Move some RenderCore specific classes and all shaders into RenderCore source tree. - Implement all existing Three.js functionality with RenderCore. - In RenderCore, further improve on existing functionality: - Textured points - arbitrary size and shape. - Mesh lines - arbitrary thickness. - Object and sub-object picking through color buffer with fragment depth detection. - Camera handling like in TEve, with ability to pick rotation center. C++ / Server Changes - REveManager: Make RenderCore the default rendering engine. - Stream interpreter errors and show them in client log - Add flag fIsRCore to REveManager. - Make REvePointSet secondary-selectable. - Add parameter for projection depth in REveDataCollection JS / Client Changes - Remove error MessageBox popup during client updates. - EveManager.js: Check change bits in ImportSceneBinary when scene changes are streamed. - EveScene.js: Optimize visibility changes. - EveScene.js: pass eve_element instead of object3d in cases where it simplifies the code. - EveScene.js, GlViewer classes: Cleamup highlite/selection management so it can be abstracted for RCore. - Separate viewer-side selection processing for Three / RCore. Tutorials / Demos Changes - event_demo.C: secondary select points, jet title include pT. - Tune marker sizes, colors, visual parameters. --- builtins/rendercore/RenderCore.tar.gz | Bin 0 -> 96470 bytes cmake/modules/SearchInstalledSoftware.cmake | 11 + graf3d/eve7/inc/ROOT/REveCalo.hxx | 1 + graf3d/eve7/inc/ROOT/REveDataCollection.hxx | 8 +- .../inc/ROOT/REveDataProxyBuilderBase.hxx | 1 - graf3d/eve7/inc/ROOT/REveDigitSet.hxx | 2 +- graf3d/eve7/inc/ROOT/REveManager.hxx | 5 + graf3d/eve7/inc/ROOT/REvePointSet.hxx | 5 +- graf3d/eve7/inc/ROOT/REveRenderData.hxx | 4 + graf3d/eve7/inc/ROOT/REveSelection.hxx | 13 +- graf3d/eve7/src/REveCalo.cxx | 3 +- graf3d/eve7/src/REveDataProxyBuilderBase.cxx | 2 +- graf3d/eve7/src/REveDigitSet.cxx | 2 +- graf3d/eve7/src/REveManager.cxx | 38 +- graf3d/eve7/src/REvePointSet.cxx | 26 +- graf3d/eve7/src/REveRenderData.cxx | 18 + graf3d/eve7/src/REveSelection.cxx | 27 +- graf3d/eve7/src/REveStraightLineSet.cxx | 10 + tutorials/eve7/boxset.C | 18 +- tutorials/eve7/calorimeters.C | 9 +- tutorials/eve7/event_demo.C | 21 +- tutorials/eve7/lineset.C | 10 +- tutorials/eve7/points.C | 4 +- tutorials/eve7/projection_prescale.C | 2 +- ui5/eve7/controller/ClientLog.controller.js | 8 +- ui5/eve7/controller/EveTable.controller.js | 2 - ui5/eve7/controller/GL.controller.js | 4 +- ui5/eve7/controller/Main.controller.js | 14 - ui5/eve7/css/eve.css | 7 +- ui5/eve7/eve.mjs | 1 + ui5/eve7/lib/EveElements.js | 4 +- ui5/eve7/lib/EveElementsRCore.js | 1202 ++++++++++++++--- ui5/eve7/lib/EveManager.js | 64 +- ui5/eve7/lib/EveScene.js | 223 +-- ui5/eve7/lib/GlViewerJSRoot.js | 7 +- ui5/eve7/lib/GlViewerRCore.js | 716 ++++++---- ui5/eve7/lib/GlViewerThree.js | 2 + ui5/eve7/lib/RendeQuTor.js | 500 ------- ui5/eve7/lib/RenderCore.js | 9 + ui5/eve7/shaders/custom/copyDepth2RReve.frag | 36 - ui5/eve7/shaders/custom/copyDepth2RReve.vert | 20 - ui5/eve7/shaders/custom/highPassReve.frag | 37 - ui5/eve7/shaders/custom/highPassReve.vert | 23 - ui5/eve7/shaders/custom/lowPassReve.frag | 37 - ui5/eve7/shaders/custom/lowPassReve.vert | 23 - ui5/eve7/shaders/programs.json | 24 - ui5/eve7/textures/dot-32a.png | Bin 0 -> 5404 bytes ui5/eve7/textures/star5-32a.png | Bin 0 -> 5229 bytes ui5/eve7/view/Summary.view.xml | 2 +- 49 files changed, 1901 insertions(+), 1304 deletions(-) create mode 100644 builtins/rendercore/RenderCore.tar.gz delete mode 100644 ui5/eve7/lib/RendeQuTor.js create mode 100644 ui5/eve7/lib/RenderCore.js delete mode 100644 ui5/eve7/shaders/custom/copyDepth2RReve.frag delete mode 100644 ui5/eve7/shaders/custom/copyDepth2RReve.vert delete mode 100644 ui5/eve7/shaders/custom/highPassReve.frag delete mode 100644 ui5/eve7/shaders/custom/highPassReve.vert delete mode 100644 ui5/eve7/shaders/custom/lowPassReve.frag delete mode 100644 ui5/eve7/shaders/custom/lowPassReve.vert delete mode 100644 ui5/eve7/shaders/programs.json create mode 100644 ui5/eve7/textures/dot-32a.png create mode 100644 ui5/eve7/textures/star5-32a.png diff --git a/builtins/rendercore/RenderCore.tar.gz b/builtins/rendercore/RenderCore.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..2083c5708433a02e61c0dd563fda0d9800cf548c GIT binary patch literal 96470 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%0A(5q1bpb~In*R|a>zLl zshosJk+Ed-E1Ly={^1zf^NhX}oHmct<;G}b#d&4O&6VjTZmqM})XSA+sJ&}xndYRmW?Kv(76( zZN9TJIg@-H4iz&?c)t3mR{1J3bp5CnV4CM_+`ugyWFm_T=qAd>yGb|_I7h)Gm9X4Y ze_^R0inU1$0&2Z(S4hr94HZom4_A-GM6wnVoLLmol1dyiw5^hEA9anNY|QK!LHW*5 zJuyp?V3>VMCU1h&G=vu7S|V z@Bs`RD|cql_}i~64lX!F%;`y6`OqqTt9n8l%|i=P#1~ej;))3(=$+PEt(K&>{lF_d z>B=5>g_m6g9c9Sg@LZkpbQ;8~A_**eYNY(VtBK|mQP0ldfP5IJ0Jpp>#^bJ?n{`4C z3QXrZMOk7&CMJ>=FLOjPGCfs?BruG)UQFyJxi5((#Z(5JGTL1r{s1Xx<4MrsJT&Qt zP=j>M1}$9iVqi!$x~>08S#!F`5-+_2gQ{r zsOu|ih>={dw5fj4*gzE0c?Kpf>EBCva_2r(30a(hPkbOzfmlhfL$Wn5h85};<2oY& zXoB^Iu^lMI)iX+N7em^;WsUy^9XH-uE_hWoClZd=KW3YFqYk)p|H5Q8pK%Y%2bM40A=hW)Lw&ardug+*mRd`1#EEGQf}QxD>XP zWo;x&uZaTGEQk$hY5wWEXH9E8Ap>|JvPdi@$W?P6jvZ`K*B_)&w zRcQjtZ2`c7j`Rffim~@$Eze zs`ZAmH~Taj1zD(e?geH@ZIf3oRuEHAUG-R@^(KXR8SaNDOoC)MzSE^cPiY4G14O?S zdsbaODuMn!jIYBiF~E~tVZ-Aw!@BN$iG%mc)EnDf@K<&EX=-0v{qa|}`)ul7S>5y3 zwMn3j19oq-^6@jZ+`c*$^P|b?Iz4r&=EjxPbo%Mk%nfp8^RN@BhnW~ocaGohol0GB z=lFQ%^We>=cklL&rNiRh!QO|x!_$GC1t;RQ-H%6~2giFmZ*_84C;xQ3f4ZmB;o5iY z^mym+P4}*7mdvD+D?EZ(&hfBz{Plx-Ms}kYbZtZCQ=e>i$5ih)9(mx$qIh)_1 z{aE(w?eWe}gVWDPd$MyUr#q+nyRus+rvmX0`-kskU*3K`6rUouOE5fBoc7)%r8)KS zB!xe+h!(EsKICsLLin;&9UQ2nB)gNMPNpQmjWl671J=@=X;C+rJeaF7f2LwnOUG3E zy;GI?+Wg$kHACFc;)Bi3IYyHZjMMzJxJf+3*wo5Ah3+I`vr38>8{J%LwAICF52f{(nZ{J}c6)3rv;4rB+L&fnRmE5vYJi=BX4o?$*r@YG z#53hI-kzd1NuhM=FG*y*xD)G~Qhj9w>X-*MW|(sGLisy)r0(1!!RC&{qn2^X_}j)T zPJSxu41k(*9I6@uA6S`ZO4CH~m8Bemfh=hupywfLUr!hxP@QSun(`KCKJO%SYXowW zL9GY+w(R~i+cJ^cn2h6h5WJ5DS8Zes=W~YE?j#KM~`bp0V!)u zbhoqd7-EUqi+b~j24wXGJ?M2G(*sd^Q4b!`fUI8D0^SXML=&q1!ZtSgk84BJ_q0KJ z*!u%rp~$U)&)=&FUw6naX>frJrykECuJNe6v)Wk&Wg?Tm29O1BnU`0Rx)ardk4p8V zjs>;kPg$+TUslu#X;}}vdP-(}`>~M~eOfI5@THdma&=$;;-)PIz_n!!fhU%NC{(Ng zRAFW*NLv>NICe^D<($qA^>c|8Kv?R%0b(U1_s{}3fCye$`2K)^=AP{_8Zcut^1YH- zI@XSuc@A?$WPKOv6T72jc$=Uo1){<3ZUHDlOrn+Z%ZC!+M<(+_a_2HxnEW=#0`>fb zm1|X$r|nvR*doL77Lzn16@f`S&s#?>80D@OJ(8+G2%s7&%J8DC(pVbIEM)gl-CU5B zlreJAXfTP&=@~Wkm~yDEq}zsF<79rJDz(XwB&582C&p>P!dH$-X6@9BUD3;~oJG9* zE@wo>0qTdZ88wOqt(4|mZR9*5U4}Vycdk7Gvi5Qf^&~+@Yr1aT%5UGCu4#W@Hf9#I ztkClqyiuhlt&h3Pfx3D!f|XJy>87D51#|X~VUmTn)H7QDBOM)vRrhU^IjSkzFJUJS zr0#@0bF(6AnA3ZYvG^aAUySL!1 zUTF8<+_dD4hDET`%FF|;SzMXJ&}JR!d(Nz&Z};Bqd^$KC?0?vKkJ>YNT+7^d`v<2& z(;zeAVHiLl;&;&`gX=(OO_XULqM2YKr$8rHfNs{Se6*R`Nj;O~1=)A=?dt2685Fd( z9c|z}IQn5nnouzaz^42q61NcQC!yHy9PaLI*IroP16dUVZwh)Ho&GS`{rKVNNV{((*W*iOKv^5uBnOkl;&s3m|ATZp6@@*Z@KqJW~NsywvjWOOt^)wZTLfgRd$W zW5u4rbD6>99XxyD91OrCfC29s%8M3EHHFBfry97dPm8J<3n+~$s~3xUF{_9Ay2a<} zb|hR0d8aHh>QzFykVp;^((gK$rTpw;a1)KtZFsB_#vw#}mQfw3Ka*&t6v~{rUTc}w zgkUqmTzFu5x%6U%_hWbC>1A|fyBbS|$mmzoq@L>3Q<=&+Ff(5&mvMMzzStp+JJgwz zBmrHT{7arLM=Wb)`T~G&G%`D7x$}Z*2PWUT(9JJ;=A80Ls7-%k)NKiLtfN}pISF)A1bXnNKr3?A88bo4j z-vHv0!v;_;L0PkWZ9&tM+lrhPo@V4iX4=s!+k+aSS>>Y-E{}{na=9$0PR7r*8MI7a zMdBJkAuP7Pmns6=+YbV<+I$UV#>-_l`x({q9i31}ja^*R%;t3A)?Kw{;brszJjdr* z-t&B?Ps;hhs%JYvIIU%DgY8sP$)|fipME;tYh(CG$6W5TVOu$9Zk)#N59*ma2OBJO znN#pm6`;`FwvJkYO zF8S&VF$M5yd-tusaGNf_;>ND6_Id89LZw!v)!R4!lznSTK&RGGL@77AfUJ{-6Hw&B|e=i8TjoPM7yNrx?bZL-dLDlln; zm+mI`TX5*uVp4b2qdT~+5-5PqqT6tCA{LJ@GKund@!i4x(ctsor@c4t51=}(f=)*T zq9^lZpkB`+O5f_Mx!$Dd-ysT!)h@673a-rXme#*Q|Xy!B?;T$WMHR$Mp%@!e@D0^V{(jzTD%jvG>+rW$$fS9qs2?P*>#_ z!vIHEZ)K5DR;#E|VIA_O_0__h!g|I(*H>5PF0MZ)#=t342+MT>_Q7(>RE5>;SAo{; zSA_@Oz7+t9Vr8QY?#2ZJEwajL7PU`VrTkdZ=+1&xcS@SwDQ&l~(b5(Rn=5S#e6I^? z?U&X1R9Nj3)fRGUxJ`D_*^nfNW`qI4wMuASjK za}X`_{Mkx_JxXc>1U=#);;SMRfG@zj_)bYN8LHx4cpqgk=-&%V z8q@w_Zq%Ol&TS6A>T$HUficVh-$w5AJ2Etc7W>)ejrzED5IRB#<|6Y_hYg*UopY-3u@RifQpC zzLi&8n@-NjY#gFTG0%*ryJHv*9Rc%|3m8mbKdEjrGxNnfnv8VD8-52_#XKcnQ`;eX zs>6BXo5d9O^_4Se{{7!s1aFzxk^$$~?fn@2?dDA#l;0Fqan#(v0AvH2m~a3PCIJ4d zzN+;a6kY!;J13t2@k_o%IoDtE8A|z9NidBv2)Y(DB{JHnjb{;(-mE!RlyASVpy2r0 zqDvk<v=r#v(@FaCSNn#s6DU2Gt-@|hUi&a zzV?~wND-PLM1Q*t9Doqt#Z&=^L&**DO@qJxy{7Y%Tz;hA#iS>m)Q6lrng!elvw${# z;R^mv<=Rzb+8`jsmEBDZe)GZijLw=i+`|63@{T}Lh?4~v$URy(LAJu)iutVG1hZWF2O3y42AX=e|SLt zRL;Bz!|39t!OCM|S=UpvXxNX!ch!G367Qi@>Q+fj6)K+c z;(;kn0HI3YR#6G}{8WdLII~IoqTv!Nl4NiQ2Bhufi3Z6VNQ`PkBLZ{6;bf3Rh|~+# zvO|%2yIPU}k#NfQb$Ox+q^&!3Hk>+}P8(aYJd3E5mz2)7r8N!1QToY>Y%0wLmJB+T zTL@PO=?P-LO~{s|tc9PIaf^D4%ql zuXts}rHYiE2x~)21raV@qC-{Q=z_E?r>$Fz#!ruphU(E#G4kzm3pS<*y0-2rm+wfq zBr4rkt^`6VC`78`MG6}U3Nh<=F$)Tj>UfbNMXC_9t{1auAyVC9a!m^{>v}PZ5vinz zyor}wF=AG&5@0qiY@}1%$hfePj<=C{VI$q*M&^Z$biIvO0y&@s&l1Q}?PpgL>n_U} zlN;K|k?86XGmum>slL^RqASlu(L1qQ8H#`5_pK~=%>hhW*Yp<7-N`VR1W8@gL5!Sa z0i@Az6K4v~>y{SFC@mIFWqj=Fc5%rLm6VIf!s1;je(wVAl%U`N+$q81YT;y6D0l#O zE0dzT94Wemc$hcQWaxiwVdL7XR%x%Au0-c11>mvE{MvWN*S^#I>T#P-I>{&P+Fa^E=utQ5Oy zrMSVB;sdS}H@H%K&@06S2XN#{{|~rQe07s=d@IGSS}88~sFu z=lsvy=Xf?Ww`d;AZGUe6*ysE=*ymWj1b^&w{@CgKvD5iur&DpKwf=9OX`&H9}VC zbuVTQJWzJ`SH1lHs;Bl>zJukILQ-By&!3QJb7kBW)WPw!a#_GFq)d@QEVfd*=k^I` zM90PxJ86nKsFfrycH0{0K-BbTppZ6{XQU(8vC5<0AxwoaJN6wa?Pbi4euprn#%EKv z%G6Y$;!|EcHpK}bREgaxD&dI_O5YfVKxsWdZ!+^vH}@Dc!Cf3bLSW7}74Jq!-(o`R9sUN*oFr>w-?!V>ole78vX zBZuHoU)N5&HT>SLeVm3hF(I$xB!r8V3W)e|TMKTZ6v8x%W@@6H)=J~0kNV_v>DT=E+ER++KJ%i1!( zo{}&F6;s8_^=qAp?RE#wPHcuJbj3K-QIvvPd_N6dWfE)7Fg%S<{yGnma0K3GZ1y-< zC&4J1>zH!r7zIvHKK`(3RACV{&b5@tFWcfwA*ZDv*V`nxaaT9n@%6Qdhdl~!PqHAf zB0yv6E-32O^V16K3BE`BtPPWF0p1*&noxcw;iz^IUIM2pTyCI0qMT-JibSvQDtcpA zWAeI|>&iBdooqU@94PAlw!J#3-3x~f8U9%Lm}SZ;{`VDDd8YK#@_$p0hT5}LDFRAU z@jK8tGM&FnY?*zXw}~mwl)i|{%9~uf!*o}_pvRcWUTXO2eWl?me;2^^>@B5E&J9?R z$?sF%EU0E$2funBhLFyvYW(NDgf-dri_#jQB0kjx*qG#2UwcqYcD|0aX80+XUkx~$ zxr>kgCIFAyPh*z(L=qD}xyu5$3aLj_;Ihm%yExNC)eI}7+tQkV3(U69S6{xv*h(iN zt=?{6ZTeNs<|G@KhcE&Y)963@vR+G*Kg5s%&A`Bv0(-1jSy zPuF=0`e|T`2U)nEJHq4>nRAxz&(tFMrX)LI(235TZ%etV_8{8m7Yr2 z5wwRs4Plz2;vZ2sWp7S&ewETV+Mc)FYQc)WEF9@mg7mVDOb z$;@-7@_{YdYM=d(nR6d9GqqfPvMYM7TFSav{J&GJlPV*@-+r~C*QvY6Y}&|o+*BgH z>6W`lSvMDr<@{Sx39F!k{hGH9QOisZ$~Rp>gQ=px{OT^QvTdVFye{rz4f&MHYWL_o41JueEz6XYc)rZ?DxlnKC!mmeC&GF-*EC*(T;c`4Jp@a4EDQ(M^IEUo)nmz0wt6%tp1zLAcQ6 z-phX0kX?U^rf|(x#l;9&T#OY--<54`?t0wtG-a$jlZS>hdCyWetEkV~ef$8jwwakF z-?$tj*7hUEm^Ex{FZp(r#>5(C>Y}G*ThD87MgiQae?3?mHWsgvfZw0zfs~FW z!8CMn91PuDj@yhdKa@jpAtsF(j&T4cw^JJ7lj}In#wV1Y8)SiO-Z9y{n*=r+5GXYQ ztxk7c??fljbZT|=auQsnF5IudWKN3XuPz!Rt0kR!YV8J0frP>f0oCK1RGpSeqiX^8 z`HZ#&MUDPn1k2h66tnvi>?F{z#A zZqpACWWIi&fHVD&^6n}70U)r7@=%>#0B+Jc1y$Eb{l_!4#LV&*g;%Y zoDRyR=E*ws{YSDc1J|f`lT>FpfC0o$FPyMp>p1t^MX#r2! zQ3iQCkHX`+Ds>cJHj?-! z+l4|>$lb(4h@m$k-+~+jd^_LXBiB5$Q}Ql)Y$LTKN$fq?6O)7i-_E!9Xt`l;5)0&q zvy)t2j^wtYPcKjGCbo;(ejLGu3#m1H6sd;am`+14k%mu5A!Z%$ZgrUPU`g88c84b> z3ivxA!=bpy|y=46?~BNd{%ed&+GQ!rbN!Jg&#K z?a$YB$2zzq4RW@{C*}ql{X(69m_+#Q(o=>i)v7|?HjJPNClqZ zGTZ8hvxo5a$1Y^*4;NM*7S!abrjI#IHorunbTRKW3YqCCe_HNp?<;4Z@P*q`nPoHI zd0g3ip7xNzk9(~3zqV=-Eg>-v^s4KCQLEnwDxCKGjh zx$ubylkKh3tvj#X;8Or*)-fj^Xj`5U=N?+&Ytw3NW$oR;&gqKr)p#GzlVCooTKoOS zPscl--ZB73<9K>iwffNy9}nNJ$s zkm13y4Hea5bU>Z}%_^hEp?b7^y05~NXY>=rfA(jVgxe>65 z#D&Q6P;Cc<-28kPQ0}{sxx2lGf#hxvX1w>guv#7{mu2>7CE<1a)fy=6*Na1ow;VrS zQu*3i2Lg;QtA!(=bcQaa8N8lhwe9ksd6`byt(~}kc_Oj=AE?*Dd zcE)<<5g4Sie8R;XUGiG;)n}_YLb!GK>BB%|+gzwaQda1ZwJ6~e*M!OmgH_}ay4etUmz zpt;TZmE$ev9k!DXA3vV{FgW>P2cE5GV`q(uP?1a#MEF^oU8Z$B8$46zc<1osXyl`V^OzUuzr$tk?H{MOfSk>$1o%m8r_9oqkRXpgvA!MzaP?!SBY>11#4Vduz< zI$xl@wN8%qc0V1!W1dva@%k&QcL)#p9@veURn}YOhdmI71r4D#Ue*$-!HsWlou;My zQyyE(im#mLm===Cb?8P2p;+ksH?qXl8;$&(lhF7hwur`|@$7490*8)sT#HdR zbo|O%jI^QakkxYN8s&TG%%K!=f`-Sfs0BcQKXO2QEMxNN^m@a6E4%@AP8_>8Qv5eVy z;9BwGpe+}Hmj1FX$Qg@w2~eiZdEq-}>@8o0uT|Svo=1Sz6yBs~!O>u~)wK%#bw62el%u9)6Wo8Rl?`=I>PY3(6ZPI=J}Yx@$iE~=Dq|W$ z+$svX_VMDs!eQ3>CA>@P45@}Lq~oKJ&|{GxLnjg>#4cO;JG@XGo~8J%tGc610VdiI-W4v(35FlV z*YyV95Dev|LsPu7vAMa~?W}uh<*e3pJu4cn7|WqTL0iLG;a}$%na08pV+z)DSU+LR z3k7xCp$$Bu`a^hIZ#?9aoau5QZV7OR&$zg` zl1(`gQDs>hbUwxWvjGE6DlYZ#)H1OVastZ21~;;VtG(N86bLYbegpT2d6^|quN)45 zTGu6-g}Xx4FjoUOEj4Cb9rN6~mg=5!>)VJ0s^kNr)Cq|733SC1MMc+t?TE@|sj>2& zY^kxJ>ZN9>g(G)~cBvIdsHxF35~?kz0G$oDm;vuBO*24zrAT7`lgQ=#>7m^NM$sqi zgo3*5wDMG4Q3LAqg^lx5c1D5ScH4P)FRHG5+;^nRafEvoDcX4bie%;=>+0`fU0twZ zw)pJyPb4hG`Q9F9!h5Ne*I6Mduf1i#~0(g&^8MRD*Yg zMOk&gvWuuu(g0aEja|gdU7y88cHFkN6wrp*TFeBOYyV5No1{H<@2!6!SKJCMJtSI+ zg05G1ZK!qvVGPI7WF)ez!}?jAM$!c1JbfVj0OKrR`FryT#Uj>F#r8>X?9>$bKRUrc zNu>pmH_>TAtdfq29u}q1g$`6={0owKn$53w=NO!u$IDQt8R=iYoKJ_$+fs~+K~F_$ zr!HhIRlQkz)}fd=8WmQl47NrtHi>rU=ZaWWZIC~{~& zg}}qMT*rb>e~iML52fv?)685M#t#~l5W~QCqJKp|P4k$xGtXl7Qp3X7J|y&k#No&3 z4!kq)s>VOr%7Cdq>-?Xs%Aa-mXDj`SPXA)1>txfwB#wXCff+?V)C81bWrfW~GU z0s?Dw5?u?Cf%~Y_#MKCpjQEudE6tOg?r}eErpcV$y?^- zOaEL}oOeK_eJ-tVm#^@pv;ynEU)g$+uVHeTCSK$-pW5E?u3YkatJ`JV+9-O9D`GZ# zae&r1uX8VDF|S>|@Oiy=c>(U*uV=j9v0rC1k8~*xyx!g03+2SR)twGT$$ld_TgJdtfWR1$Ou|xK1?(Laf zB!9J`s3OnzwCFDSrER-~nd7>g2DazA)c>68c1$mP${Usv>bBkZV!J|&+O|J>hFmH9 zM++*OpDt*5J&b3Z4IXbgQINOYl_86X=Nij*)zjCXog0ICh0Bd1%X->@{1!dUcl2{X zNAHR|diPJ+(JuuZy)W+Q{XbPlwLavZrYf6HQ>~Re`7|}!%XWV2`7~42Xmob`MXk%_ z29{+#?ZAGk-sU^|xuCOm#htzTr|s;Qg3jI-clQ3DuCr_x;Gejr0H3-Hjeo|;n_6Kz z><(id?`vuzaXi@x43EEoA5YY=+}6tzk|Jj|GK}o8C_N=RyWqDAFnSC_+uJ)&_ie)` zyNBW!M4z(b7VK>wB99VSY-j2XL1Ii*OlDaItf%ZOgS?$b;V~W6+i72%B4v3qU1Y|% zr|h2syY006n4aaf{=RTst{IUG?P0y|L4=m_FBy$Za<=*IuPJ1w;KOU@oki!ZLKGy1v)SbC zL!qUubv>VCkt%|3dD+G>$5z%Fh2ZWRr=hbg&7b92hmnnvuuR`#%i3H*Wo&)}-HV^$ zTedrkm=7i8LhB`?_NSw_JEwbtHy=M8zCB^4O^zE`)#)jPJesyjh&gu0nzo*p!$|1N z83`KOF-D%8zs~nZ?jATF*0w#x^4x3SS|xK#?ewwWNY_t7`QG(Q*XLi%->`W8hMxHw zp0_;XU~vzosR^t_ixL^Ezv>Bhb=emPj`W5_B7(1|l?VBZ&dFt0D-XD6t86T*4Q1t# zTriOJB2I50eq79I4na%CRT$pI?g&qhpG85CKxE zj}}G5!2NMOV?HDRpuw0&hVWP%)UM@QV!RcaHCNV_2wKGJlTReqNaP)dmm+JifE-6* z5+uX%ohu_XB`hZ`XAb28g8*rzu9Ne?aq7xAAR%{t@e?t85M8j*C!k1ZMJbWX1-LVZ zId3KATI6*0GMb1r!#K}Kva*Nw%2!`c*WJDsbUxR6>^}5Ati}!?hsa$=`WPm`*Dw*Q zVn$H>BoDzRL!Z$l_u{I<(26w}Nq8-YcO`ey{t&ut7SErPZ z2m#HFf{8^IU!o5plhJt3irz}pLt>Izw#$Z~Rg@kEhu~KgjG}qkKn}*BDKZ7VLly!E z-Jsg+9+{BUgvmr=p<{q*{w1cJI4&`rrvYCNvACwE z_EBYI;^mm_mt0630(vxNUM4@T_s zAezd1G=K)Yd={R~%_Grb1kal_VC6_8YrlI)Mh9C^0Ru6&p`vc z&l~Q>^U0_NuMJ*`HMgcE5)dF8hqY_bm+cx{I-MgkNssZ`)KFr|2%spSZpCcVi(NJI zD`fSxjsM zo1W3vxkT6CH*eMg!5WQ{$1Hi}<6ZFqV|C6zsOKz^`Qm16td}^P1F>%o{7o?{%z^YQ z7W_HE*$|&IQI1&ECn(}4Sa5RQ5R6)%GYxLi(3-hm)Nz9rFoTtpbz_>#;cb2rtHT>o zM<@`Lnmv&WOr@tyO6NJcuneNc%TtmP+OifTB2Lymj9+~~K`hmdJbJa#{n}lOoqHSG-s>DM*-d!oK97B;3JOG? zTVyZ4Q}`GIhIOVzv0+4lEEcI7Sew9`IKT&ZBQW zh5hxE6BFuY$UtRLF z=c(V+wN4Ie{`y|=Sm+CDrBs(WZFU$CDYwHGo_q5fqAqpa8@X_hF6t`*1>yCA1dQ@e z{d(rpdt2w^lywF2sYR7Dkh%G5U9Vi+_;LcPa^qjBHok<|s@!;i@G`@~<(Hus7h$F& zN{Z?G?Ym-U6kkJt>M*Gs#U`Uu60l`NWWJ~n*2dS8zV|+#Mq-nF%_Hm$f=tM@i+QHP z?Ac-KPu_R}*JGj%UGIdZ86pA$Fo7A5o0B&XB*XJ?#>pG!$%AXBs4>aQ;zbV**roVO z0_=#6fP2F8iz@OAkKv2~JuDTN)8H%v+ev8fmc^;8+NZizEMz>71y~f;{!gE1;BvUA z0kv_#<5^wBgAc2wwOpsX{mztIUZAw2_$G&x;V2JUteOWgtExsVSI0umQ|D=d$2ez} zbTDc2j6Sl|wl5ZP+$t(?v+Zda8o8`7Z#dsAu>tycGZh#LY{`>Uy9&xckeRXHF(z9W z`i3Nn@*F}J6tg$@?;Q%q**Bj4uzylJ*?a$C@9?xHe!ks1I^NseIi*1S3p;2B#XO3J zzeMyZl1I*a7lFU&L$nU-)xJK62Cs}AD(X4I=E`7(r@?P?R+{o z9q?M-=3BZ%OF(?-EHNOlb%=r$cfldO+Fp28ztRMNO%(+zf9wsDUKF;18>*PkOkp=vGdl-Q|tV4{I(rNcz9T~KLVO*DgG4k%&b7f zTEy}=Qc_T+Z|N-chzDJHSrnsMc@S`kr-1q*NNSO$Er73b!I-5tjylUtVCwLe>1!*=r)>fug066=84p+Dzho z!`Cii>RxHCFSeSS(!?#kvXo;=N~ZA@m09-tyONcP-rtqkiz2Ad;N&By{A1p~wHUgr zIu@|5meN3GKIY}TS{m<4bG!8*f{nLF)U}G^(- z?)y_asN5I=WB^N03ESbARXk{Y5cA)h4?0@PjXuVyrY_?8yLns$L zi%|JS%L!$`Y8hq0V=FqFJ8@1bbM5UBa=*d~Vz7{M(!y7-w5*(xl2$>p-m;QhMp~uq zbvP@&+E}-RBkgPR%U&X$#Ea-IUbXUbSKG8u?V@kFH(wM1BETOKhvR)+{L( zbVb^geVemTuvi-wXO0^XrL)-A9loGEq-T!wdPFB3NRRBV3oYMuS_Qp*RvKUSuu#a{ zkvH#dr~Tx)V{hK8P8KVPd2Ea+TU|5^dQ_{=b=n#fO0y48yD(whtIjFs1-BRGT@~;XQlJiBOM~lxoyVTN7ZB( zW&2kA@*>9`bqs#y4o+@Q-Kgd6i1_A!y?(+{b5HSPG`r?7WN&Ykf5^}&#j_x=A6;$LjBrvUuiKdldNb|zHO^!$i4v$aTTjV!M58SAxAQs**I|s z?jyi8xbxn?7{c?10eV9hnC+^0?|KENj=c5ma7F zL9sYy4(ejjg}+U)qCBs`{F)bK2GEzHOuFy_M2X=?s+_&meh-f%wFSE#qxI4ILVF|_ zFoTt1n4OBs=*c({Xf{w%&O#m8$h=F77QReM_u(Rb*c zB>J`u7)@d@aA9I5d$}!iopBk0x|SHpO2sBsiq*~$3-WrR%f-K{(+k^Y$FLdRUQ&9q znr=F3eb)C8lxiX+;ux7&s40^w(18?G6;27EDfHfy_>Q=diy8MN{ zReHX^RQJI^NX2UXBmos`nWt|n>R1WmhSy7COp~2m>0W(R>ori4jnA@kl33zbZMD`p zr~8PhPU(siyc&Crav5dO0B5VjRuT_p#}IUh@RqJ83Q0AI@mTwN(VD#GNJ=hT)u+pS zK736VeTcsx#1Se1O>og#g(HMi{_Ix8IgXFF=oBZat(#{EqT|vW*?3< za`rNNXk>+VlQ_tFUA4I_-BJ@)>XZ_?NPFm)u3?{7RE{#*VMZ_Xdd=L2X*P=|0oWbv z-Mb2fB6M9*M(t{?*T06ra2$>l_qL6*iP#sBvT7d+!a9)1L&31cxgbJTr<3&nt}y(l zp3Sx1(Up&1M=z~T7>8RROpugxieoZj2-^+5>M83@^@DGLovNQ<#n=+Ev_Dsz~hG?xsIpNFzwY1zYXK? z@-iAmAw8XrcYv9{nU;1hxWgK+MB>QO5s6&#`mm5m(bcrhX);UVET*Us0*w$D4a0%D zPPu4$X<7l0oVmdsQs#3*O4zeRED8brs;!Ad1NZ4tB?@&wM;-Ab*(SHPI&?Tyi(ZX6 z;7!r6CIt7(+Dntol?V`EDa)ehZ~|V?psS5!EujA~bDes`*+0|O0ef&MdcQXv4eHn- zueanm^fcLF#z z*OV)!7vunm#L`?RR$5ylW@=dd3~fS$G-z}I+JwoWUcF2xT8-|JQlfyh!rGtz{O4MS z(I`3_pQCe!&~@NBZg4{o1;-q&LX&u&5vR`izwuZ~s)DK~zcx_=$%K-U>flDwgX2V|54ORX5lzD^$$74fx2FJVyMzl+ zHVlgTiCJ<7dAXN=VVOd#@=n~6fba+4g)#O67n*(HQ|K#IZ3+Uly9IN@14GP_5eds(EsJZxh0rttxTA}AFjpEJ8XG|yqEMB@(pYuj;C#AW z^OkG5#1<%M2CP~Kkt$JnOjSWi8+rL96c8|Od+Risj)J~j)>iR=xJN;niBdlk1%4)) zdYCAdOf;o9A@xX1T(O1tzJ^mcls2hYaM1Ln_yhbkhaX}a)Niofw9<*d{GGtOo}!g3 z0N85)7k&xomU>OPvw;5XDgL{pvn0*jPx1*a18cBb9MH@jAmyhpvj>e@dNzQW9h{BD zxQL(6MGY~<&tmmC_tM&By0sra&`$sbQ zfQZ_zeF>9zcP^x#=t;PTIZa(ge0xhQ6r1^oR%Plktp{sL+seoufoj%SdQL2uoY6wy zEcPt+lQg%P-qsr})>RgI1$uNciD$Am+sbH@QKMbKhoL|x@`e8RDG6pf37DZ+yc6$oSw#2dMa>Q(9eKcie*1>3RmuDEKNjWFkqrn z=h|f*3*^vtsL7B=cmi?K0*7BINJy&K^2fxTw05Oq5IQhMQ)kyIx0{#`qKaMh=iAp5 zL=8W(=6lsZ%AsF>XY$tVhm3UPbbuL}8O=x@yObiW%_GOjEchIHh?@rb2}dz&8P1MX z!#W@eU}87wt3SZaoFf^#+(0L162K=HX;eem-dKV$WIg`X4r^e#lU zoc0(o9BnX4J!Q7xI0tq81zNU@>HLDnZ{}7G2;oe3&c&~WoQAxVEd^qg z3S70fyT2X0-MH<$DzC$owQl8hpruYjTK7#eV|bff<)<#DLe4bY8}hC0R=53P9fLi> zWgUD%Cp`m0e5=2;wb6zny2Q=yTG^S3ILS)%tP+t6pnu-P*#Xm{(`vuyuJ^axGV6FW zKvRU3aa-DemU^A_-V2x_^`5Ljx`PXIQ?|PR<`%$Q?{9QB`y^aR%qK{TMpJL1;J_w< zfUR%{_YW96LEmn7(>HjcbKPFAyHzxLEWc>@SP}2|wYJu`+FKh)8&50iR*|HjAsoHU zey`JAX9L&j_BS`%TfWh=Tg{3Fuit&q?)ORuPgHpV>48DS-PD2^E*~j)bej$vWChK^ zuFT7lzGo|5@Fyy)2BY&9KyrH-oy&s|PsM>zAaaK3V?xhvSn>Y(1{SBzLB)&Pn57gx;&u;0hkfCY^R=MQ>1*XC~(JlTD$- z&ji5mly>Q%iSIw3{-RDHwKpk!^Q(U>NO(I}_icPLodod+9ogk2j)_3E?m90uVej`h zdEuYDudI;#f@21k(FFGoP&pU+eXNm#J!V}#pe0haRThQO2eoy(wsLWoh3N{~z6Gtu z8vffL3&7dP!7py`$oUpBhHVLT<^T2bhl3xoYzE%VAq2d~jeRSgig6s>!P$36x0v&> zZ1i!yD0o7#y42zP+mDBP=nPz+1xX5a9CAuImV@|12sbbw^0C;{O|!KF+@!BGq|ZVV zFqnnY`pOS`J8xIeF8>Vfc0z=?>8S4X4~e145EhP9QFq5Jhq>|`2ef|}P4vD0gO59J z_YdFm+tI2}(@;$j0Gh+w&0(7Qnh8}je<}*h>MMpzB-#YJ?F}Bxf=Uppr9y2JpKyq-CM}i zR@NZR#?V4gDdyBJaR$aUkF*47MOk>gCo({*BrGvjHTmz^rSO0v;Z(&i0Y0(@_!i4z z0Kr9^WIg~>Yxo{cF-Nv(qUF+EVQEOGDA%-QFLnv7b@jxFNOG`q0&@I8QeIvs#52lv ze`|en{LOuk`uyR2Bn>@ntSqImQF`GoNe2uM1HO9F8dQmq-$Tci#@y=5QxiG58 zERz@_ya>WXDe^P~&23^6iI|4E34+u%*ukZ~Fv-%^;inI8_KpW1-wl4;Io{uSbFeo! z*gJfG`orLOZ};Qz+mqHvpx2Q7u;oBoV)v}=>ozal)X(+Q=@AlN1w%b8S6*vsp;#d` zu83d}c~hbfaXqwDI~PVGeHU_%MOm~t*n7XT`*~nUbo3Ff?Q+7%_;>cMEC;9zf_@Jn z@YSU9Z+A|2WFayjrul&ygsv*nFePuq|WhTBdU+gjie_JQJJAzx^?d+a@JeDPg{uVtYNB(v_ zC9j$6(0E|*YY=8 zsn7*lrk_RU^4c=@sN&}$>zyV=z2Q6i>@2F+ z@z?7vs@L_`>n*C+^VjPys@M0|TVGUf-Cu8GQN0a+z0F1SHvRRs7S-GG*L$(3-iv~9 zU&t_>Jjd|U?t)Nn0uaa~QJ-E^%3Rk#dEJ<4RFz#~Xl$j1yIH2}p#e&Yda~y0Pd9ieMd5p<^T7x# zio8V)?{8{lva-cq_ah$A!%aA-4a!cr(-%Q#08(_8`F?$O>tVj2Pb5bWc~5 zl1VPSR&iLMi`PKdh_TY_xe7$=Sd=$NZRe5E+S#w}83&9n)35z+Cz&zHipGPKRgP6? zaM88kC`RGJX94f1=MueicnMqNV8K_Kutjq^_vYSk93=E0kmc75#YQ+cx?-a$cogp^ z?3;Pix{wB- zp2+vH^6(3)K?A;elX!aNZKT&NX~bU(+VE9_(gjuCL|3A%jsAkRifcogzUr57^DAFN zUI@#VAH{S1N>;9d-9V*9=}V2~hFqJ_{NGw>nD?hVEM*@S3E43ma}y;g33MVP%J(+cm4$G`*ww?t zEY=3BGAc6fyM3_%hHw)*r=O1Zw)6J3eJo#Jc^6H>+KC_`de_$K|L_0(|B7<&{@?%m z|7)z|P-p>vH*0;02hgSq^3>GFPsh7^gZ;M)n~LYjFs$vr^+2~c7+=PKX8P33`-6{f zb`A!6hr1u)M5&~m_mlV{nAG;B!x#+ml^fcS4Sm{wOD2|1Nh3${wYB}XwKc4QX3tuu zq&47OYE1mMo4D2zofZc&nKsQAH@9G zJ3QTgx4&1`!pU8lh1a$HQ8>+_%P33=Cu*-NTT!gNcer=Fb1J5fn6U3of7&S(4e!bB z0h^hV_%gc*gnU@o)cO{a3cJU9qBTLZw>zH~35H#`Y=BpjgFAR<_j4E|m8b5Dwy)LC zdppOKn_aFwIiKL9e(zWa+An2cNVfACZ%dkz`!2EhMAH_|q)H+Qnv`6! z@GzmI{loe7MTl>O{}?0@#&VO3+Hp9HlTlg~nNC{{X_3jhOrYql%T{Y-JXts!JMDf^ zqeOwF8kPPXRY$zjma?Gyz@h|&gYYUC-qp+)5Kc=|ezO#hH=hM6LaR7j9+QhZfea|_ z3vubTB{H23A!8pDlr;Tw4LwPAmdVe?46a*WS9jz%M|v+Dg9w z#mWm$x8HN|tGe<%Y{_$4-}JVAQfa|+5J)R8brOelwH;V*mPnHZ!gn4-@L(^(T-T&6k2|e7e#+{kfrYS;*9R#8iwT%c2Ltki zRRyUQ?K!cjB?hL(b>_SI7^rAw}|l@ObZJ@A${P+>#`)QNryw6o0>lqs3d9u8Hy{w{M>WbdOn(LH{KF z`0asYS?Fv?*+>B8er&%SVcdXKF*0_Qer7?JtKiPNQ-M8g@)tKJhhzsLsnlrCU%CCR zB-h)$lilO}BQRf-H25}5hecpVmJ?ZNs+Te=dRXc@Ye0FZogeih*QPKh_ZPIeF&;qUZ-?r*_EEUF+l7@`NNKK}-`upf>IISJcCu!JN zw&|1Iox{P~eIdWl^!e^!=Y2`zC&K_@KqestLxg7vmu+zG?fX64?;PyE|KW51p-f8} z+#6kmxT%>y=!cpNj=EHXyWpa}|89SG$0r?GTe~p-qRVI)6bMLvGwY0yJBRQ0#AfJt z=kPxkQE>-?L)VUj=`XeV+KV=rURPhV8@2!U|Nei(ig20LPNGp*ytpZ&2wN?^Cf>L9 zdbz-^YkCG#V3Qj)i%;rI6SRq1;;7E7qGp}J_t8kwInSeM@y8TIRnqqaA7wpm9df)+ zNq`FR31ad)kbxlUz5_u90_!_2jaKw;$Z!P7iuDZzJVIb4mj(sBQ+CCZ3WdGXZN-xc zh25#LVkhXvEjC|qlb{%5$QAt?GIGqc;-st;(7#i%DzjzOyL{4OtS)V_WpvBE+-z|} zlNQVOM<;{R{SRegN))M`M%Q79L264YZ02S{K~EK@*{J=kj4u&YgXoI-xk4e|JRZH@ zf}9&w$a%c?wsajGEk68|Hp2JEdwXR9{e2RK)5XxF_2|vPr?Me_Gnt2#k7W`ya!?$~7;sQ<_)Io#Vz{g1qqxn`_Lw`v&wFG+SS}dL8W??|j%hJ>K6Pye%95 zBMLm3M8n$Xb}{tzJoL{Mq2Dcr{vr?kOGW7S3!$&~77C93Lj_0qVcQ=oY9U9Tc=d&>#jjo$BE;P(2z z3xTUnZvUYIR~^s(!vyZ;w-mVRoo^{{*Vn(PzHfDr0Z|6Or?oY+<9q*lz41_^-9PpO#4l=Ae97v} z!V(iJPRj4_R7JXAl$@kk+|CbSsvWa5mpd=mtfEwGEU8p%{I(`kW$b)n6KeMxm{1qn z+c%e*x*JbvLfxvu3!6*wLZzd)!~HVNN#s_i@Ww(4Z!JaPO+#VC60s?w;EQiCo7P|a z-fbf7--k`4^LsEtzxW5+M0(%U2)+4DZ6cj-Y7<#sC>UNmRP_E~6ZuEkL^|y%5_fA! zEA;RE*su5dIQHxP9*+I`|KMZ4^>2FYxBg9!{no$fvERnxW4}&&DUrMNhsga+MXr9- zVmFw64f1!uxEJRTi@g2#L4|HpVKR4BhKa5|3zBe}QCS()6JH3Zy{=^tuSv-9-ElM- z)g91{7S-IFgxBIvO}`=r)`j!`v1HZ=&fazG;oZ zuhB3(GC6<$JBV5Zc0LmLBlg%Z0Kru$t&X$D;p)d9|E zy$o@iruo)QG{U<@S!+0f*Jpl$Z={bYGsZ{!SfL*Fy*Py zq6!8jGirvWon1Z14jN;)OMqR$~DtQ22s;0u$6KXVS7_u+Bn}m8`RyytWzpa>bO@B9I7K|Vg z6Xdj4q7cQ7FhYQw8c!w>LmbQDY|3~bUG#*RM%7Yv)3FEe;2(>DCG$)SwR3jX`4FT!R`YVIBA5SA#frmfA{HzINXHnY{GGd{0)C!P_UFPG}V>A z;cpK*-BFQchw=4HVD+QuN_cw+%GD&T<7+PSuYPW!*ET!q-;y6~`~Ue!6{VDv?|5^e zGhONo?c(t1Te-%|H>_7+zOC3(@O=``XL=5j(%E}kwmnp3T;K>v>%f=EJk93UyFwk2 z52wgQDid;Gh)>u3@69oaLd$@F9Qm2m(gT$48au=8PL4;H<3D{ zfNApUik{4Yo7qS3c2aPiI#Dvu&beGwk798_1&@{lPeLXFwPN1Y5gc{ zo<>SgJ^Fe_ZP$JH3@2fb)b#@>xp`P=7$bZjvuTHYubU~`uXB|E-U?z_r8)kLwbC5j zmcOC3!j{&Go1&~jB_y%6jszaP#z@ABj`%7%q&m;nc52^K`i)xaXY+Ji$D~_X1s>9XD)p?DU-?KJieVbkRH(dr~Q&TJgaa4i~)Gr<{c$ti8gDdO)v{O!o}j!YMS?@5)G_!nuFXi$u% zAVf4KPmZ+@JNVm`>0O!L{as=0!w&xTWO`4g_kLGc`><;kaL(@s>%PRUFR|-Otk-4w zx=dgHU19CR4*qV)^bMK5@w>v>haLRgly0q6XFur89+~Ze(y)5D3#_S z&a(L0$)`kBWD;IxP60?_8}bA!vrdu3n8{~xngM#pVOoSBM~wj42s)_QMF1NMHI;26 zXrpF?0TzY;kfOiZwF*lSt?HHB1G`8nC{vtU}s zA4mIQchuQxi;UK~j`%0g;x-&TtauK%mg7q~rm1O5cHw!g-qk=wg@*W8Ypr_*J7AEB zVHb~etti*9tGkgFdxl>^HXQX_i-b|LX!11~F&X_CwQLIoJbcA^qRj;IjVnqmon%W7 zn=@v7xR|{U+J#mvL>x4%xw>db?eSzo$zyST^#|hoYRXVEzG9SPN>0(Lk}n78tWAHR zOI`-B^cAwPPm0bP6Mo1D4n$r9m`FBdXmotVD2GRC{i>~8IwXrUFO zjr^0cr!`EsB^!8pF8R=OUW-eo{}p=A8bogLG>6mAG$tkRKc3Wp$Tq|AF~q*~cm2}p z`K8tI;!ZvtGno!vKCXW_zdap9`DS{k#NBV#MQCxGA@i6{CYIv|>m`X~(@m{k{dWDt zCbv00kLf80SWiD;(KNl3OXzRcN73N6!Q?UhlZ%6=i*ivny)+8O2;_iSN zlklsZPqVWkwMl5{G<_mO$2;`&E1rGO)2a>gk|}?^RG>poOj?USVL1vXVHT<{gxdB! zq`U$>;ZPJAWfwIZ`YZu-X~U6}Z(UkYE*Ag+;m4sUQQRWUT*INy5?iQHCJomRT!O~=5l=D>DemL4T= zy{R4nTF~w){7J;05dN&0hrXWl)WC17H|Bm#4`Pjl7W!rP;<=N&rrK25hj&fdBsm9~ zE$>;OJY;>`o<8qUo>y<{YA>nxfpRBjwqSqk_Md3C|4nzWK+&l)(@S<^K0y&%LZEaf z>Z?BXx4!CaUC5lduesHN=0y34C_fhXOsT1{C_AOI=(+eA6TrDB5HBICJey@`V*>lt zNz=F6wX*Z3f~m-!L-xGs+vxM`CzOSHk9-{52?m9C#R~VrOjsttosH?z@KrX1+Ctc4 zYtYJqjWTOi;EHq;3m(c!m?ko5O(xlRmGy>hHLZ0T~?|7&YLse(jYgCD0}T4MHZRo4jyQI0>iZ7}9RDUj@59@^^oTbP)bdjTUM}(oKE%swX`E#B%lVYZ zXCRwpd=$~(<4~y&6x#s6i(y)bq9id84=*URqEubEXyCMr;dX40OzSJU8{0KVfZMOx zIILY^U`|rG(^{0)rm;|ar!~5GxYBr83nyYB!anG+NoXt*F&3E~i)6uAWM|2_MK4_N zN=R0LW)$23S3FrbvgOptP;4ol z3sD`3+(@zjqF0a1%3h3)6yIt_yO=~MQqM{HJAjnr7iEIvDpb|6Y)!2^0!_rSO)H3V zdyQ-t?Dm>iq(Xj)Z5T`@b-4@?gkNb1LGDNSFcTaf4I!pM)4HYm$j|Sd3YNJN=t5Oj z2zB6`12hmUS^%Z$M$@puVu(=nVyHqsm^*t}!$ftLVa@Hcrcfy6p-_f9`K!y;MLreo zBF#?5!6?2-Z7@-bXTeP82@Li)NJcpbC@oq%OKdr;gr<4%@l6y!)u^R@gHLd~&7Etn zgIiNd{>D<;T{A_n*TGD_B5yhfsz1dBaISnS=;qcpE8J*$w{W)RF{Ephp1>`T!C$eI zL*;+RQf9K~Kys*gL=+SJ!>p*DI#&N}3GsyeXMnf! zZ9S&{QW6zBQ43kt!?#%0efU-itS9WR1iEdv@u+^1EcJ(~xOMs|%Q`_RZoff43Fx-n z#$)=aR|j8Db*Aa7DR1?bFEUo(F}-Z~rG?Js(!$hnqy=BFo>W?Bz}xw@9(91aFbW21 zXkjFbuz(sY8b(+YS|MLanYrwk3&rI?kIk+5;lsAodd>E%uua?~uq6@7=!6Pxo+@OU zx?M%r_Rt%J8n#-H2ai<`d=~Gty(c`_E|fx=in@YacXIR zT$!Sshc{*lp3ULQE2#cWz+wtb=!=a{5Kl#K_;;=^BHV%U{Q`Y?HVb@{!0q{JgTSG! zR5r};T7CwpaxAF3r0v+jq++*xdmx$a_Rs~ccSAFIqM)qnJgyUw-g84m+vIWcV7}y( zVPT@kSdT|f#iy-n8xV#>{)ZocNO#LzmE}@JUGT*WYl|YFp9-SQ6D#njM$acib*2%W zro6R=@IF7`3ZlVoyX`!FZ9eq-BY8!1`@ChlDKQY74rP^}mNEu`?Zr($o>=deqSa-d zRPQQM>hjwV>fJ&*J;t4Bu{0bx66OgcBE}f6E(`VTY@b9-bJwmla1)tZ9o*Pfo7!vf z-!{l3BMib};Mj-~Q7j=Fky@wm;52hQn_D{lHUF@UOOwds6Wx-jug%ZxTC1%a{Oddz zB{)t_J(Ka;`Vwu1YkfDSRsN343tAVpNGLZ;Zo{}Gqsk?d$|Zx!C8rfjz9iKpj(^$7 z>W+Pqo@aEIwKZHOffT-KYm4h)q%lubvjXQA(hgKi3TmwtR$J@H27`jCZ8zwejq0?x z+FGZ)i{)##n-LaeL`4}%Ni#uFrD>7w0Kwdp{(zD-jqby3F=J9uW&j_c;B?nFOqhPD zFp$`A^#v_Vo>W~RsM}5}k1;NyFmsTX^6e=x2awww{$ti`O>?hKr;prKY3!k|C}ccF zZCZa?waHto!;6&y>{bKVs|>JT4dA*D;BTe^uKxp6!1X6o0gDE`La8n*RoZB;61aY) zQtO^Ze=9Y1{ZVQxoiCBbck-jbf^z0MZdf=iaP z`l;2>C+sD`+_qYHOb@j>>+7V>H2pN?t*&zA`3ZZg!EU?l{8oNQTo199N3XS%R>Wo8 zlgvKKyW?vz6!15|e~7>Q1I?$ZZEo^ZY0^4;+sbFZ2 z4e97Niz{tx^P5>eiF59M3chb|%XotPn7=FNhYT=Qww=xRb~amPJDZv9Y_`mHHnX;~ zS#CSa-zJ%7@#Q6X=F(5mArm31uV>yo3zJ<_n12HaS{>!pPhbixp;-k>Xp@K3&jbT| zO5z5Jn@lV1UmtHKt*TM~v+sGT?g;)__eOtcIDcq3|I8W=@-PZoF9%}|<6kOibFY@= zPsqCnbK7d+F+F69jIWz4+w_&!u{!LmU{BX;26#K)*5fQVe;mvGaV+=8vD{RVmORAd75M_nR4MUF#99q)s18HczHYn9%y(v@Qf73hRw|nn)J{_D6_CM^r z-#d|=kU?S4Mfy7x3rBrnG!^xsB95qUvF)9*vKPVd7y1&Yys2`{J z_DM8-7fpniriW0j%m-d`n+6klAQ-Q&>tC>Wguigoq5d|Odo30)ICl4Il=xcz+E#RJ z{dBx@bhLl?zO_3Eu4kw5-smd)NtBsI@M5)UI1)VwQx~dCeQkbj^D%7MBc(jgC(-QA zaX6>iUN3UYVP4o&(ssdC$Yo}youEDCS(`mz3LW<~ff?*US1jkbKSz`^caapKJ*`UF zfUh6z#?#B_$__bTXUO^*Udv#lrv|*9c@SJ*gh{5}&ou9Hs)UkDihP}8Y;RGw?rYn& zZF|?YZM)sIcWv9YwfnDach|P<_Vm2D_vU^$Imw(ED=S&anjdB|=9tg;J(bZmB{#kF z9VK(C|28%*+?Unb&&kqbiT*)Z%>9rER2t-3XPHe!;={=#Td7cqu5hXnE`*1?n&gYz-j2N=46b6{f zXfcD2-kDTe$Y1zJfJ)l9^vzbd;^@y%EFb=BbYBI31z~!F=O!yp()pdyQ&m!ADe?6c zX%}ea zZkh~Z8)VRAWm_3HP_KcJS)_utcP}#KA};BB!>FI67wN~*zilNjxLoEvir{Cv3|%d= z0@K#@9a_{92A>W<%AYxq>0P6kr1AUd7(%Sl9U-OjasF?24^A%bwN>=*D}w7w{588w zNEdZ571O?v37LdQCa|Snk9V=NGkgQ&UX(|h7xb!&xnm*?jg=w~$Viz!GUdDn0GC3S zRf5^YS%EW=@V+KK`x1zUzgNUxKVH=A8<%+Vh+0Irh+3~-kd+YM2jd$_)wpK;TZtjy z#sY0#FPJ2xxU}@bvZlzkFFTWMRu>)N9JEbvo ziE1^dI+;gIQjS{$Hiw=!)8>Z`0OcpPu(60XRV^G~{y^W7NsHWN{{|^as$h%;fpaz{ zRxXdvYLAsUoT;JRURg7f0A_OKDfnus{<=LRH`xL(Qnl{xjZ(7JT{5EMVX(6 zTsDdSGZrOsSt;u}XyvhMts%W<}xpQrSiU*I$}$l-foL zV-eZ+^@Y(Z%V`}YA@BTU7^MJp!B!i}vXOU*C;i!GewqHdhm8u@;Y|oHN&4qN2^5GE zbnq;pg&kF=c{yz!V?<0K&cS18d9;DCSB`b<_uzkQ= zLKq}DVhsVx?ohDCRn>!wz1qxt2R0K2nHXB|ReDjlTIZA6Mv+{aSi^TCHyRy$i9NMO zMn(`ueL+rH+x&7CE|lylBTj1j)#oFF4O?7FD*X3N;|${$C?wt*DF+>UM>dy;HO~t0?b-Ys7G%i`KjoZbQ+h&m-6@#8MKJvcvLcb`2 z_7gg*nTakX->#!BPaI>42;F;ddCUuh132Z!zM@xS4Pwkr$AA0~VT)JmknjD~rkehL z`ma7NSIC39SLy4xzNW()#6HC8_D|4dlu66SMu2$i(t~Lv-QP>_U2_OzoCppB-f{HI z76#ALfdMV9?(&S-ecRm3Z@NN=gM@}R=_LCmEk@bP#~{OKKdmMZN>xHQa+e@1s33>Y zr<8n>Y-QxT+dzK;LZWHPL0=bBo+@qKAH{#o7$l_Ze`1l54W1{=%O$^(rAJ_5+b9rZ zkF2mJNQxKy@(Bskg;O%0<)XBWhZO1)>RHE#F63@b;NcS*u=UYSVTc<(Y<3Jk?ZY7O z%qCz0@+4;3A&tU@Ht#yHz`20Ni>UVjrc8;$ZANj-{~P4b00dl4f8U7VRb^1Xi+uf% zQY?_@r;)kk>sK--svhU3xM;ri&wmR$s!dkdw6s70$pMa3Pn`Z*wDRa?j~Z zcKV$GSroaDwwz{x&%lbp+sL;u01^Izm44)e&MPNt6ZO-;we~T2`BZDt^IF}GNVEpb z&c8#th7*J)KNhTqs2-o?8f}V9(^`^rQ*FLg5Y&d@Ojz(SIw^6PZO$M*Nt4@U!QL6X zIrO%-O`sYwhTME)@HskasB>oU3VP;-TO6%^DtB@&r#{;P`B+NXQ27tH>Q^IYv1-08 z)HvmNMzz-CcR^scEzlnYMIv7CE5sz=#J}ieq)rI~>}`ycz=jx$$kkwvXbX88G%Y|v zRyS?d8lj6r#BS0th3fcXo;h|JK6d9xjDLdVJ@RGhD)a71QsU+dybcf0-~oxEwgRg$ zqrn}8CcUMR1pG8r0EN}p@waAW5`9Gj;)}&++v@HJ_}KOB{#2oB9Jfv7AI%k3TDDuK z&%`c!;y(ydy#f{OKsvEb$F^oiPDDT(625wS4-z`$+nb21=5CA`byEkNS!-&^WRw}z zf)%4oro08Ph#H((C}CQCx|5Aoro{;jBz54x%!a5j0)s7v`-Ek1)Kr&!LoIxzMnQ*P z%h56|Tboir$CUMyotov97`kz5Rm;lh3vRVxi!^?Yv7KXg)3}>+z1J5DzJ93z)QG(U0EqGT@ZYyGuYsDvK-*y4(Nv~b zdI~4)m_aLWm@;&#Z1o~X?TB9T2e~O+&!^j5Wua8_7ui#eTID{uRwc0#65V`NQQOKe zwdBZt7=e<)ddCE1rO4PS)#SoG6&mR|r<6!Ds{d)-3e~DiU%41(SbYPo(G>$*fficX zJb3so8bcKW6M+`!xjYwt^Z&Os>lT~z?r)dy4BLk*$bB~?yql0dkBDEz#cUAbc9gA- za<_?Nr2_LymLEmZAqcLO1t zEkHr%CVJbqpAnsd)H$!gu5<>O=C$AHCmx?wK^NR|TGPLafLs>QIi(LODR?*?(|FkloYG~JkN#@d!_mV8V*1O2?{j(@myPBg_MxL8 z6LZEf?%weOIe07-ZkSLh3YhksP)ZHHpmCCiE~!LQy-ZdIXVzD2(9v$tSvGed?gU4F zhfScjb@3(cbmhuT$h!?5%jQd-92@xm!|5-}b7I#wB#~pDM1hdATw+N=7O)Qe5=3mo znVn)pY`iiJqB!0b!M_PVAFH6Rbhq!x zCWe7O;R>w?+*_Z_>tDPxtrD{JPbNod?@kM+eXQl5?Ge8$gSCClSN(2gr;FX1-#a(x z@+}ep4V$%Ma!KP)B|9(`tS`0Be^VRbynnUp!7>#$wUGDu`UN-%9Jrfit{apTKbO@n zy6-SA>2y<1Nfx7tGu!d*|VwN z50Ugf4H1ZP*uh$pKY>CCsLOo;O+~{L2%J-x45s%t;|XKxr){wikwM{o{jq63tRp`PNIY$kbY6Q%zby$uD0jSTBSzcElcJt&~S*ZMWC6iN3$c z_9cDK=8>S=d!0n!ic!EhX2f37Fy+UXgtCYl@?D>wdkRi_XR^h(QLn|i!T2*Qk{&AK zOfH8Z!ipD9Yt>j!hP$n+lxm@G<$k!O!o#qRgwE((XYzMYq|M~>Ebx}^NrcLg|Eoqf zjL^xq=S=QCY=gz%DfIQ1eHx>8(+6?ecokvY7ssv`#zx|k!~>O}GCcs>ZW>>PQ518S znVL^u&k?%w?$+d5rz7qu^*157Stsl<5Fp#T=#qbJqJ!gzU&5QaM||C;Iqlx6nP1$S z+kd#L&64qW@@0xo%v;7gQrfx{wmgHO)Vwnw{S?HwG%6y4q1cSPO!2t!E)f^>8(`9c zp-&+Kondf2O^I4zQUw%cyn+P99$E&!)7tcxw6}TXY{cE}P|+2_?;fZv?UqgDnw|U6 zbJ^xtN9;XGUx#(Z*V`p^73&7)IUdQc5L(wc^0`)yP(DOrX%%E^W1Zq z$Zidsyc|xxn%7q0-09;?MDZfTzUo{P8onq7(jx`JIK-_tODTMN_~x}*A1+5>phA1r zqhnkZac<0Tz?@qko7HOmk5fu01glAf;C&kT2kCn6-K@oyKVjx!GblESLce-CnMh=H zb`2D?+2F9X-J#FlC#$y!w(h=RYt;Y`TxX4c6XLTH-YVpWp?fowW@b%t1Md7;Y?8;< zXQ#RAl4~q9E@Pb8A%Xu&r{=xf#_z&_sijWJ&p>TB_ug|k+)u$5u2(KQ@SE7Jgm!6wvejyRB>bLIo#%Q zwp`2UF@gYg>0|w3F2&xwiuY$<9v?+W?3qZ^DR+|e4&Gv}oy6h*W@zXeEFkWF&f-h! z6=0lm15WW5rYB&~3k-cBdJP^I0fS#{+7?AmfS0ffEX(bl>_KBR zgy5l4>+HHF`O^-LVu`z&->s!Jye+;u&eKP(gmD+{Eiy7eX%(rdA5&rHsW8FIGrOFp ziuw`?=fX80*z%adP=VXx@fH)%cec>Nx%8ByO=ai@XHf3`WR^I3;*LE>ZA2&9BQkUH z$YS@0-e0EotqQTFIV73xU2J-IB{kvYS7bL3kzRaSDuh~n7%-6V_29kRRU(Dk+6iY^ z01dU(RtE1CR~Yv+u#JfnM&PZsdMd~+ef0ep@&Nl(BOQKOgQ<%o*^_b~E()BRP;XY} z)0A~RLefa9pEqN2&E;9Kz0Rl*`NyL+}NGpo6(ka3dK8(e>l2;XCqe?lH=h-xNl z&`MHyQu~-?sVGpiHflnPs{@a4r<=>8r$mb#FGy;ik+4jYlPbaqf@zSLAcS(da#!JB z%&X{Gv@Cn6d};8GR4WW=IXZt+NVdG%k53=_Z}@xPSp7Yty67=OvrB^uP(!K{al_$m zGWi1`#eI|?xjvaTMEs0H(?phD+C+rBRv689(z&J^)08|ztfU$(#RlM!F0|>>eKB9? z``X=trq9NpvD(~97ot;1-`sTfKPSIJSCp?2lfxEO_!?!*7xAvDv3n>v*Jy2BcBJV6 zA{PSgjpK`-mT_L%KKbE^p@heW37M5M1Q&zR1E<%O^CTV;EiO;9LSZ0c*h3r)F`yTq zvDsWYV7>j#T>7H(BWo-x8B!}lY5C&_^POskE!c}!;d95**0lT(Y3oI8e#Tbwl6u*Q zAcnxmSXy;#ZP zr+U<&3=}uQi0FZw#`Tf{Qh)Skd9~Z;{r5!=-Fy6}?-jYXy)%g=;h?z?+K$0ZD_Krn zmgr5s-oxNuP%Q!4nI7HIyPY*(tFHnDG8$c)uEc)~UPk|oGM`qdtn|~c$umE5L{S(B z%pVZq;pR0m1&((hI@Mi9^3kd2-2x^(JQ?)G{~8yBQq2@xm!9$SOY`qi72J!6O znwD8}DGLz9((@~_d+dJdMq9zr(6^4|e3|=Kwasl0dcKVej85=Jf6P2RF}t`>GCaR^ zs9?C{q?y@mdy-D))h;~As@m}hrI<>9HlWW8RK{G^C=PKf zrltm62q@~Odc!GkGA6K{+0PC+VxBisys}JgI#isFR1}8}njg=%Gad7P$804xarPj1 z3>9W|8TZtBk3!||9-T?@A^r6lMFrMlstWf=2Gs^!mv9*YE~~^H-w?_T#Pj_R44*S# z=+77HTwH!kXMYfb2y9K0V*{i^Q~lgsxxL&WUHl5EerMh3Y(s#8$?u}weSw`RzRYkz z2K0^_o|l&|*JFNt++DR+8`|ObM=uWKP08QEyPAM!(wwWKB)Xu>J$oNteEFvR8<=g( zuP?^!@59$se*r;%eSg4b+Huw1u|c=sNXYPgPx$t>0ke$*%_tKzY_HF@7s8*Zg}ISk zO942ygr@HgnY*;p*Gh1w)R45f%droXuN?4!8@7mAx0E;kFLJ%lzc1GYxDagaW*pEB z?N}|lSjdv#h8Etku0eY79i~>>NobLh*{0T}g8Cn6hONttyZG7PXLR)kHPvjdNo)xM ziG`e2z303vV82QN~l+td3}g}zCv#;F~JY22sJJ;QM?ac znma$HSWuX~v@pX~D9%B>{J|EQ>-HnQE>a3b^(i5Rvx*tIfG+Y@FWMN0YP$ia%?VW7 zSRJ@cp4j~hq&x(NM%c;dqY43t#pB31Ew8J5wMp&ptU6Q-jR@ryUFL5%p#Un^giJP~ zz`KvivA;&_J{49NWYi$#6C;JzJutvAQIgBELzW(PWg0SY)ywKn_F=nljHn|6yL?jp zN|)w%aw0f(f#o~U#3P5{Fm(O(>Jn|5<(n@P4^$D16uayMYXdt_N+iyylqGzERde8) z71y(ZjigfNHcSzYySA2;$9zpREiy==WYOXDLUV8E=!_Tr?)1VZ(@gLZGwP-axf#49FmGhtG(nq{z8^RgoFc8>R$tNl z(SF#Gfu;7VaR5y(`CO(A+v5cL0v5}r-wTQq^$X6O>t@KQqS{`mvuey`&iU7Akm+{q znnO6F+Os_vjahwtl8nGu6kHTX86VW?yhE{$wW;?=>{r@cQUmkI8!^X8$c_DGZ1_&` z)=;@MC%g=;$vi*3G@H0Sd~3WL-ZI_N-5;C5ag^!XPWR-jXDwI$(8B9pU(esX%S`bd00+$Ql2~IVR&j45SMFiuqT=3&^>E?C629K}!?2f+_5N z{wi2!n6uB%?eEWPf8_5K&CjT~c;g_^0mC~dh&^(7Wzehb@4IV%!T0yq?am&Mz-$EQ z)!&pyOAlvLeBMc50|xzcX3_?LTiG)o1ighKuFVnD#p`u{!SiYWmmq38OsumGSMh{Q zF+&>F=#C!*c+_zPGr6Hw9x*U$GtAnT<>6vnRHHyHZZqFlKgqpN0cumV&E zJ6J>3!)&{)i*%K@qRTXB>r6f&3RC?8CNjhFvv0FXcmp$9L8~2bkPu-#c0-wxq6%di za#-sA{@PwyAg#C*J7kA;zKk$Ou8dsMh-9$u@29P1fE}I|R!-f% zL7&H!Z(Z_eBmKIR0^g};*Qga>|5DT4>+Rs+=c2WF%1Uc7J~?f2%)h6+UtLpDNk7rG z&F9Efl4Uu?ox zJ+O{jU?5=wlcd@59b1ghsg!>TJMS2>`JJ0EkvSWzZCAl~mEWHRs6n#+yYw-5dE=(u zREd{ZBozfh=nK=pTyG8Zr^ zVc)PlNjsT>eauq;kg{6!-K?(1>#fDM(D`knV`slFKzfcp-DE*yJ<7FYMo`USa#PKf zJ&F;(^PvmY{E5jyO8>re5H-$uhw{&b#|FIyS*nD};bLKGMCRykI;t%2+yx=rf((@y zFShx+Q^YuBh^}-o-1z0PrU657F<}}sa4{A9Fw&IFkUg!KLjgY)d66$+X9Qz`h_D)D08l* z6x7}8l)2h})e?#t<};Rly??sp>b~sOHe5<<+Q%zUaoxu;$9xpC&^vnQj+ge-?+dti z3rEXU0YF;rk9vB-iT?Q|OielgJGpwCym)N&p^pJ%wR zrL9D56=Ctrk~Z0`6|&0z*q~U5boCFgQmc6R4XbBkL~@$Vp)|(w81_LZ86IpbyCuUZDh@_Q9(*cxU?GI&Wd z7ogNNqCG|FFlW22ix}Re00~veZA1dKkGCKR%1s8EOLyZ2T1C<~OrpPn= z2oT91kEzoBeFw&sxt_sSv6k{Pk%yCE{w6>`h^|FohG7{76wJ(;ERcgAvY~2iJ|ssq zMn~23#-aI9ZSatq?zJ)5BUK079~Zr4o2gA9I1buea^K zd8;>sY(975i-rJ6gNteQTroED`J1&78ww4|TtDf{PoF9NH@eT)GB3OJd-eRQxn*6T z?|q1-^n;;LGPqeAqjN7$~55e?hx5L%>D zRG0++9II0sMu(ZZ8}u>~TI8n42u1oLJ+V~z?^6i=gLEEJYqE8`K2mcVZB8;*In043 zA#ji7kZ0kCXmXTrpAls%$2+}G3%%k}OQVm6VilvJd=-T{fej2wV=W$mC=CAQO z=k7mpEiOw^thNhC6+dFXA~C74&*@!3FQU+FHzkbfm=lKDT~~3;F+XQ3_IAUpFBDo` z;gFrEA`0pWV8=x zw1Qee2_$SO2L}!7iH*i5?j$Uug;f%@DOM8$w{R>D`aa7NrOCA3tm9IdS3u3Q0Pj%_ z0r-Q|V6!hm7rxW>=q;p#Mj@)JForB+V+%){yF4%98lYxjA!+() z)#|TK+T4j^d90Wr%R+7r(Dqrd4t^{dko*$3S8CC56RjG`#0BMNJgp+GtR!S@FYPEM zISR*bl@S9cBjA5yOkH?_Mqn?CMFSLQTo8%|0L06j)U0Ol#I5UR(K8<2Bo{1)zNn2= zqqh66dI0QdKRb4vFzW@xMORBA-xh(K8GP6j;IpVkkoSZ?>%*9??OMTs>7q{yfS6T$Vfrzo}A(O8RV0QVLzXg%2L`}P+2xg$6ynhB*l zrX%-=1Sd{l1x4J`U|5FWXZGkjvHk@g0la7ypV9e&>3Q1L)9Mz1J^02{PBx1qhB)(= z+phpZN@gyj8o77~BT#qt2e|&x)6^@jA&qtuHhau7JEOE7)ju>Dgf!Gf)+jW;Q&Bew zeW*1NnkC4yE*!IS<-L?5IazSE3am*r(~5nk{Avo5POZR7(WU4khS0R{*A`BuWNH>q zlPEjo8Fm2ZT2#*1j`6`b>ht%@jc$ zY#}|(axp`o%pSXjy(ovi#jlgp^N6u9`$kGDB|Z``a}(_izsEFgk&lH)wM7mpaRV^mKPhh%#QaF51SBm{RH@C9LM?#95@&W0l-9@P4BdR$E?U%KM|I!tpD0GKFjG=Fiff>PE*h7T3ME-A`34aOW zmM0L?8`r~T2~+8}5HW5Wd-NX$XZl%^ka&RPfXvNT(?6)vqH16BPB4WJr;waLNJT)e{%-uBvSq#?lz)NM8(zw1504Vo~FSsbeF~o~?OBf(+;$fyf2PRbS<08{@)pt6iO$K4I$Qho2e4_a?@j!&@HUd1UGx zjioU8@!c}%n)g~=R~QnUq|Y*A!WQQ#`y|2=tEiwqZ`EJBizi4qn zN_pMZHd@9LYWMBDX7BMEMPwfvI((si7wOsh5@u+#SYs3cNh|U-8NLOcn1cgj4&b!7DCz|SbC$V>NuMRGshBR;y$|_ zO$aLXD@c=QEtH3yyprF2EnEO&$c`>L-f(?|)F2zPAQYJZib3ceC%k^ax-D=}PlBVt zWTu4l{)BNL2#gEi5X%Cr?w8m_2Aysd@Z%eC^k>>ntXGluX4j6ha(M>p0#M{0mKaUQ z+JAy)6;J#C3}%w=fw7B!2s+$Ax2LDK{pj#jxi|8g&n- zqs-q#BX(k=(Bx!%Z0NxlxWUU+vU(P;`WzmFI9@b;Ecj68hMrrfBR<@eKfCi`{Lg^( ztbi>|sYQl55&I8H)$TRrwXtZ9B^HTt3Y_(5yfhhupvkgu~(mMjKjk$mW4E;2;;_ ztW{4R%3_pA0R)H^o|v{k7apDZVsxyy)wt&`DFD-xHar9RlyaH5v(&<(_Q7CW;D9tv z_f=|$$|-jD5kyUB!G?h_%BoryE^5DBm)2=D!eFi<)&fr@v`r(YlL?pvUc(rJ5^DB01)H+1mWVSKWnk4?oLub?NYDV_{Uex1{>Z$(j#9Vo?BDyZ41(g34LRhN_|uZ(I0a3X;bKpB)wdjdQqW{{mA`&^-k2{ft_ z@TwD$>1h-W^CNuYZ!j2q%mC?rEmwVf`KOwk;j%wd>uE;ub_#!@VK{txT%0ZsiwE;1 z4rK}XPiq@Z^GsDN)H8T3SB8%zVlD(yFWwK8<}$KB_=IXR7E1Fm5v9<(q@b^|#oWj7 z`i1zHYlJDE>v3!}`@D9wO$6~s&kkruMUyZ)w&BPdYSe>m@N?gXlw<4DaAO` zcy`;HJ2ODOw{CH}K{p;uS!e8A5G*}cX;8re9!+obGjRmxf7yq2j$8bLMNZCg@0K)a zSFWXv!(DhhNFd%(RP)r9l3@U7k9-;^Y&m4*6waKLT6QNx^|e4Os2P%al^%tiy8Y@` z&Q;4(HA}JjuG}sgr=iu*3dDD;2itF4oBd6uvZP)lab7O&l`F(F?7*sIYFhUy>vY{$!!~|X~^rTW+VvE^H*;~QVprLi(8Z%6FU$;jya zxe^+G9+TH3>~VvUB7jGFaA0(G)5@x#T^<`^#wdXq_spT$z{I%Ot?)7xUJ&gl6D9gj za@i`M_4jgrALJ=W%W142paadX3OKpmtY?ICFb(GERws_?)!+m=Hpy4gmutg2W6w;Ur%~q_+ z(1B7hMSI3o5s0Z8VR7Y(M&PYs(24cd0)m~oWv(cj6$hWap5Xeko!>~V2Zwgbu){`3 zSMXb!s4bDkQ=r;~-`b`3vXk!7KLOox!oN;9F$={r4r?YjIm~0uIYkIa9gDDL1Xp;K zTy05sln6M;F;J?h#E*10jT-ne#t%CZt(+%m4x)o9wj&Ml9R~P%zn)jFJ0y-0aWav% zXn3*=6E0&Mu z#cSn8$0}{5mo)QX<2D5`drQUW(ulz;CBCwqP=_xAE}ZvH_C zV&-5BjMa>@EO!x=ba0_rw&D<=bO(o>q8-?KJDl5oH~+r-r6A1BvvJ;aj5E3_IWm#gTMg162 zNNIUOIkR#Akk*QyTtpw)8cFT-G(vQ`YGP@9B9FIMi-G+7`9v7deBCAk6_ued3MAC> zC_!|pPA%7#n_C`sS(D`c{J=|6TjNz;Tl_LT`uEN28j;CDi;<8B>Z7@(XC>(@hZ-zJ z<$;&<4?zcM`}H?`@4Z;%i){(h2!a-czqYNqVrLL_^K&b^T5a@=De|i?*3lrY)usj@7!r9w+^ELP z%{eupWZ$BRQAE7NfaJ;qp)M`3B@UHw6L88JzBtS^?q@?6StYLdZ9m|O1m6n5SFg{7 z#%RZ`s>LhKSY$7VBbA2tqN9^JXZ+0$4OxixQEio!s%{5;(bQ`gSzKd`=9%k`WTvrh zgz;SBW;a#;)|h3bUz|jjr+Y0M`s>eQmjtI)6bP%r^gK|HH9BGClzf?A`3 zRUEv`!ZHk>^Dw8+A1L4Ot0%srnACAQvR+S#YWK^9s80)(U;R-Y&5I!)*^o;Z9pH~&7~ z>yah!SWAVRsTaNk%S~D{my<<0g#LL4$UC>0FsJn%Vxk8Ps)E#Bge)I&gy{$#`%>8C z;$Jebq8>Ju%N(agHWi%lyw1_?{4pODyn?Yx$;Rr%MUV2sLA_A))iNoVa(!HU7M`8G z%DE&iaQpYi(guTpNlumMlL>Mc@;XkRq|_z~&p(X0`bp5)2`e&|w=uw2muv#5FHIn*M$l38?hYV=eEp}P?eN}ha+8>J-QnOTPZKB8grFOJuEi`dr$rCVc*B!<#}v|RgGjz@^PhryDV{o`^ou4t z$Q4&NL`b9FkR&kSn$W_)4d?M2RNQD7hNJM{CXTap*qU%$2m!`%&aQc3UffR}X?qFd zz}4UpT*X)pY44ZtX{T#VPt?h(XeLZ|D1&cfrs(RkWYOiyc!l#9It>HY+$K_4=c&9s zg7|p|EHj&7miQsg{pcRVdC$jV9a=EA?iD7C0YVf^F^xIPj5}iB!B&Nv$ugNp%Cje` zS)=+R{08%?`Dg$AQ!E1aP+Oq#IRf0UF{-R+BfVf3nB~-~4<<4g{9ql(G;1=K zyzIUz4FO_hQQWdS!uPrv^)AcC4CA^AGsTLdR~7OqZmSYpRMpWB^8|52z_7C<{Az*l zR@>nsCbNIDj=VugK)NB1_~n|m=g^tMJ&X^V>7c#lUIRP9PH326%K;~}Ll<|w24YA~s?m!uu5WkDiLpV7Wcf`SfWp~l_@K9>3+WlNOV1(RCHhOM zW8}R}Jd>@hikYVj=t=v-(X?I_X8wf}o{k6~kFNV>tfsu^N0?^@86=&cF7Pt)%{0&G z`&n~3@x2P%TM2-3BZY}Fz%dIsS!y_OcUKPyANzhR+(l(jD-ARebiZ~=S97hP7?Gfo zqIu+LLyfp)H?J-5$MC9;YD!2P*n={P1*tjVk5f#7gZAN5H4Bqy#(0jlW~gPMr=&yG z_R^<=mWnpde4<5ihBejQcU0&5zh3`l_z@ZvVBVV_n4 zd}i5|$_ZHo&$92H>;xz1Pj9EVII=&^*V||PJzxBfBoEf25zgku6m<(D`8hDansstSsZ7mX!|JnmQm^o z9E@guZ4d9@srm+jD!%0Bo(s6VqYfO8XYTSwNcb-2`nQinEVw@}x_S>z8+aS#p>3?b zX1I@_u86klsU(T@niPU;kYZoW+Yelj?e*pBD~1U;3R#hcmhQq9|0d-ihA{FnPz_MH zYSH7Bgg&?=li~@Nh(`ZT>Y;x!`!u!?1I1SSQ<~sjt;hhyfQK;itLwCA&L~kzz!da) zK+m+|aw;GqlZZ+#SB00D5srJ6mFeJ;zaZ)HWviIz)_{n5*R7c7?qa?&rk|%Vgz0N; zAv7GFF$62NL3mn`@{a|U{|1U6uBE<+>LO+%IOJC)y3DF+0P==5SeD`l&tlBgy_}V_-o&|DAmvF9s+mbf z)otc(i}%=9FfKf$lN1*sQ)O=R_j$F;{MMm-BP4ghA!Q z@p<3Z+_L@g4gb(&tjOWWwo{XSP`BFCX+0+JHryL8a^uI#->)JW9z#q&OT;sVvc-l# zy4)(dD`^jA14fB1p{c`}dfSJNTzd_e&c9(=5-}#~0r0un*J7|sNN3U>k+j<#nmk!$ zf%bjN))(-m+ese~iQdX8O84j~;aE9*EC-L3n{j8g^u@HjGm>PGreYh#c(I=Y2(_sr zhC8(yWML_{!0iNo-g(lxRS8Co6xLk6c#i7~f_tRtoq##-hjI>#5jm+t0=7!4RQaC& z5f}?XoY(Z!em_3`koY)p-fT_ZWyk{ znx-Bd)9(e2TLL;atmv}zGe>uRd&MBVOGNN|z*>L59MBt(VjJ+7{dy)l;G$6&u?XV@ z)NKC9Dh#cdVz0!b5wOT5&D|6<|9tM}mH)mf8gRePckx)hL_bJPdmP0-+Eq{^7|f{4 zlLo|U$amii9@wi7eTiypZfpv`pWp3?^+5zXm7ehpt4fho@&GiNO~I6e_~VER^XcV8 zxqB$Y*Xs3#wYh_Y))YFv;U!l8!duY|ZrkVvH3YlurL63}4Gl-7^n16=+QP{pp;1>C zK&Jq(Hm)o3kDu0IdB_nGf!k(_&sHc-G+(=W!!xbc8{5>*ogZn{uP)sYD>afcb@U(G z%PY%DRqu?ao_&&dsl+x`d@PO$3#}}*egn49w!qv_N+l=S%(LA({MM*QRQiw8p6^o}ABQXIc#Eh=OFT%neq!CU*mI30{9G zRPn5pKt`UYoya5L3!@xaCgW7aCVh31CnOpj8A3}A;K;el;#ry{E3iCkMI1tz8m7gW z`3frO<-I_Du{oZEALi08zlwq$k1FbmO;pf_HbPKuh|zDJmW+o}}4I7pT;YmkgM6FUx?bPV6m49i`PoCc)$pyU{ zG3-d>Ea<9&*hPAoOV#O>IloduF+>NK>n06SB?seH8jzIyRR&2CkVH`rrHjI;M(h|8 z<#5@&iCwfiM?cs^AYtX_kEPEl$s~np?~XeEBDIym$)cg}j(hxBmCy0>|Dq*?=1TQ1 zyIf-!+v8?qgunI0$J~<3o~Lk^IW{zRCIS8V#fk)Cvt4p$5^{;=D^W-UoRIS-L)vYP)F<2av-j+GeF?mo zHBxdRes7LPyMPfoSQjF0p3?8HGYd?W!B7yb*=E$~SRz{F{)4jTsHWt^Gl5+ngBNyj zCRMWKhZ!21vTet&uTfoSvM`3q(5wpM>Zdeb4ZiWh$OL4}f+?~xa?eu3-9Ah)x7JU& z2?(#1Q#Kg*quqh+*!s5PJsmJ?uMP>6KOov#i~S}^QAKl}(V_9!SpwYh@HFJg8eLQ! z{5Xl`HJ=2cz$=J~)cexX?!9CbhNFonhBR3=HLL15*{P|KC@O-2fT{~^@hB%7Q93YI zdsTo^yDN*dwVZ|XIHzDZIHN6!wc9H&{X(uJb%?}KhLL32@@-lf2!r$?DK)GH2|~ke zQM$)U1;=Ihk{KLowS&$4F92;olE3oh0w<6GVA6w%ozV@4qCN{i3A=38OG=;>v$NB_)}7U4rh(!(-NGgb#~>% zV0#fQeOVON$~1yNgkis~}+#~**h>m|GGFdM^32kY|RJbHJ7(SE? zs4Acd!d2w~^2fjjyWs~NeDVO#~sot{$=$ih040I$M~oV=L?VKIpKWBLiX6-rGZ`>NB|GRo!6-?1cT4>i2O)8Bj! z7(f@5vMaidvn;-e7O@$Bj@X}Mf<1)`AP?e;i%I4ItTF`LzHeO=00pix8HT9~Rp$t> zGNCHut9TSM7MpoM>nsUdrj&x@e5czFhaM^znF1_HD+PQ$j7K9Eq(~OP@XKUav~U{r zBQ}`e-5|O3blT1rP|alJ-DZR{U5qTg3+IBRGo2yXnF_3eL?HP@_6!mRoW`t$UWxrj z4>Nx$33${Jr`3_sDybry9aU&HsdLYV*sK&2$MuR`RTXCKX-hWAX9sWcs!Bgqg;{&K zCYzM3MmZev-ELeJ+-^(^WGE=qh1%!D_~S&NIjMl=6F_fOida>BimK|n z-UuinrnFoyxQy$YGPOZjgIYDXMO`$Dqsk>mZ3`1k*gs-r%Qe?D;a~kJS5h^rPAP&&p$q_qa zZ5{6Rwzqcv(d%}0+v*b1XCSkKj-9u5+Gw;zK!}>PLK7lg)3Py~42R}hD8Qty;cLY7 z{q&>YBf>?aC8$jA4e`ymzGSNQ*efFUP-qpk-~O+&t^K$94im^LM^6Lc#k?w00jArn z0HoV32JyZcChw55DcBqyomPV*3zmRtf2hzcEL99-Yyao1 zU#5=~FGigM6t6upzYN<}1bYx>zt&=NQd{SHF(s)d+@^RT2}?_Tke%9mRw)Ziao=NA zB5y4Vgo>D7xGxU)<0`u2apm$fxXMp(E&7<^el-@VGS0KzRH4{XFzk&{RZ%5ONF=!w zlq`>DP7{Pk64kwjQe&({HPn!rkVKMz#8s$7WtXwzBb=U?yvOHjXh}}dN3bg8uvAgm zL|j4OgiD4WP+~KQ$Cg8G9QeE$k^(x75bi2W2VM|Hh5$j*EKIKB(RLhKAXK8LF1|_Y z*WsO~noN|{__vZ)E3XU`NDJ1)h#&J7$G$ajRkS7$BCduBUk!t)m~3+h(|&$Ysn~KH$^fF+cK7B;w;T8&(f6T7>mX;rdeC33IH;}YIqR! z!+WbLPm=ZE%M^oUL4yk<4i~s`xd0lB*qmVEZ~{2dCw@*as(6N*=yS3~X_p-&1?=F; zVFyVOJ4g!H!IhUCBtCYKc-X;}!4481J4ih2;7YLrvqetLz0jIzBw18fDWqNVOy;Jl zi7HNDX46BAWMbBpX`&|oBrX1X5)M)@;5Dm}WKmP4%+CJ6#Xj!8A z3;H;sp#G{+|I)5sTDN7?JvZxus@5RDt{!zogxXL#_h~X2Yj(Es$!d6ntD~r_YYGSrkDP`bD?+w3d)O^* zh4>EE>QGBJ*r@uFv?Aj!uu|Vpn~XkYlW)it^8mBNZwXk;2cE@$S73nnCT>`FmJ8ly z>V?vqx=`d%;;rR}&d4Ncmhy5$VD%Q1Cxy2-Dfw@a17`tDh9BS8GNtXX!}MDw(EcEr z5fa*4+ZD{^d0KwwJORMF*v5Gf0iYXTk2DFrX$nt2enLL1NCeyhl zlV*n48Ob#h_$cL?y>hPEE9aWMVy@ZqbIqPscoB5i7i7)6$rcJhT(CqA+;cf_mr0SF z0i)^T@QFC^({fg>MiGG#`$+~Q5h@YPCrT2dkfA)OAZU428Q--?_KbYJBCj0I#+SJi z@d(~Ld^le&Y5Q|kz#g7l0@lZ0ht>J(?@`nq7AOp#0^8z&E5ZCh(+GB;A{d`5gD;#+ z{@T{auLjG7MQ??yfOe}D;b~#vDfacN2kh%t)A#i&`!sa*+c*uOwniRZ({^wB=A6wpAx_hPb9p8kh;EY__Lp;wZGhnf55-bJk_ zG~bm|LECW*a7p511YG0Dm&A+0$;*P*8ww}71FzION^ZJ6avxE7Jf)C2Q@zhf*MBp1 z65T`wQK92Q!zK84@Bc8W`T?JOy`==F7frkkC&-B;_iV*Q=Ji$;M z5%GE~l42QG3B6y%Iff#^2#6=vb&K%3sQmcJ=W)&zLVc#2hP{6_tyX>43_)Cr2(JYv z^1N3*=kYv;1pnZ@^@jc6S(4;{3{+-lbL!hfg!LUNC z!3Ol6qwv*Q93*{=(8TtY!95tjv)@1S2cW*JpG3sQl4=VNtXEcT;rEQ@CR^Ju#1No% z5MA{6+unC&NQjc+P42rXz;Pl0vyD>1KG;tkhXq{a^++%l#^K%u!cVUmySJ%|0vm)= zC%kB$UKmPwgS3FW@SiW-H5B}t;=fkd7V1BZuV)cl{hcR`Nl@bSZDfkj>hFAsq>5Z# zf*0_^nXF|dvRWAMMXuV~BBpcJoh};AgSneRKcNGG@3!aIYx5=J6X>4BJHI0_cF9=Sn3Ad1RLp zSNC<{DUkDaigm3D#(soz?F{hmfc~X!8ea;BW(s#?SKLSL6K-lhi@QkSNq=#wd7)x$z97eo|VS7}*pYnYrdv zbmLSDIwQM&YcOvlHfPRnOi8dfG+$gAJJ6N6dY|4CVu6i^BLo zo*7&SOAEoK<}gf_0mdT!X06Dnq#wYMWw2PZ>QJe2_fWmDCrH&6kB`_S>MjbVqn`b~4OX!Y zxo**m*}Gs7;V(95q;T}Y9bd)|K(Z9^{jqVvaNJ6=YEF-H=qFkK9E`jAeLgDn zQS%~AuK#Z)T*Iq_rql&sZ**(~0Tu_WNoz_OPW~HNVF};wlW?4(i@czvW3N! z3DSkP%om!Losbn%oA9 z?UNIT1i2T2kJ=zlV7${fVzc)Bejw0kE*j4T8J_Jsyewf8VUe*svALe(i1sdE(rS5p zuXEBp?b)g#C1Ls0NJN*jzcpj!)eWUtJx|OCkni`&#FH@~6+uX&DwMfd`_xFLHTh5F zb=I!9Wo7spg%El>px88Yw!~S}JL3X%Md?TDM%i5ucHGE^nAc@v){lo~CV4<`A(iZ5d({0Q#t4f>{1=S+$jmhE4KRB~!K1R%p$!-pM4 zcizDoSAt8^6<1U!6}X2|nY=3yuws&V`%x!RlgykRgdE#%pSHDs2O*ovexz0KY_nPg)-S&y7#Y=S&d2lfop}V8-I$>uZ-Z|EL z+L5dQ#Nd4$Eq#vC1Sq5F$BM_-jE8Z0gluiyLAwSBB%v5~&tOw$*##O5{Cp8?oij-6 zXmypa^DwO0`zW}*N`eS%8Ui$yP);cW?Os&H@`?(CFnAMAZbI=L&!!nB;%Q{Dv{VS7 zPt(7ZJhpST%_h{=aqsx3+d1ugX!rifc0V3GVs}b)#xfFB#;5XrMOdG%O5?*-%jneW4S$+uNxcdekxU%<18J{z z;t}z=k4@ku|0-T{iag>xod<((s)!3OYR;J%ubQUY<>nCX55&UOgPQJCYq_l9wW5Yc zWepo+S=RK0x9L&6?z^egd+k_HB^U;(PV6Bn&~|jIl@dv_8~@J z{AtBx?z#CdQ)K282$hbj0y67xU<1Y%AoQ?GSGs-`aJm9wG-x7>7-LF#Wc0sb-sm}} zJimyUKEVmib44{pL#uAgc|Y3R{Z4I>U-vBndjZ1K(xx;)%$vfXB}kjf*pMY&NrT4# z<8!Gy!o}8LQAf*Y!E%M-AX{0XM4ja!n+jg)2H5X)VHQ{O8FBM_A&n%#%&^rapL_d9 zTkJq67vtjb-v5kvR7uf1#FZ2Y0tuG#Kcm&=%Ku<&NokaaSPScO8P=J?F}txQx+34V zf&+Jm0#qB-s|Jfs(0ZP6(H~$khfJ;pmNSU9(2<9RBBMewC|4MV-9?DyD2QSZXdl*D z*&1LBA24^akL&X5lYAysrS7UxcNOYZs@F2=wS-3-pgd9?-WcTxOe*kD-|Vxkz9zSr zANXtnpZ_}Ayu70poTQtBEd&}4>{izPR*eFCW5q_Dts@+N>Zp%m@yt z>T+5Mw?q zw71tiBJ?Sc{DQ|%OoRV*EI|~IQ^y}aIxBk-d8%?Hrcssp@yB|x zr=ECh<}_-Gowc-1Y__!c>a*I^x!PEa)Sw1qrsYNsPknQE;G4tK*}QpMR}g41K1?bq zITaD4Z3!uZkr-CV2$GXf^zTVHgiBP@t$yFzm}}6&-%n(gD7`6^I;5i;8O?xuu*YxVspyc}sQ=DO&TLAaZ17 zA#q1U;uc#JiB^QkayPbixLlb=sU1}40YvxWW|sD`ZL&o*4*-*vPi#GA#w-i2qA+>pChh#c=c*TWPcQZS-DJvUy6ez_PxYJMM&JFk1Mjt zd5Cb626}JKxH&(p(Vt^BYNHx=YztU4=&Jda#6ho;aHgF z(oFtj=lvacS;<6+nc)(XTGSi-*W@%wOCO;UJ7ly%lyaqf#U2As z@WrcoV~md#$_inLn{n@!J5iLMSL74lji=!tp1=fOYZ1th)8`7rcNh~Jldr>Hqu%k! zac?{fW1KZ+GHL)@smhQYXD63vSICmeFQy2!#)CvSt9P%Rro%+U;&_^|&(V zoqDK@+dvOgvb~G77F$WaQ};(A9~&UEd>}U z2`5?h(}!?0*k-5Ifm2mu>|~4}C?Cz2ur(&lW@ZuG?3V@H7}nN~kB77cH2kW$;jB)Rs@Bxk0OH2xk{E;)>t z#tqm3*dn!J{vaaqdQ_V9JsM?ux$z*4a=?f`NK;x)WTf(q67tZ(D+NXkGaF)FjxYc5kmHU+yl z-yZ~`lkCdC)Hv7Biu}l__ZG87G?fsYPg%(@izrwrd`fo-E_k?J67mDmoeL`BUeYRD zFU7!zJU{&r0f`Sacs}3^P)(>@&IxA-`CDy&-lmVm>Gy5mAt)Y@#Z^#jRTLvWU-;A% z=#JGBMh&15EQ()?F2h4-Hg`Q9F_5=UFf|L|G#|Iw_7W(aN)Zi!5d}U1{GfC(N57VP312pv=1$6|nKKDR zrmq*%$CX!xC4fH`>n`XIs@d7Y3W+9M%=?=rt$&5VD6u2#*-+>xQIu{x*|KR2e{%J zEsP85RInN_>cr+c&J&ak$Lxo2C`UzgSK@NS7PFP9GsptR#tBlu<3(_q1U%7Ri3ES8 z4l5olEM51I(R_~P5C-oh4BGJQV!}KXMHDG^lJR}8hv_ zm!#ssc1=m#M9JVP7`!P|EwRLQ$R+u5nImk`N1~c!IkbHeWg1&ui@4)^vFcI6G^yu4 zOV`nGv>z%+y*et49r~hBn>NMHmNxG>vr4%^6#H0Og)i1s2XQxv#1M=%b_?u_gy$dT%b@1uyPed;5(AkG5rE9H=sxxp8L%jnRhe^0sc@Dbv!VjE#g2r_Ad9?DyJjf~h z+u%jros)L^P}w2*yFxUZtXGa<85iD!URGRBL(4=A^-i)S_U+DHN~fJDcMy+a%A}|r2AMxTa}O*7 zF&Ds-;>i*C4m;fKZEx-Tqu1^1w!JWW7sJ``@;8fMO#BKqM7#|!DDb4?eJKOAA{BAI;v%wKj2etEYRa>xhuA1UnN5}3a@$cAt+)c)D8-DX+eZ<;;WJr!uB9yC+rph~ zsavWv(^jpeEBun1bWT#a#;=kz{+xiLWfjYFoZLoim9JZ?jM&Zk#V*YMs>?&8RB~is ziBCJ(7=C-)OenJjZm=HgI}U z+9e)0QIzF-={!L*$#`_RH5i~j{zb3te9>VNOMKOtpl~Xm2)3P{y`6$u;5oKZG*)`z z`BHAaNM>o79b=(7gqM9bQ5RWWy>ihHb=#D(pfsXwZM(k558jV#R=116RC>D|c-Y)N+-dXi=59HqoXsykrG%XmDVfK~kg>O+4U$ zR{1p4Q{~y_;RVZm@_E?$?aafLAy_dHp@M`A4Rb92m z>yQLleofdPg1I@5UuSiSjiXC}rqe(0BXod?>%_YnBj>MM`9W@sIQ8nFUIb`gJ!{e* zs+APFNy_v*;_gy1daV<3XFL%`mE{V__%lAHOPl>PVP_Tul+|MJ(Fv(Wq0B54DxZtN z2tuvHMcf_3;N$uy3=4|Ce?X&l{(F^|xrg5yybS*SD=&k;TfEF?3HLQG_ZjQ2@PeNk z>iEOP$t5v6yKIM9#PRg4EO}@GyZWW2oV+viQH@eK`nUowrP&!g`naloweVNH458CB z9A!gvPDe5FJ+qjGL;i8Kg-U?cHU7_fNz+*&Ei4aFE>SWsLQyjoGhhwl(F%f~H=H|g zF}SL~@8P?pzdxhRKdr)tnN*66Drs@Y*blaEbdHP0*BMgzR;52oM&eO5`@*N2FnfVV z-m!Kgn7;6Fj~_qq4|KJKZ*O20>#`fP>|a$`4*N5fZ_)CvY57&r?B^+++~1>}Ch>=j}JTgWvLi{B$Cg4jMPCHaZ zMEH;eKYfo|*70#mgZ+nXy_s-RboDWY?!$YQ1V2;!6zq(ou(lFXUc@d|)zP89D$5vy z3XfPq%VUFcAHhH3 z2*{D)XN;eG^iq-nKAT8+-mb3xE=U@>?7QHK7x+?2wd>@@ntw!$Gu-~_btbt;E!VRp z2BsCf0`Gim%41ZH7#NsNlI%1FhNhD_FDP6ggUR*S!%!8qV@~auQ#)1;yVdMn{1_~j zj*xcpZ9UeL9p=xoAU4IPW}fWDchR8M5^DsT0Q#rg2`(YIp)x}cn4cEwbP89IG2R8* z9lCl0xGNF{$+d8r#uAC6!yJ49FitGpSLyRWWZ3rbC#a|kbOjCazHLYPayk3%HSv>-D&!8`%0BUr+KY znZIPi4ezWH6hTg=-SB$MUFWF-(|{-Kg<`jDu58%E75z~3{mj0*;|PhqYQR51hDFPA z7Dh^&ppU~5EjuEELc_9V~Q@-e|h zO)kP?-Gk2VE(B+Ynkd~<=uwl&{sdy4Oqf`&fE^vS@hGZco&J0TDeBxn>*DfQQE$@m zQ_2`}Ad)Nw=CneL;@B!t>AJWiI7|w=G^CMWt>HDubqD!f0dKx;#sju4tp zZIx^0?c_BP-dVGDV@V62y2dFW@m5FQ?3yILTGP-y~CWB`e zT+D2RvJAG8g*T&`2nl|tzJ*EKr9lWv(KtJf(rgTm&LyXnu1I(jY=2*4vv+L;7(A&M zVkPOD89DrNP;0SLSda0vS}s41JM35U2UcM9hZQg*ycByX9|Qgh7FfmW)`qN;{X5NT zYjy4vlJ|kuVUnH1guAcOPv`jtDZEvFF{TS;)CDI`ho^I|k)q6|>@AFu*og(zC&N4* z5AQLtA7wQ*Q7=7{V%@WC2r9xrUmLPH9-BI&8#Y8y?V1zLZYLraO#CJHL74u^V>#8F zRJGvD=h-{rdA_Cqt#cD+@%hkT!$v+I0WaOb2QuA+Y#~LQ?fT8}RczH0n@>2LS~tIn z^3WLV@FwaCUTxPJNq)r@0rC@Pl;Tn{&*D;8NHV4ox!2*S2;&*tw?k3+66n8|L^~@LA>YgtreqX`xncj?0T(%#TwR#>+RCR z8pZZi#2*!=Fmik=&l~12Xm7@11Le?<#oDM*pBc1L%3W_5m3EThcNje4~nnCu3Z}db7hCC0cVsv7h()~7y_!II0_!P4XHj==>1zQ47q;U>=O;~ zvj{+ZA#ecK_FVr}tsfYqV3Xbk0>(+^=j-~c$M^=!Sj(HTaM;ZXOcoo#KN{e_rct3D zO4|^sfz|q#s8y}S+p8!7d)J>Tw}o;zqfDB!YX6+6F)qT7z+v=%&(BegCnOCEt&;?D^RnS2O9-zyN_tCfuqDel zd$jbr5%i@im_$(rO14yiZl#c2s<*aR!|-?h>Fx5@8d< zf*s#ZYYDZfoxUbrD`AK%S%BP?)G$y?j^SKWycvS8On|%gfV);V16T=n`A|v$A?R>J z0fTYlSE%G`uB@U)>>d7EVuH_G_WzLgqdLPaVICeUv^q(oE(NkBdQMx4E$Lw?#RRF- zha_>Fqoq>oh!{&*1<8)FO)ix#8wxNC}^t(%wJOjV1p4r558hPI`hTgv&H!^zZAgh>E$< zhZOJQmvrr6bddvE=CMJefuRh<3=r){2E;rWQ+Ve|o+sC35PuS}oj?>ZBNY6+b#mA_ zeE-&azd!0H6FBxpgCHCQ6A>j;gcgAPgkdpeIZQ}F9ddI~ZQh1*WX6s=-BQ$%7EryLhnh6#m6gzksF$MWB~~@OytO)gFkdU@(OD*H>pxeA>{4lb_61&VDDI;Zu zQ?9SLbI0r7Gc28x?|I}-jFH=-BX`0UmS^PTt*B`9ytV~qOIxVfYjc8~xCHz8ds&4y zdKI!Gp!`s$yBnM=$7oAO&Umcse(e_fc+C5F^r!VMo|LnWw{lM5sf|o7e1Mq&~8%_#$Ut3X1CcJJNS1xz@!wdQ%qL zL(MDKQ5bpT>13Gio18B; z@9cOsR6iFjnn8?|NJGzB z!q$$=D?vc6vG&mq4{aEDxNH^QQnKB!y|WHJfd9tQ?^*BnYX>z{qrm6iSO$al8Pf!^ zYOAe;tXf_cs-QOkOi#)%eSqXsAo{qtI+OiSO?G>Fo2&9!uTSiyd1c(au+Qy8Y!zBuMYBV~LfS^4D5k zO?uH6{$(=Ezh?tyEsDy^0XOD?$@H^t4g6bKCC!SQMaIfge>#c+8+%B875EopX-rsa zzB<$HzsqI0S*5Ihl!9|-th%12Ckgj>R>69y;7sS5E={RbYPRGSFS1#xQmw>Zsi1~y ztW<>tXs*22aP3Eh36fil*dHkqv??tPqYx1+YH6CWlWFfp?$Yq_Id~mUa{#z?eI6qW zI6W=|+LJQ-z0W@OJKaNbr%hh*cT5+!iW(LCBE&+Fan^~yK! z;+XZiKW*){PcX&x7cEoe$JyadZ)^YOtzWwGtNmYRTl@0kz4>+0W*qB8e(oEY`{2bv z{61`d(8UgqPF2BSm_wiA(ItNISUH5OnDlzxNJ<&0CfDrO`+b!znKm};b$^Q!^uEn{XUHc_)jt!vwux*SlcMjA97NZILhi=vzFu> z>WuQ$*R;!>kwq4k@B{tc@31DszrFpVqhtBIdwSBr4>4W#WAwfZ|G+%nPp}udqX%36 z{LtQkADu(-Lr+HeQ&8+Qx*oIfi+D~N|7Ai!jvdmW@#F`09czB+Vbjhw)CWd~ zS6@6RqWZ*2-Q$zaDVrk4TRZH(Jf47+`t#ArK2Q~>QMPMge06~jNTlO(^=^9Bt@Bl$xx?Cv;G)1f;~<2+J7?5V?3 z38U$uVb;t+mya)%8EEtgg0ND}M?$ zz6Jp%a1aQBKY>T9o6p7NK41Om_xLBr{~XrS^7k_St+kca`S^eR@yGx6K4-{cd%2K1 zn>S2;eB}7QUSBU7|JQ44tJe6xd9&X7Q?N20|7ZXE3%u0^EzsJ^N)TlWV|MVxeK3H# z3~CZ6!Z~O%$D;mP5M9Sv#*QvrO}D(vQwQEwKBT0&m zMG+h#e{^;VXU)1y1Z}g6M=xfwPCv>NDu20%@h8F%J`U^BribOSb}-a4gAl9)c@i9+ z9l*CLV)j=!Q~b+cu+pXm#11^X9oF8L%?0>}&6cyy(YKp3lTpmNcMX0F*Mox(Y^*nd zA_vJJ8U`O?^{mV+c#P$sptfAM3$hu}3Z$dQO_hv`MI14pl})n(&*;XVS;hh*H_odM z$4Ry+h{)Rfz?f}x7s!|NV&d)`?H`?V`LOYd>%j+9bny{LCOJ>MNW)8+fkWU9JFnjg zCO1ai7S=ucK{$AZei1nsgt!sVC-f zXXhVWUvt(uJk|3PgT6tF6Idr5G08i-n`*8p5XSnT?VcyeFqp__Deu|J9!Wr%p1_PO zxE6y@mb3~%W4ezy=+!&^MG^6Ii+RLz1| z7~3csS~`~`RzZ+w_`IJpJXp0gIdPi?*9U`v<{g{_2h;MdaUYCPkr;gDp5gjKc!tkNfLZP=gJUyN`(m0T+!SCyfHWpW`+j{Cg4(_~w1bUpKv4T4`?7n+i0W z-pAUP%`qoT%_7+=PAW*{6ncr@8dEj^nuziuQJANIt!g<1xjbZ(P%qm4FdoaX6Bycy z+20+jHt7aKm_cbZ!Lx(W4YmlBqvV$sfaWdfr0k<+BXl#A9146K-|(#y{XfG{!+wTl zkNlLQ_Y}290_tQJ#hF+vE6H{yH>`~$9mFHJ;GNRY8N@1*TdmoEcX!1Rp&Kha>@`OL zR-q4`!zJC_2)qiiZHO$!CB6{Jh6^Ih;569@;f-RbL7Y9~;6tP-3 zuF}F3_=K|xM?w-Yks9^Gd%=Zy3i{X&#S24`29j%NKm)NO1z-KD3uTH8Lxtf{b$n$N z!FQt{@ElAzDNeQyndEfRhRw%3@gdR6U>|1nLKhu20o4gI2*%A}F8h=jgEejoG6ZW3W~1%HbAd3WxozJdZ{bZe?l`uVJv5hRt3Y7WdN10WtSAV7>w_ zz`ZK|`wHaD)e9!%%gQ`yg(ucjsxBZ8Hh$#93m84afuptPGVZW3XxAofAhGFrh@XFz5mGj>4*G`xv1I><87S2*L)L<7LMYYnw`+FiewopKlc|1o3*4YL-2XD^e-Hf%N> ze-cq`0&oWDW~zmX9zg|gjp>&KsxnlK*I|bA!*pP)q3GIVix8*FK&?#gnv{j!$vv0x z2Q_E3kfmuZ*fc|Tm$C&VZ((bl!JQ#;iJ~ScWz(Lm%c!6h$P=EXW9UXx)!MH#C0U0xnU=aWlbP^1{m!Cp_*mR2@$ zJ;_R!SApxv(o=LwTjTNYp7miU)$1~T+N=_|7;2@m)(T`(B5w#Gt%%ff?a3cn|MOIK z9~JdK8!Hw^WEuxzUf)rzCc`FHZJY)a^H91uaDC&NbSm&0rX5g)jGgI zl5uO^g2JTYt-q_!R}~K#Dw}piE*^<}iQu~y94dSdxdyhA{ zqG2KMZ4489$6Vu{-BNAvO87jX?&%kvy|`V*qm3VS&bHg4Tk5P=c|^>4iL=HXe1A`S zx8RQ5XLEohpr%pedpwhfNzIobTpH;Wp+u+`&NY{_x}BTNb^9_}->~`(0E)~NP?4-* z3Xlg=AbasRsEAIvjmrc3&W+WprTZTP2K7t`Ln2I?w zVv+0Ub?X+|v_&a{Q(*IRQ8Oz14JVil1eN=by~PpMAoA??52m$)YZCD_?H{J>`2~&A z!oJqQ*##rK@^~?~!T1I?80Hd`6~!9NjEdb^jW>+r$lQ+Ok#-zK8k?zVF|E>}3orZi z{ye#30@U56tIhe~XmbiRH%5U%#m(I0WNvaYH#wP`oRpZHm^;zqCxP;_QtMiCKqB+K z&IRMK6zhdK1_)|51~nPqo8Ylm6-=$}${227(;{Iu2*@RgvygZVO$|bb##uZhJckR> zta1M7NYJ__8^vhEaD~T5eQ?=Qt8sKZbwTeaT5_IaaU!uH;I;o|Vp2|KGJsZXqw&fk z^~YJZte6_W@7ZeX8yby$|Au3nFrAZ~eM1Ed;&lHG)+K)jCsoQjpj&6lQ|XlT^d9$f z!AAP<;*b5uSY1I43jD}5)FzcAYHR|JqZGtXWS4mwj*W7b`O>Oj0^&dBa{lrS4CKBq zvpD-!=-b-DDQvpcvFTR8MsM%g_hFd#TeF;-rq#i<&@rS8-{sDdb>=iLOV=YARXD|I_on|9ri^vg*G7oZtU^XZJtf*!|O^{Xad<-&2tZOsOBJZ{&ID zo2&H8boyl~ZbQ}Y8tED8esU6YRQ^w{{k;@_mi`hpO}9#{uccm<3j2Vye0{K8 zlquDFPpVODUn07M-*J-8FZ7#4a1DwGe&aOksjx~b4D1U0RMI{^{Ylf>1EfWO3?c#q z=+T!BV{qzfuTpAC>lrf1or(H8xNn)if%|Q!Gn8GOol|gT;TEQ2yJOq7ZQHhO+v+48 z+qR94ZCf4N_$TK~4Q}RUs`kyU+IPF^`)aNAyw56is;A-VOm5l%P4G_Gp~^czD*Ijk zLM+W~Tj2$}A=iGU^3>F(gDmpJjvXXd6|2-(Az_$%LC5O?vduc;E_&DlI6>I_@d~t76{8xtU;V9E9fmr0FV#-0E z$vDM-6=9&_kDV)^7^k5&AHyX)e0&)BJb&j9kgrH1SqNcn)jzxmTK3>U>3e$#>MTS9 z4_>C5wCTVn85LajCw@@qB3O!>)J+O}%8W{X=jAQ~9yUfcCc+N^URO__p8_uc+3z38 z3-T^Bhp{!#7ADysXtP#exZoY|+<;5tym4BR(vC>6yh= z4Ix{DX(5bF(p&eHUZ*v1D)sG+x%s6{Fgvj1IQoqbx$;6D%>yg$3P-QJL(C{iR6E_E z+J(tVDyEOXK^Nq|w#j15MTyhC?Q=%-)$FIR6Ubr~iW|}<$j!aKCC(aXl zs@zKHtUf`JU~|{jt%DJutGzNRKf>=y0E~byfFA$4Tba-vfg0?rPy99>mD1Fp>8^}< zd?~M5kPTiM=^V_0X`}T#3Z^9QdRU}en%*k;1qv!+Ucj|ToZ~dci<)6IW~-X#JN6ml zkxIm}5|sz=isB#QzMG{X?9A&O#vK_kUYGJBjA-arJzzA*HRlMf*)mdm+8gHCI?5rc zSf!D*y~%FnnmbINl@ZucvHHr_yJGs7lo7y^LH46BJ&_QLYZ;=)A6$YN*$MsPq<>yW zxLRuP2o+0%M2-6zQvd!H%f}b}(Nz;Zs|EK2Fhs&Fnv}_VQX1w|*C37$0M{UvAAqUl zjQ*2W8<4A}-3k8iBU@t%r!FsMcS4Vk3MwJUP+k>XjKR?OD?B{3jc1XEzOQ@vwX3W` z7~S33<L$y7o5f{5Ro zZymf#zoOADBZLGS7sRz`LHznwUw|=+NKV`NpE;|R))#w?k%en2?x4eaTPw=>E6>p& zoyI7UYr%43APpT(xQEWY$$Ym1>fUO1k!`-}>58+fJvp^Ms)b5V29ivQ!@=`lrDFKr zWKwIj+llMJHy_ zix~_C{Z}!X8nHGpn#><|TI?fVl%}woJ&RG4X5qFmnOKY_dRr#Iqcl>TJ)B*feMGdG znXy?VyXu!pb7njdvJq~}r)(qomC^o_VoZMsHo~zlCl0`B+?@$Ruf9D`T?7akIZTd4acXZ&CQL<=u|DORek2?f; zs0E+jJ$f&IrF`V!gFnidw{MK{`J7JkZbaV0c!b3j6?6+RhTXUm8bkHYq2Kj^u3WSJ zZecr`s8R!C7~4JGjN=I%ebfTmfN&;w*eK~Z23W_{#7wu>(81o@p>=~X*`5RAvPbS_ zTwRXkOx;7_1+E$FewHjVq@su`5t+$1OgY?@?mXuU9vneSkrov3Q><0)k*o=N>vt(2h^w%ns~)$jJHPlW|EohMDiGQOa>K z{c)#ENx!ZzO}Lu+Fe%FMB3zjE0G+I!);J#-x4$#(s|nrRi}>y2MN)ycWSw!FAB(ty zx9w4>Rv|kdsqv!he5QHi5~~kpX&GXHDw*#BFYEl_ZUBqZ2Qz$K(@9^W>EmBJ*#|)D zg121{K;Nf{DFN`V_We^CK4T##UFZv7?ZS>m#5jblpBxZQyj$^xACID7cohA|2)3RS z-h?@mVVJ~(41t?*q?9^ZG1SU}O;lKu?| zCJQ0#+=3M(*do?a7ht7{w;MO|w)`Kq80MpQD4Izq?hot?#cd@tu9<9qj5kVlAj7f~ zGl;zokCx5V@^9k9b^r~fn4c{5Ot>|DB$aLNk(S#y7L{g4FWKj>c!_xL(AUzC%nWK^Zk z{W?`(50j2bW}8D&+Ps>I#0V0=Dc+ulsv<1ULaQ`q!(6{;(?6YmK+JkPzzNMg(fYkJ zWoGWqk4Z{uRvycNbB{l>SHCP4m=JS{)3v#@F6OZ6sOk}BQ)}d>1OneV;QySzn%3yw z#W8Kb1PrFf>VKAO?hN{WdSE>KDOwOpG$J*@SBx$9yEv0*WtBr^F8}5+d9R z2VaE-<@Axja~NFazH}45ylKDHc>Du7cR^<5-Ir~>+`3nh^Esi@Ab4uT(@?Fu;D(f4 z*zDoGu+HtrVndfiru}yF8tm zr;cw6e8AiIfdEDrJ9$CGmE&l7Kd1+O)mY93mH=(`(K9#h1Kc$k_tZUosRilNkCR56 znxoU0dBYtyQ)(Yi!MoLRX#%zj&*`zCp%NDAZ&3k1MU*RwgE@oj>3&n+akk&d+_pF? zf-G=y$oO1nhF-(NFy#a(|5}F2%JUc$-OBnV&qb>bq|T^YjA&s7s1ymy#)^~Ascpc; zUkxSW!#XcdSLgbco2tR_yjO6Y*J^tG#qp)trSMAS81M1CC{ zCEs5atIHi;V($C)B`@AJ(0Kdv34Uxli>XGNTB{kb=hea1UfW%f8%aP6<}My zndqbl`MN)CZ&h;}kBS-olrEYofXaZw)1tA~A2|e4d~nw*&jtMnMyP(}sXrJrxbs+( zLzL`{0YpOr&;UTyuh4RfY1yth-HHGNkc-c7;dKnKcd973qi{V+Xn+dpdj#E84{Jwi zEBfH=075%Gwok!2^J`4fPwG*p>|O zK;J(a^ND4|=p8S#O$i}O>EA~?gj=|9RJ<6C zmB6~sj}o%%M;36+vEvqNJD8?Ay?b)zk--8iM`P zeS1DzhBZE)Ojh?amMb|2*+pOGF@52ov#x3R78$gNctMMKFz?V+ zLCc|?Tp?lPBB3G~Iy@evG%==A({=`shlNeU4vllZrIOwp{kqP+W+tbi!z6y|+0*+KbEwZJ8!v^L=Dc zQ7xdt9ua|zPslq!mdnUhR>&0@`nijdHihZU&g{ele66SVpWo%zD&`u=W8KOviB50r zDGhI4S}SvFMsh31&MoNwXc+M|pM8DpJZ4r-Y_AYUR`fYxmMHf5!m=+3aUpZd?rzr%bEk4vb8FcM#i?}R+u_Eo)B_Nad!jKS$VvcZ%%Lh;S1|k@L7RE&%8S?m{ z-KCM?lH75JLy!Jv&V2h-w?T7@9ED_=V1a5m{R;vqP{4>O!@s^*e&k&$CdEKZ?bsl@ zI9=>KiCUXL3Ljs7U3~&6(=5bkZN)bv!-3KGXk01>LYV_$@HtAK8P2QlboX9#wr?y- zzb0(@baRC;j_?b$D;x3PA?AKZJx{X!1?3F-$QS2QR=MxwD0MDCm+Z||iQk@t=L7PqTkCC@i_ofFU|0QJX?eAbn=QnK*o zJW*A&bUj{HPoiuWkK!I^=}QDZkQ8wolSr~{jS6ME|COEh1~0T>RP>M*KWUS|-fL9* zBrkp)uMWI+Kmu;u$1v%iZ1u?e?GmU1av|CQsqA)(L1bnC3lk+p5hiX`>Ad9`I**2WyW#sG9y>wH>Ge$8)>|siuJh^X^IM0X zzdc|(chk;GR4eVe(>%#n;s{aMdIA%})(P1TJF;=kx=ub%T&=ZJ#Xq_T)h>U%?_V2R zZ`7JX*aLh8OPOYw%e${wQriVM8@CJbEk?cp-#sUiAtDZYa>7l71WM;kJc8@FIjn zgs@pPuLrTL4ziX zq*#{_I6<$aJITqNHE4FmOxd^An%s0iS>SI!yPG@5zs*1xKVFurcqaR5X}uVvcdC+J z)TP35Ji}h%N!(VUu4@ckG7k>Fhi$78?_hzyOG<kpAmiB}mLB5IP5mB*Xsfgrd*ywwf*(@w{W`;?Jhw}`=iuQu-98EkI zg?$0aBLnZn#(}V-J=eI*7h6t{1GfU}TjeRbMjO>OTM0dAc(lVR_}55b~0 z(_3cCGOz)9uFDQ?Bjj_vwyCsdJ-BIE?j|;u~mr?eb!c{+oWGA_!i}^6f zMxB1?(C0?_X%db^p*U}H)FLWd3MUoX4BUDT0~cse6JoB{*5G-HNKxkIxc4lGcYRlX zROgetS{*txjpu66_o}XG_kGDD;E)-x3AU_6e?|Ycfw6rmaFeij{TtbJTx`{g~mDYsn>bFtSbAz0B9~uO-Gg+%W=Bf2}ZRXo}g%nkSRz`{5e}FPhfkr&mGx=bZ<=4fy-l1N`tWCB`kS{M!Te_VqPql}@#iT96R6pe$O3 zIpGj#F-!LyF?XEaYRvUdb;#W&5OFT$# z!|o4R7}@8+I_3!VNIcww1uL=IH&&;#nADbo=8xWgGCi%3chi=bJ5Af-_)+_)MDJrT z*CzO`DsdEa1Fx0TcOh+po@FrI`e|gkxAnHF>*3*>iXO)+c3qB#q~bBVRpA7C~t{eIi?HV{z$On z;qMzN&mKL`a>S%bAgWPr1#w2q6y*>MF5E>FB$@N3poJ*Mm&o9MIXA+q)VPz@OIJa@8Ac${vIJnVk2}|Y-nQ#t!lU0u3HP{}ZK^L(5701ap z8P$Gh11YemyGT3d^98LdvJ!Clw>gK9@aZ8~AK64b3w0@5f@9=cmK>VIugXAw{NZtw z&OH@7FNHG%S!wLQzaLOPz1`%J@02ojUw}STlEVT=_u*Nz*BT(gg20_#lxo&0UBBn1 zG102i_X>4}ZwntKUX0Niat}ohu#R@D`B99oWk}a)LN1B|ZCJ;&id>{%?U7bh+LK=zvOF8{fmCcEAc6;i0M19(a|@hrzui76a&g;= zt_!y(%ejWL^@ciAyvE1iZlFE&tED}UTua?#niWV=uhvq|nA&|2sQB+41srM$;U?>= zTC&l)0TN^b?{{UXgisq_BsE1uvB?Czq}oy>idfc{Z{J);-DgpVb&mAEAW>}L4QhQg zar{j~h_k;w2E<&?^GUoY)g7AfLrklglQO{|0QWwTXv_&<7iO-_w);9CETKhscbYCKA3ACRoHFl5I8SO=ip&eCcyUNsPeXJ-j zK=3@Rks4(FHDTgpVlqH~UmwNoKN!W$PEO56MD4gBzQx^8%Vmm$CkexS> z0=@AhKR3lA)RduR?s4*#F;7*D@37ohmY(asc5*rO*~C)C%Tw0z<@cPC&9^T*6PqDh zTL-%(mONvYVA7t!R?y@ePUAYhXQCr+q**8B34e{z+uNat@&TehA3HkaqwXFFgIMD@ zZ4RGUz~puI`(Y{{>w92$MlCKsNT&5LR{=MRf0532+nd2jc!$vqyLSkog|KK#QqML2 zIj)LWh@*EySBC{PHepm11laTtx$-Np(T43-%^<=9qZ$@TwM!TS8`q$U0x=gn3|U6n zA_Ql!7&#kN-i3KqW0^NiSGdpOh)lau)HC<2sGauJ%xLA+0ZDX!{jjgbzFSMr?Vzv0 z_~F&ze?NU6uMPveKwB7i7smL86~=*N`YkSNeB0bxRyV=FV<<8ce!KQBs&+9*+9?R7 zXZ@FDa+CsN%363uu)zs)D%}_5mro9W#_Oh(g{cLPsN5sc(25q8S4dAEo>%-gp-6?< zBL(X~;F(VG#9QdCNrAsG4)Is4iUR(TwPU@Q1ACytrHPwNGYKvQ%!;}<@t07P5dXpa z&L1o~9yM`GnE?uVh<%Ao5Rr-D2NQhCV!WW zq$p#ig*s-^VW%}bMpJaExbmexBJe7;fnkMhi&BY;M@R=d(RpUEVdZl5m?O#`ujJ8j z|Iq;e?^lq*5A9HC)Y>K$UvZ{Ic^AVTG9Yj4KH87zGEEK$_C0j?G6BQEx=8~CNfe8I z(0}(|`{QyA48eIeiEcVq@^NmVmuwUr*dYrxfo(ZgP9r&8_%uTyZ-VSv+!1ejUPgHA zU9{iSG)aZyxZEZYJP;{mUkAyJX~bIc;U=^va+N1%@iq>_oH=ouUc|3-xMY2yz?1b; zac~as?4rs4D$x!nwJ_kfmHSgaSF)zj{N0tgexo0o8w=~h?0=H$YH=aup{>w3m}@$_ z_)Fx>N>14ZI8sY{CB;LVz;}B6D~08=E90L6*Lq(T7a--~TWK+%fK!w6+Py3s9nTCt z#DvC5BE4(zR&7(|g1TW$<-<|hE`?*-n-VO~p5UOV+PB7i1F98gpkVA6%Tv}!M^Dvc zw>=2@%ReJ%)K=zqLp+r*3Z9AJMTv(TbxAxU+hH5}&t!kPnWW0rQE8*TBsY8D+OYLl z?~PmEh=-nJvD#ywR;u)@^TYn_Jfz2e#Ynr)pk-{uRtIv=a@XLQ!LnNYHOP zsk2}5XuWDcYh6Qd^@&2j7qw|X>{m1R79Frd80PEB{(s1mCu#EV!w-C)gNS%L?$-O( z?L)y$E1veB$Cs31pd#!OO(is$RM?W&;nX_Hwa8uR{;ev1>!)5*EiJLd*4l^<_c;&G-pzLZ#ZdYh(vR)3P#v$Le3Nj$lLcD6 zx-7b^_qcPV{+9gG_a&7|u{t^HJPOs;{nLwrxemCS?F87_RkwVj+D_dB9cgPFlk4M%fxtA z^XNs1&YHBV>C@DBHhV(s5D zegHgL009v(xOHCU`iD)`os$o@9u4CH^WOkPm)rGY!18k3+k4(5z~v2KQy0LV?d6b7 z{?q-;mBo4m`cjBN%ex5Zy?ZqXPo~|(mtTKZG-hHzt+5PmktR`8no`=O{1Hj9F9_$5S;t89uqDv{@Ks-EWno_}R zie5=Y-t)}}qhe%i)T;wreAPU8`_A3`tB9wS7YhbaF&PfB@#@B9k$NNHnL9$~L+GWf zX8U;@wyL=y^A>>mCR8NQz z!cGs1Mcr`$G9LRViEDfw%QX#20A?7cV|eXfT+ko}NVdZdG!EegIQd>m0#68T=kfM` zlnZha5-k|CLMVgAyVwIFXr+eGpG*^hB=S zVx3}X0U~_RipGT(Xmf3HUU&v9>_Ac3wauZy0)&prUwHgR~*(+_~G z)gNT$Mh5T#wn=%&n$t70^6cM*?8~{a%YAj(W&qZ*{d{+(FyM+=MaI~kVFpfEgF;Ba zeU<0ILqU#eQHH?}ITz8{F)9%vkaU=BP#wIlXdVwysy3>U{`fxw~bJpLz}{o$aRjmI`Lgv>J&Lq?I&? zDN1;->jkK(3=T|k4|r}(z~0Da?T+3XPr4}pM)0H3_LdrFxpIsU+kw;A0q(D3?c;{C z7%Hcf`n3=2eWZdYvL*?xzG0Wo5LkhJe;=D}9Phh|UsL&nJZeb76930&Wj;9gnW~of z%seyE$o$<{7>#@gHOjPr+7uK%d7Pm24w}YCN%02Xa4Jbw>`PG$t{Rgt#Y6Pyh8A&T zcQfT`{&WwkmFljmHfNC8ft7HZAn=M^WLBx?7R*d?HZ%_Vj?AARV*++R#xfIYl2xeG z&*pyd^Cbp~LuKPIlSH7+0ovNNuX~eFY!~U!j zi;nG-?6M4N$<&5a_V}H57{nQe+i_)SC=Sb{__>tLJV(Tl6e>j4u~k7^xxvL^`mmN* z4K?oamCY=8ACQZyh{o)}-bPch1Im?r(WWcsCj~V9kZvjBMZF$)myKJCezwxJuVfv< z?65T1hGoYvX=u+JfX`C_9XG+~lpuk|fHE)m_)hUjc+3;-LtAqMo8VKRDqTG$z1GH= z772|#M4(+@Xr)zBV#g_w#Xp=wjsZ&7Nh5kW$}5Va-A1@Yq-*`Fas` z34+7;#rLh-rDDF+Dwx_@;Pb{~rRFsw!1Qk=(4@lpRws9F#0iE|po}9&kJ()L=3q-sBR<#A%KWC0;J~p+Tszkg6c%uN8at&cY-2DE50m2J-;6;Hg4S(1zcK?!0ylm+W~0e$G*vvq)gYMg%+&wcTLQ;xdTllT7| zIe;%L13!aOg+7~)+0ktvc`bkq&IL~E*X%^r)6X@s`Qx7?QFw!>h(1Q}2+b+JJ2g#N zggbM1W|t;#3489rI?!x$aYSj5%pd%1OdbdR;dGeX-SF{BRbIoo4(;J;*we2l%wm&kIrpHYb2pGJLy(6qsruSjh*;`n*-J=?Mee z*8N$ndi}ZkwfEzys|)jI-^}VGd7YiSMXCb6E=DHKHy{o^Po5@53|6&40!Dxh0R1?+ z0T1txJF*u~20zGP_c*~$_m1YNn7a0yQP9N1`^LqS+SdB_r-hftvJ4}0VW(Kq3&C-l z<{A}LV0K01KP}!)T=7qY@F4??8-uDW=?c$Y0uBD2BUp&t;pp1C&j1;0z*h0a#c#;vWUynO;QOY;(|veiFLZR>9GFqziLik0rm3T|EHSiKBS2<@ z%pIO|g1oJ=1#&1kUqXbF|7?8CdgxVY!1r~{LDfcpzzH1Z*JqpkWWWWD%g4=)+>gis zC?F!ip!zAHQ@V90ZwYzp@l;S^hAWlY>OuGQ1CLeX8tROsEq=Xgp6aILTgK{ z-TWZau{DEwI!(gquvN0@o#T2eqm0b=ogD{)Yi;0?fi13yka5n$E!N0%#ULE!zuTZ} z^KK{L31}OI_4Z~rmyTs^EnOF|#WOgnbb7MlU)dWPLFyinej6rQEiWH@;+Z=u-**Z+ zae;n6;s+ndoS~*#`8tP)vec#zutiK6?JsTsi{JtYN?$o%{T-pKThW3wxXJhq938;i`JOaSaT|_av^Pb`?EqSvg#Bc~txg4?ThC*PK}X z#(BmjtXo2e68n|EjCMC(%VR-__9BHRaH=3BfiW3rJLJjyzU>F^VMImYHtK^W;!wWK zO+`j%f`uNXQ8=pvQWE716J#5-3N`8^CZVeRx#vUUp`e25hGgv^pqZeVS5(&6Du zKQw8j$6?V9l?cNc7{nzn$?C(t@4KahkCIXw+_L;tfbj$(d+rD!Yc^lAh243*ZkqoI zvctS?Go4Xx^ZrqQn5x8%(MLCtIS)TQ6I>#3M`vHu1}^f($1jKrQDyDsyuSPd&AQ6@ zN%LL(JLV3|j+Sn>!g%W^sE@LAFV0r<2?)@JcBlP46Seh6K3C$l4L2Pl(#$sdEN;wG znhUs8A`{NcDIw09F>-kM5y^*U$Pz7*yI_{1pmnPr|b- zoNE1&f2OI{bOfbnxs9frQ9rNE>+i-?7}?sB#UsLwsQPctBDr6-`pT^f-5#vQObICR zg5&y{vJ1t?AZ*cxs`URQ0=K5@7C{LP&;LyQ(O}00lVQ;;z(ZLC-H8XgGV3ALuN?Ba z^J@cDmmpqpgVk(ga!G|T^fFfI zRz%!`&KQh3cK!(85LNI89hs_zTmqR4#^7*}ly0lVD~}eD-Dk~D6IP~8K$#-h7q2se zJx0Jb?eO$8uC({#0#zV7@@+e!kx|`rK8stAeXJsC-R-yv*f#<&-=}s8g z(I@36K)GVUOHm@;?peE#ZI2N`Dj^wm_Q`MHqk|O^nHE4{qw^!P{ zD9$H3Sxw=itc`fbbTLl9_9TrK`Z-RamVlg}^@rU#;^jR^3eIXFNragcG}4 z0d#|?5hXoGHH;ZlFRj%iJGAJWZ$Xg>Wm}C|i#zYe2wPzr$rOx#??V4s{?8dD_rIXQ zSIcI~%4k4M;L=22{2G@P&gV@<+g3?bYvrak}b$`NkIJ+wy8x zVHLocT8lbM?=tjoJUR8GkP8uY)g(@#U{Mpv*3!6#kr^puFeMi2k;ls3OZv7z8Ad!K z67~umBv~Rc?xG>4uUzVK<`ya@aeLfC!?5`gHniY@<>*X#(F zoxzs?(N$0XT}Zchiq&)f-_EvY|C25Zec}%PW%gk-FD}cmA58rXpKjj^jt0xF->JhS z`gC5XIW*I7S-LZL@G7h89tipLe*nYAzW+C1m_qpj7(Ss2qN0x(Nqucnkuwe$zqiqw zFSy(2sAW6D7Sa>5>N<@ND5*PA>)9z!AqP2o^s>Gj7*oq2HT7~4mUSE2QL07LW9U{-Jfov-@owL z7M#sSAz^t&{v+7lTtEh!Zf|bIzBx^YcX0aGRZ^g3!BtV&=I{&VNCV+mj zLMD3hK9w?m{x+ISGe{beUkvD23#zEUd=r`Nk;h364=hl25ATnC%X->y;8n4ifJsxi zdGs#?O`-_fnO$Ik^DheoCjE8!`#s3~#m~Mv?(ba<4fUO=-GG@dKnkCc@4-v7%|HP) zWuZ=#@E%U>y9>LumC1l;^K@T4K6|TEN8-mA1mz(E@1iq>8@8|^B z4*K5`dgoVH4mm4sa)JSiwN&!^*sw*{DI8%NE`MVRty}qXUP>oKMB`^>{7+xoU0!=V zy8GPLu2-%qL%06H9QgQlKrMw^QA$N#`$xVH;rods!-Dq8)8`do67<4^+od#hlGoZCD1gNRQJ#2FH!W^pG9n%s1$L3mdw*mAO?)p|llj6654wBO z6%HW#9OUWq7njc8TyQrTv?@5{Uy6*9Bh5mc1B2sSjH;AXZQ4}hXgr>jNS(#pyY5@& zAatpJSv!Lu4Ys}Wnn!hBX5C3!nQLAdnX1bLf0$wH6{eSP42G? z%?_R>2O25^d^A2(ISozV#EOCR-EM)Rqf<#uH(?OhPf8FFx1I?`IrWBWz(Yvuv`lT* ziW3CbhUJv_UgwjkB>tF%OhO^#Xy>SycrJV6)rP(saT8*L3TMoW&-4~J_itWx`J_)! z=18KAAn5AA4vb{lAM%Idh0-@h!BNG8 z2l`2IqOG?J+c3z9zv-`8JR~CMsbR?>#y({aLK<4Zghq^c)2QpB4R$%bi_x=Qx_L$| z_PnjTHGe%3S4zG);JyU-S@-oJz5q701Dcp#03U3}0ZiYYdHAau0%DhS)bD*O@0D!d zI6pt>rO)MUce&^HB+FF+nuDSMYDcr!z;6Sw-y45!k_w)uUdCsRZx;1F{T;Y5gUB`6 zur-9AS+Ja_R6VWAYtvtmw&r}6!0*}3H?Bx%(u(iQc#Vne?NVY>50E+3v6r6EzXm%) zYy9gfY6uAMA;U;U2svR%m5|C1(cBXD7|(nrEr=7{1L5=|JHiEV@wYCcS1Leyy1ugFZYqh-Dqwi wlfCVJ9ACNQY5o*mZ@Zrk-yDD}z%<|_;GysT|5f4p1psOo6^#w#2nF;%0K%I|j{pDw literal 0 HcmV?d00001 diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 693c3be747424..185654a4cdd4d 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -2146,6 +2146,17 @@ if(webgui) endif() install(DIRECTORY ${CMAKE_BINARY_DIR}/ui5/distribution/ DESTINATION ${CMAKE_INSTALL_OPENUI5DIR}/distribution/ COMPONENT libraries FILES_MATCHING PATTERN "*") endif() + ExternalProject_Add( + RENDERCORE + URL ${CMAKE_SOURCE_DIR}/builtins/rendercore/RenderCore.tar.gz + URL_HASH SHA256=a0b1cc0d4e8d739b113ace87e33de77572cf019772899549cb082088943513e1 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + SOURCE_DIR ${CMAKE_BINARY_DIR}/ui5/eve7/rcore + TIMEOUT 600 + ) + install(DIRECTORY ${CMAKE_BINARY_DIR}/ui5/eve7/rcore/ DESTINATION ${CMAKE_INSTALL_OPENUI5DIR}/eve7/rcore/ COMPONENT libraries FILES_MATCHING PATTERN "*") endif() #------------------------------------------------------------------------------------ diff --git a/graf3d/eve7/inc/ROOT/REveCalo.hxx b/graf3d/eve7/inc/ROOT/REveCalo.hxx index bb8f2133aafdf..4b5b775541302 100644 --- a/graf3d/eve7/inc/ROOT/REveCalo.hxx +++ b/graf3d/eve7/inc/ROOT/REveCalo.hxx @@ -72,6 +72,7 @@ public: virtual ~REveCaloViz(); + bool RequiresExtraSelectionData() const override { return true; }; virtual REveElement* ForwardSelection(); virtual REveElement* ForwardEdit(); diff --git a/graf3d/eve7/inc/ROOT/REveDataCollection.hxx b/graf3d/eve7/inc/ROOT/REveDataCollection.hxx index ca5776243045d..20c808c490f34 100644 --- a/graf3d/eve7/inc/ROOT/REveDataCollection.hxx +++ b/graf3d/eve7/inc/ROOT/REveDataCollection.hxx @@ -116,6 +116,7 @@ class REveDataCollection : public REveElement { private: REveDataItemList* fItemList{nullptr}; + int fLayer{0}; public: typedef std::vector Ids_t; @@ -138,8 +139,11 @@ public: TClass *GetItemClass() const { return fItemClass; } - void SetItemClass(TClass *cls) { fItemClass = cls; - } + void SetItemClass(TClass *cls) { fItemClass = cls;} + + int GetLayer() const { return fLayer; } + void SetLayer(int i) { fLayer = i; } + REveDataItemList* GetItemList() {return fItemList;} void SetFilterExpr(const char* filter); diff --git a/graf3d/eve7/inc/ROOT/REveDataProxyBuilderBase.hxx b/graf3d/eve7/inc/ROOT/REveDataProxyBuilderBase.hxx index 641e0b54c8cca..3bb6ccde993d7 100644 --- a/graf3d/eve7/inc/ROOT/REveDataProxyBuilderBase.hxx +++ b/graf3d/eve7/inc/ROOT/REveDataProxyBuilderBase.hxx @@ -93,7 +93,6 @@ protected: private: REveDataCollection *m_collection{nullptr}; - float m_layer{0.}; bool m_haveWindow{false}; bool m_modelsChanged{false}; }; diff --git a/graf3d/eve7/inc/ROOT/REveDigitSet.hxx b/graf3d/eve7/inc/ROOT/REveDigitSet.hxx index 580a736d55498..6b1f8ac162d66 100644 --- a/graf3d/eve7/inc/ROOT/REveDigitSet.hxx +++ b/graf3d/eve7/inc/ROOT/REveDigitSet.hxx @@ -182,7 +182,7 @@ public: void NewShapePicked(int shapeId, Int_t selectionId, bool multi); - bool RequiresExtraSelectionData() const override { return true; }; + bool RequiresExtraSelectionData() const override { return GetAlwaysSecSelect(); }; void FillExtraSelectionData(nlohmann::json& j, const std::set& secondary_idcs) const override; Int_t WriteCoreJson(nlohmann::json &j, Int_t rnr_offset) override; diff --git a/graf3d/eve7/inc/ROOT/REveManager.hxx b/graf3d/eve7/inc/ROOT/REveManager.hxx index 022de4f4d7fbc..e0b35de7bf996 100644 --- a/graf3d/eve7/inc/ROOT/REveManager.hxx +++ b/graf3d/eve7/inc/ROOT/REveManager.hxx @@ -155,6 +155,7 @@ protected: Logger fLogger; REveServerStatus fServerStatus; + bool fIsRCore{false}; void WindowConnect(unsigned connid); void WindowData(unsigned connid, const std::string &arg); @@ -253,6 +254,9 @@ public: static void ExecuteInMainThread(std::function func); static void QuitRoot(); + static void ErrorHandler(Int_t level, Bool_t abort, const char *location, + const char *msg); + // Access to internals, needed for low-level control in advanced // applications. @@ -266,6 +270,7 @@ public: void Show(const RWebDisplayArgs &args = ""); void GetServerStatus(REveServerStatus&); + bool IsRCore() const { return fIsRCore; } }; R__EXTERN REveManager* gEve; diff --git a/graf3d/eve7/inc/ROOT/REvePointSet.hxx b/graf3d/eve7/inc/ROOT/REvePointSet.hxx index c09ccbbd8be94..520e5726c5b10 100644 --- a/graf3d/eve7/inc/ROOT/REvePointSet.hxx +++ b/graf3d/eve7/inc/ROOT/REvePointSet.hxx @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,8 @@ namespace Experimental { class REvePointSet : public REveElement, public REveProjectable, public TAttMarker, - public TAttBBox + public TAttBBox, + public REveSecondarySelectable { friend class REvePointSetArray; @@ -45,6 +47,7 @@ protected: std::vector fPoints; int fCapacity{0}; int fSize{0}; + int fTexX{0}, fTexY{0}; public: REvePointSet(const std::string& name="", const std::string& title="", Int_t n_points = 0); diff --git a/graf3d/eve7/inc/ROOT/REveRenderData.hxx b/graf3d/eve7/inc/ROOT/REveRenderData.hxx index cb33c190e3df3..9b9c4a019c1d3 100644 --- a/graf3d/eve7/inc/ROOT/REveRenderData.hxx +++ b/graf3d/eve7/inc/ROOT/REveRenderData.hxx @@ -90,6 +90,8 @@ public: const std::string GetRnrFunc() const { return fRnrFunc; } + void ResizeV(int s) { fVertexBuffer.resize(s); } + int SizeV() const { return fVertexBuffer.size(); } int SizeN() const { return fNormalBuffer.size(); } int SizeI() const { return fIndexBuffer.size(); } @@ -98,6 +100,8 @@ public: int GetBinarySize() { return (SizeV() + SizeN() + SizeT()) * sizeof(float) + SizeI() * sizeof(int); } int Write(char *msg, int maxlen); + + static void CalcTextureSize(int nel, int align, int &sx, int &sy); }; } // namespace Experimental diff --git a/graf3d/eve7/inc/ROOT/REveSelection.hxx b/graf3d/eve7/inc/ROOT/REveSelection.hxx index 6bf052fe30841..8d5e6fbe01ff2 100644 --- a/graf3d/eve7/inc/ROOT/REveSelection.hxx +++ b/graf3d/eve7/inc/ROOT/REveSelection.hxx @@ -90,8 +90,9 @@ protected: Color_t fHiddenEdgeColor; /// fPickToSelect; ///& RefPickToSelect() const { return fPickToSelect; } void ClearPickToSelect() { fPickToSelect.clear(); } void AddPickToSelect(int ps) { fPickToSelect.push_back(ps); } - Bool_t GetIsMaster() const { return fIsMaster; } - void SetIsMaster(Bool_t m) { fIsMaster = m; } + bool GetIsMaster() const { return fIsMaster; } + void SetIsMaster(bool m) { fIsMaster = m; } + bool GetIsHighlight() const { return fIsHighlight; } + void SetIsHighlight(bool m) { fIsHighlight = m; } std::shared_ptr GetDeviator() const { return fDeviator; } void SetDeviator(std::shared_ptr d) { fDeviator = d; } diff --git a/graf3d/eve7/src/REveCalo.cxx b/graf3d/eve7/src/REveCalo.cxx index 9aadc7ba8c4f1..c3d04be414481 100644 --- a/graf3d/eve7/src/REveCalo.cxx +++ b/graf3d/eve7/src/REveCalo.cxx @@ -1393,13 +1393,12 @@ void REveCalo2D::MakeRPhiCell(Float_t phiMin, Float_t phiMax, pnts[4] = r2*Cos(phiMax); pnts[5] = r2*Sin(phiMax); pnts[6] = r1*Cos(phiMax); pnts[7] = r1*Sin(phiMax); - Float_t x, y, z; for (Int_t i = 0; i < 4; ++i) { pntsOut[i*3] = pnts[2*i]; pntsOut[i*3+1] = pnts[2*i+1]; pntsOut[i*3+2] = 0.f; - fManager->GetProjection()->ProjectPoint(x, y, z, fDepth); + fManager->GetProjection()->ProjectPoint(pntsOut[3*i], pntsOut[3*i+1], pntsOut[3*i + 2], fDepth); } } diff --git a/graf3d/eve7/src/REveDataProxyBuilderBase.cxx b/graf3d/eve7/src/REveDataProxyBuilderBase.cxx index 875db7cd2bb58..8f7958f27f5b8 100644 --- a/graf3d/eve7/src/REveDataProxyBuilderBase.cxx +++ b/graf3d/eve7/src/REveDataProxyBuilderBase.cxx @@ -105,7 +105,7 @@ void REveDataProxyBuilderBase::Build() { REveProjectionManager *pmgr = projectedProduct->GetManager(); Float_t oldDepth = pmgr->GetCurrentDepth(); - pmgr->SetCurrentDepth(m_layer); + pmgr->SetCurrentDepth(m_collection->GetLayer()); Int_t cnt = 0; REveElement *projectedProductAsElement = projectedProduct->GetProjectedAsElement(); // printf("projectedProduct children %d, product children %d\n", projectedProductAsElement->NumChildren(), product->NumChildren()); diff --git a/graf3d/eve7/src/REveDigitSet.cxx b/graf3d/eve7/src/REveDigitSet.cxx index 59d2e3df39802..bf9804aa3c327 100644 --- a/graf3d/eve7/src/REveDigitSet.cxx +++ b/graf3d/eve7/src/REveDigitSet.cxx @@ -504,7 +504,7 @@ int REveDigitSet::GetShapeIdxFromAtomIdx(int iAtomIdx) const } } - printf("REveDigitSet::GetShapeIdxFromAtomIdx:: Atom with idx %d dose not have a visible shape \n", iAtomIdx); + printf("REveDigitSet::GetShapeIdxFromAtomIdx:: Atom with idx %d does not have a visible shape \n", iAtomIdx); return -1; } diff --git a/graf3d/eve7/src/REveManager.cxx b/graf3d/eve7/src/REveManager.cxx index f0def3d183695..a8dd8830f08ff 100644 --- a/graf3d/eve7/src/REveManager.cxx +++ b/graf3d/eve7/src/REveManager.cxx @@ -106,10 +106,12 @@ REveManager::REveManager() fSelectionList->IncDenyDestroy(); fWorld->AddElement(fSelectionList); fSelection = new REveSelection("Global Selection", "", kRed, kViolet); + fSelection->SetIsMaster(true); fSelection->IncDenyDestroy(); fSelectionList->AddElement(fSelection); fHighlight = new REveSelection("Global Highlight", "", kGreen, kCyan); - fHighlight->SetHighlightMode(); + fHighlight->SetIsMaster(true); + fHighlight->SetIsHighlight(true); fHighlight->IncDenyDestroy(); fSelectionList->AddElement(fHighlight); @@ -142,13 +144,16 @@ REveManager::REveManager() fWebWindow->UseServerThreads(); fWebWindow->SetDefaultPage("file:rootui5sys/eve7/index.html"); - const char *gl_viewer = gEnv->GetValue("WebEve.GLViewer", "Three"); + const char *gl_viewer = gEnv->GetValue("WebEve.GLViewer", "RCore"); const char *gl_dblclick = gEnv->GetValue("WebEve.DblClick", "Off"); Int_t htimeout = gEnv->GetValue("WebEve.HTimeout", 250); Int_t table_row_height = gEnv->GetValue("WebEve.TableRowHeight", 0); fWebWindow->SetUserArgs(Form("{ GLViewer: \"%s\", DblClick: \"%s\", HTimeout: %d, TableRowHeight: %d }", gl_viewer, gl_dblclick, htimeout, table_row_height)); + if (strcmp(gl_viewer, "RCore") == 0) + fIsRCore = true; + // this is call-back, invoked when message received via websocket fWebWindow->SetCallBacks([this](unsigned connid) { WindowConnect(connid); }, [this](unsigned connid, const std::string &arg) { WindowData(connid, arg); }, @@ -158,6 +163,10 @@ REveManager::REveManager() fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue fMIRExecThread = std::thread{[this] { MIRExecThread(); }}; + + // activate interpreter error report + gInterpreter->ReportDiagnosticsToErrorHandler(); + SetErrorHandler(ErrorHandler); } //////////////////////////////////////////////////////////////////////////////// @@ -939,20 +948,25 @@ void REveManager::PublishChanges() jobj["content"] = "EndChanges"; if (!gEveLogEntries.empty()) { - constexpr static int numLevels = static_cast(ELogLevel::kDebug) + 1; constexpr static std::array sTag{ - {"{unset-error-level please report}", "FATAL", "Error", "Warning", "Info", "Debug"}}; + {"{unset-error-level please report}", "FATAL", "Error", "Warning", "Info", "Debug"}}; + jobj["log"] = nlohmann::json::array(); std::stringstream strm; for (auto entry : gEveLogEntries) { + nlohmann::json item = {}; + item["lvl"] = entry.fLevel; int cappedLevel = std::min(static_cast(entry.fLevel), numLevels - 1); - strm << sTag[cappedLevel]; + strm << "Server " << sTag[cappedLevel] << ":"; + if (!entry.fLocation.fFuncName.empty()) strm << " " << entry.fLocation.fFuncName; strm << " " << entry.fMessage; + item["msg"] = strm.str(); + jobj["log"].push_back(item); + strm.clear(); } - jobj["log"] = strm.str(); gEveLogEntries.clear(); } @@ -1112,6 +1126,18 @@ REveManager::ChangeGuard::~ChangeGuard() gEve->EndChange(); } +// Error handler streams error-level messages to client log +void REveManager::ErrorHandler(Int_t level, Bool_t abort, const char * location, const char *msg) +{ + if (level >= kError) + { + RLogEntry entry(ELogLevel::kError, REveLog()); + entry.fMessage = msg; + gEveLogEntries.emplace_back(entry); + } + ::DefaultErrorHandler(level, abort, location, msg); +} + /** \class REveManager::RExceptionHandler \ingroup REve Exception handler for Eve exceptions. diff --git a/graf3d/eve7/src/REvePointSet.cxx b/graf3d/eve7/src/REvePointSet.cxx index 5d1fbf7969ee0..6af817cb3efd9 100644 --- a/graf3d/eve7/src/REvePointSet.cxx +++ b/graf3d/eve7/src/REvePointSet.cxx @@ -67,6 +67,8 @@ REvePointSet::REvePointSet(const REvePointSet& e) : TAttMarker(e), TAttBBox(e) { + fAlwaysSecSelect = e.GetAlwaysSecSelect(); + ClonePoints(e); } //////////////////////////////////////////////////////////////////////////////// @@ -208,10 +210,19 @@ TClass* REvePointSet::ProjectedClass(const REveProjection*) const Int_t REvePointSet::WriteCoreJson(nlohmann::json& j, Int_t rnr_offset) { + if (gEve->IsRCore()) + REveRenderData::CalcTextureSize(fSize, 1, fTexX, fTexY); + Int_t ret = REveElement::WriteCoreJson(j, rnr_offset); + if (gEve->IsRCore()) { + j["fSize"] = fSize; + j["fTexX"] = fTexX; + j["fTexY"] = fTexY; + } j["fMarkerSize"] = GetMarkerSize(); j["fMarkerColor"] = GetMarkerColor(); + j["fSecondarySelect"] = fAlwaysSecSelect; return ret; } @@ -223,8 +234,17 @@ void REvePointSet::BuildRenderData() { if (fSize > 0) { - fRenderData = std::make_unique("makeHit", 3*fSize); - fRenderData->PushV(&fPoints[0].fX, 3*fSize); + if (gEve->IsRCore()) { + fRenderData = std::make_unique("makeHit", 4*fTexX*fTexY); + for (int i = 0; i < fSize; ++i) { + fRenderData->PushV(&fPoints[i].fX, 3); + fRenderData->PushV(0); + } + fRenderData->ResizeV(4*fTexX*fTexY); + } else { + fRenderData = std::make_unique("makeHit", 3*fSize); + fRenderData->PushV(&fPoints[0].fX, 3*fSize); + } } } @@ -544,6 +564,8 @@ void REvePointSetProjected::UpdateProjection() REvePointSet &ps = * dynamic_cast(fProjectable); REveTrans *tr = ps.PtrMainTrans(kFALSE); + fAlwaysSecSelect = ps.GetAlwaysSecSelect(); + // XXXX rewrite Int_t n = ps.GetSize(); diff --git a/graf3d/eve7/src/REveRenderData.cxx b/graf3d/eve7/src/REveRenderData.cxx index 10516ac79c5fe..f4dd21b6d2207 100644 --- a/graf3d/eve7/src/REveRenderData.cxx +++ b/graf3d/eve7/src/REveRenderData.cxx @@ -80,3 +80,21 @@ void REveRenderData::SetMatrix(const double *arr) fMatrix.push_back(arr[i]); } } + +///////////////////////////////////////////////////////////////////////////////////////// +/// Calculate texture dimensions to hold nel elements with given alignment on x axis. +/// Static function. +/// +/// Implementation could be improved -- now it simply goes for near-square-root(size) +/// dimensions that satisfy the alignment requirement. + +void REveRenderData::CalcTextureSize(int nel, int align, int &sx, int &sy) +{ + if (nel <= 0) { sx = sy = 0; return; } + + sx = (int) std::ceil(std::sqrt(nel)); + int rx = sx % align; + if (rx > 0) sx += align - rx; + sy = nel / sx; + if (nel % sx > 0) sy += 1; +} diff --git a/graf3d/eve7/src/REveSelection.cxx b/graf3d/eve7/src/REveSelection.cxx index fa0efce754b6c..86ddbaa89d7be 100644 --- a/graf3d/eve7/src/REveSelection.cxx +++ b/graf3d/eve7/src/REveSelection.cxx @@ -39,9 +39,7 @@ REveSelection::REveSelection(const std::string& n, const std::string& t, Color_t col_visible, Color_t col_hidden) : REveElement (n, t), fVisibleEdgeColor (col_visible), - fHiddenEdgeColor (col_hidden), - fActive (kTRUE), - fIsMaster (kTRUE) + fHiddenEdgeColor (col_hidden) { // Managing complete selection state on element level. // @@ -86,18 +84,6 @@ void REveSelection::SetHiddenEdgeColorRGB(UChar_t r, UChar_t g, UChar_t b) StampObjProps(); } -//////////////////////////////////////////////////////////////////////////////// -/// Set to 'highlight' mode. - -void REveSelection::SetHighlightMode() -{ - // Most importantly, this sets the pointers-to-function-members in - // REveElement that are used to mark elements as (un)selected and - // implied-(un)selected. - - fIsMaster = kFALSE; -} - //////////////////////////////////////////////////////////////////////////////// /// Select element indicated by the entry and fill its /// implied-selected set. @@ -350,7 +336,7 @@ void REveSelection::ActivateSelection() { if (fActive) return; - fActive = kTRUE; + fActive = true; for (auto i = fMap.begin(); i != fMap.end(); ++i) { DoElementSelect(i); SelectionAdded(i->first); @@ -368,7 +354,7 @@ void REveSelection::DeactivateSelection() DoElementUnselect(i); } SelectionCleared(); - fActive = kFALSE; + fActive = false; } //////////////////////////////////////////////////////////////////////////////// @@ -699,6 +685,8 @@ Int_t REveSelection::WriteCoreJson(nlohmann::json &j, Int_t /* rnr_offset */) j["fVisibleEdgeColor"] = fVisibleEdgeColor; j["fHiddenEdgeColor"] = fHiddenEdgeColor; + j["fIsMater"] = fIsMaster; + j["fIsHighlight"] = fIsHighlight; nlohmann::json sel_list = nlohmann::json::array(); @@ -715,7 +703,8 @@ Int_t REveSelection::WriteCoreJson(nlohmann::json &j, Int_t /* rnr_offset */) // XXX if not empty ??? for (auto &imp_el : i.second.f_implied) { imp.push_back(imp_el->GetElementId()); - imp_el->FillExtraSelectionData(rec["extra"], sec); + if (imp_el->RequiresExtraSelectionData()) + imp_el->FillExtraSelectionData(rec["extra"], sec); } rec["implied"] = imp; @@ -728,7 +717,7 @@ Int_t REveSelection::WriteCoreJson(nlohmann::json &j, Int_t /* rnr_offset */) rec["sec_idcs"] = sec; // stream tooltip in highlight type - if (!fIsMaster) + if (fIsHighlight) rec["tooltip"] = i.first->GetHighlightTooltip(i.second.f_sec_idcs); sel_list.push_back(rec); diff --git a/graf3d/eve7/src/REveStraightLineSet.cxx b/graf3d/eve7/src/REveStraightLineSet.cxx index 2ff64a80cc34e..8556a6901156c 100644 --- a/graf3d/eve7/src/REveStraightLineSet.cxx +++ b/graf3d/eve7/src/REveStraightLineSet.cxx @@ -11,6 +11,7 @@ #include #include +#include #include #include "TClass.h" @@ -162,12 +163,21 @@ Int_t REveStraightLineSet::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset) j["fLinePlexSize"] = fLinePlex.Size(); j["fMarkerPlexSize"] = fMarkerPlex.Size(); + // j["fLineColor"] = fLineColor; // streamed as fMainColor j["fLineWidth"] = fLineWidth; j["fLineStyle"] = fLineStyle; + j["fMarkerColor"] = fMarkerColor; j["fMarkerSize"] = fMarkerSize; j["fMarkerStyle"] = fMarkerStyle; j["fSecondarySelect"] = fAlwaysSecSelect; + if (fMarkerSize && gEve->IsRCore()) { + int x, y; + int ms = fMarkerPlex.Size(); + REveRenderData::CalcTextureSize(ms, 1, x, y); + j["fTexX"] = x; + j["fTexY"] = y; + } return ret; } diff --git a/tutorials/eve7/boxset.C b/tutorials/eve7/boxset.C index fda5793fa970c..48e424d098372 100644 --- a/tutorials/eve7/boxset.C +++ b/tutorials/eve7/boxset.C @@ -38,14 +38,16 @@ REveBoxSet* boxset(Int_t num=100) #define RND_BOX(x) (Float_t)r.Uniform(-(x), (x)) - Float_t verts[24]; - for (Int_t i=0; iSetMainTransparency(60); jet->SetLineColor(kRed); - jet->SetCylinder(129 - 10, 268.36 - 10); + jet->SetCylinder(kR_min - 10, kZ_d - 10); jet->AddEllipticCone(eta, phi, deta, dphi); jet->SetPickable(kTRUE); jet->SetHighlightFrame(kFALSE); @@ -75,11 +75,12 @@ void calorimeters() auto b1 = new REveGeoShape("Barrel 1"); b1->SetShape(new TGeoTube(kR_min, kR_max, kZ_d)); b1->SetMainColor(kCyan); + b1->SetNSegments(80); eveMng->GetGlobalScene()->AddElement(b1); auto calo3d = new REveCalo3D(data); - calo3d->SetBarrelRadius(kR_max); - calo3d->SetEndCapPos(kZ_d); + calo3d->SetBarrelRadius(kR_max + 1); + calo3d->SetEndCapPos(kZ_d + 1); calo3d->SetMaxTowerH(300); eveMng->GetEventScene()->AddElement(calo3d); @@ -91,5 +92,3 @@ void calorimeters() eveMng->Show(); } - - diff --git a/tutorials/eve7/event_demo.C b/tutorials/eve7/event_demo.C index dd3e64005ac88..a7128b05ab5eb 100644 --- a/tutorials/eve7/event_demo.C +++ b/tutorials/eve7/event_demo.C @@ -61,7 +61,7 @@ REX::REvePointSet *getPointSet(int npoints = 2, float s=2, int color=28) ps->SetNextPoint(r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s)); ps->SetMarkerColor(color); - ps->SetMarkerSize(3+r.Uniform(1, 7)); + ps->SetMarkerSize(8 + r.Uniform(1, 8)); ps->SetMarkerStyle(4); return ps; } @@ -81,6 +81,7 @@ void addPoints() auto ps2 = getPointSet(10, 200, 4); ps2->SetName("Points_2"); ps2->SetTitle("Points_2 title"); // used as tooltip + ps2->SetAlwaysSecSelect(true); pntHolder->AddElement(ps2); event->AddElement(pntHolder); @@ -135,12 +136,12 @@ void addJets() for (int i = 0; i < N_Jets; i++) { auto jet = new REX::REveJetCone(Form("Jet_%d", i)); - jet->SetTitle(Form("Jet_%d title", i)); // used as tooltip + jet->SetTitle(Form("Jet_%d\n pT = %.2f", i, r.Uniform(1, 40))); // used as tooltip jet->SetCylinder(2*kR_max, 2*kZ_d); jet->AddEllipticCone(r.Uniform(-3.5, 3.5), r.Uniform(0, TMath::TwoPi()), r.Uniform(0.02, 0.2), r.Uniform(0.02, 0.3)); jet->SetFillColor(kPink - 8); - jet->SetLineColor(kViolet - 7); + jet->SetLineColor(kBlack); jetHolder->AddElement(jet); } @@ -159,6 +160,7 @@ void makeGeometryScene() auto b1 = new REX::REveGeoShape("Barrel 1"); b1->SetShape(new TGeoTube(kR_min, kR_max, kZ_d)); b1->SetMainColor(kCyan); + b1->SetNSegments(80); eveMng->GetGlobalScene()->AddElement(b1); // Debug of surface fill in RPhi (index buffer screwed). @@ -199,16 +201,22 @@ void projectScenes(bool geomp, bool eventp) { for (auto &ie : eveMng->GetGlobalScene()->RefChildren()) { + mngRhoPhi->SetCurrentDepth(0); mngRhoPhi->ImportElements(ie, rPhiGeomScene); + mngRhoZ ->SetCurrentDepth(0); mngRhoZ ->ImportElements(ie, rhoZGeomScene); } } if (eventp) { + int depth = 50; for (auto &ie : eveMng->GetEventScene()->RefChildren()) { + mngRhoPhi->SetCurrentDepth(depth); mngRhoPhi->ImportElements(ie, rPhiEventScene); + mngRhoZ ->SetCurrentDepth(depth); mngRhoZ ->ImportElements(ie, rhoZEventScene); + depth -= 10; } } @@ -252,12 +260,7 @@ public: auto scene = eveMng->GetEventScene(); scene->DestroyElements(); makeEventScene(); - for (auto &ie : scene->RefChildren()) { - if (mngRhoPhi) - mngRhoPhi->ImportElements(ie, rPhiEventScene); - if (mngRhoZ) - mngRhoZ->ImportElements(ie, rhoZEventScene); - } + projectScenes(false, true); // if (++fCount % 10 == 0) printf("At event %d\n", fCount); } diff --git a/tutorials/eve7/lineset.C b/tutorials/eve7/lineset.C index b40bfc03893d6..2c6f931f01afa 100644 --- a/tutorials/eve7/lineset.C +++ b/tutorials/eve7/lineset.C @@ -26,11 +26,11 @@ REX::REveStraightLineSet* makeLineSet(Int_t nlines = 40, Int_t nmarkers = 4, boo ls->AddLine( r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s)); // add random number of markers - Int_t nm = Int_t(nmarkers* r.Rndm()); + Int_t nm = 1 + Int_t(nmarkers * r.Rndm()); for (Int_t m = 0; m < nm; m++) ls->AddMarker(i, r.Rndm()); } - ls->SetMarkerSize(0.5); + ls->SetMarkerSize(5); ls->SetMarkerStyle(1); ls->SetAlwaysSecSelect(sc); REX::gEve->GetEventScene()->AddElement(ls); @@ -42,12 +42,14 @@ void lineset(bool secondarySelect = true) { auto eveMng = REX::REveManager::Create(); - auto ls1 = makeLineSet(10, 5, secondarySelect); + auto ls1 = makeLineSet(50, 10, secondarySelect); ls1->SetMainColor(kViolet); + ls1->SetMarkerColor(kGreen + 3); ls1->SetName("LineSet_1"); - auto ls2 = makeLineSet(300, 4, secondarySelect); + auto ls2 = makeLineSet(200, 20, secondarySelect); ls2->SetMainColor(kBlue); + ls2->SetMarkerColor(kCyan + 2); ls2->SetName("LineSet_2"); ls2->InitMainTrans(); ls2->RefMainTrans().Move3LF(40, 200, 200); diff --git a/tutorials/eve7/points.C b/tutorials/eve7/points.C index 8e51cedaa0e77..914b465a90fff 100644 --- a/tutorials/eve7/points.C +++ b/tutorials/eve7/points.C @@ -23,7 +23,7 @@ REX::REvePointSet *createPointSet(int npoints = 2, float s = 2, int color = 28) ps->SetNextPoint(r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s)); ps->SetMarkerColor(color); - ps->SetMarkerSize(3+r.Uniform(1, 2)); + ps->SetMarkerSize(5 + r.Uniform(1, 15)); ps->SetMarkerStyle(4); return ps; } @@ -33,7 +33,7 @@ void points() auto eveMng = REX::REveManager::Create(); REX::REveElement *event = eveMng->GetEventScene(); - auto ps = createPointSet(100); + auto ps = createPointSet(100, 300); event->AddElement(ps); eveMng->Show(); diff --git a/tutorials/eve7/projection_prescale.C b/tutorials/eve7/projection_prescale.C index 51640dcc3f88c..c161c72f6293e 100644 --- a/tutorials/eve7/projection_prescale.C +++ b/tutorials/eve7/projection_prescale.C @@ -84,7 +84,7 @@ TGeoNode* getNodeFromPath( TGeoNode* top, std::string path) } -void projection_prescale(std::string type = "RhPhi") +void projection_prescale(std::string type = "RPhi") { eveMng = REX::REveManager::Create(); diff --git a/ui5/eve7/controller/ClientLog.controller.js b/ui5/eve7/controller/ClientLog.controller.js index f1b2e10987f7f..7dd58c8e70a9d 100644 --- a/ui5/eve7/controller/ClientLog.controller.js +++ b/ui5/eve7/controller/ClientLog.controller.js @@ -14,11 +14,6 @@ sap.ui.define([ return Controller.extend("rootui5.eve7.controller.ClientLog", { onInit: function () { // create any data and a model and set it to the view - let oLink = new Link({ - text: "Show more information", - href: "http://sap.com", - target: "_blank" - }); let oMessageTemplate = new MessageItem({ type: '{type}', @@ -26,8 +21,7 @@ sap.ui.define([ activeTitle: "{active}", description: '{description}', subtitle: '{subtitle}', - counter: '{counter}', - link: oLink + counter: '{counter}' }); this.oMessageView = new MessageView({ diff --git a/ui5/eve7/controller/EveTable.controller.js b/ui5/eve7/controller/EveTable.controller.js index 43bcd792a04e3..ddc3162308c7a 100644 --- a/ui5/eve7/controller/EveTable.controller.js +++ b/ui5/eve7/controller/EveTable.controller.js @@ -34,8 +34,6 @@ sap.ui.define([ let args = oEvent.getParameter("arguments"); this.setupManagerAndViewType(EVE.$eve7tmp.eveViewerId, EVE.$eve7tmp.mgr); delete EVE.$eve7tmp; - - this.checkViewReady(); }, setupManagerAndViewType: function(eveViewerId, mgr) { diff --git a/ui5/eve7/controller/GL.controller.js b/ui5/eve7/controller/GL.controller.js index 88f2a32261aaa..f06e03a57a16a 100644 --- a/ui5/eve7/controller/GL.controller.js +++ b/ui5/eve7/controller/GL.controller.js @@ -220,7 +220,9 @@ sap.ui.define([ onResize: function(event) { // TODO: should be specified somehow in XML file - this.getView().$().css("overflow", "hidden").css("width", "100%").css("height", "100%"); + if (this.viewer_class != "RCore") { + this.getView().$().css("overflow", "hidden").css("width", "100%").css("height", "100%"); + } if (this.resize_tmout) clearTimeout(this.resize_tmout); diff --git a/ui5/eve7/controller/Main.controller.js b/ui5/eve7/controller/Main.controller.js index 9a842b2989696..b28922b0ce3fa 100644 --- a/ui5/eve7/controller/Main.controller.js +++ b/ui5/eve7/controller/Main.controller.js @@ -92,20 +92,6 @@ sap.ui.define(['sap/ui/core/Component', let toolbar = pthis.byId("otb1"); toolbar.addContentRight(logCtrl.getButton()); }); - consoleObj.alert = true; - EVE.alert = function (oText) - { - if (consoleObj.alert) - { - MessageBox.error(oText, { - actions: ["Stop Alerts", MessageBox.Action.CLOSE], - onClose: function (sAction) - { - if (sAction == "Stop Alerts") { consoleObj.alert = false; } - } - }); - } - }; }, UpdateCommandsButtons: function(cmds) { diff --git a/ui5/eve7/css/eve.css b/ui5/eve7/css/eve.css index 9d06f2881673d..b12df129e2562 100644 --- a/ui5/eve7/css/eve.css +++ b/ui5/eve7/css/eve.css @@ -82,7 +82,12 @@ white-space: pre; } +#posDatGUI +{ + position: relative; + float: right; +} .eveNoSelectionCheckBox .sapMLIBSelectM { display: none; -} \ No newline at end of file +} diff --git a/ui5/eve7/eve.mjs b/ui5/eve7/eve.mjs index 046bf47e27379..80da8aae61ce3 100644 --- a/ui5/eve7/eve.mjs +++ b/ui5/eve7/eve.mjs @@ -14,6 +14,7 @@ function initEVE(source_dir) { import(mpath+'base/colors.mjs'), import(mpath+'base/base3d.mjs'), import(mpath+'geom/geobase.mjs'), + import(mpath+'gui/dat.gui.mjs'), import(mpath+'geom/TGeoPainter.mjs')]) .then(arr => { globalThis.THREE = Object.assign({}, arr.shift()); diff --git a/ui5/eve7/lib/EveElements.js b/ui5/eve7/lib/EveElements.js index 9e1d8cbb9dcbe..563edec36f87e 100644 --- a/ui5/eve7/lib/EveElements.js +++ b/ui5/eve7/lib/EveElements.js @@ -27,7 +27,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(/*EveManager*/) { let s = this.obj3d.scene; if (s && (typeof s[fname] == "function")) - return s[fname](this.obj3d, arg, this.event); + return s[fname](this.obj3d.eve_el, arg, this.event); return false; } @@ -595,6 +595,8 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(/*EveManager*/) { constructor() { } + GenerateTypeName(obj) { return "THREE." + obj.type; } + /** Test if render data has vertex buffer. Make logging if not. Only for debug purposes */ TestRnr(name, obj, rnrData) { diff --git a/ui5/eve7/lib/EveElementsRCore.js b/ui5/eve7/lib/EveElementsRCore.js index d17468ebde608..05beebf349657 100644 --- a/ui5/eve7/lib/EveElementsRCore.js +++ b/ui5/eve7/lib/EveElementsRCore.js @@ -9,48 +9,467 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) // EveElemControl //============================================================================== - class EveElemControl { - + class EveElemControl + { - constructor(o3d) + constructor(iobj, tobj) { - this.obj3d = o3d; + this.invoke_obj = iobj; + this.top_obj = tobj ? tobj : iobj; } - invokeSceneMethod(fname, arg) + invokeSceneMethod(fname, arg, event) { - if ( ! this.obj3d) return false; + if ( ! this.top_obj || ! this.top_obj.eve_el) return false; - let s = this.obj3d.scene; + let s = this.top_obj.scene; if (s && (typeof s[fname] == "function")) - return s[fname](this.obj3d, arg, this.event); + return s[fname](this.top_obj.eve_el, arg, event); return false; } - getTooltipText(intersect) + getTooltipText() { - let el = this.obj3d.eve_el; + let el = this.top_obj.eve_el; return el.fTitle || el.fName || ""; } - elementHighlighted(indx) + extractIndex(instance) + { + return instance; + } + + elementHighlighted(indx, event) { // default is simple selection, we ignore the indx - this.invokeSceneMethod("processElementHighlighted"); // , indx); + return this.invokeSceneMethod("processElementHighlighted", indx, event); } - elementSelected(indx) + elementSelected(indx, event) { // default is simple selection, we ignore the indx - this.invokeSceneMethod("processElementSelected"); //, indx); + return this.invokeSceneMethod("processElementSelected", indx, event); + } + + DrawForSelection(sec_idcs, res) + { + if (this.top_obj.eve_el.fSecondarySelect) { + if (sec_idcs.length > 0) { + res.instance_object = this.top_obj; + res.instance_sec_idcs = sec_idcs; + // this.invoke_obj.outlineMaterial.outline_instances_setup(sec_idcs); + } else { + // this.invoke_obj.outlineMaterial.outline_instances_reset(); + } + } + else + { + res.geom.push(this.top_obj); + } } } // class EveElemControl - /// is it necessary? - EveElemControl.prototype.separateDraw = false; + + // =================================================================================== + // Digit sets control classes + // =================================================================================== + + class BoxSetControl extends EveElemControl + { + DrawForSelection(xsec_idcs, res, extra) + { + let sec_idcs = extra.shape_idcs; + + let body = new RC.Geometry(); + body._vertices = this.top_obj.geometry._vertices; + let origIndices = this.top_obj.geometry._indices; + + let protoIdcsLen = 3 * 12; + let indicesPerDigit = 8; + if (this.top_obj.eve_el.boxType == 6) { + protoIdcsLen = 3 * 24; + indicesPerDigit = 14; + } + + let idxBuff = []; + + let N = this.top_obj.eve_el.render_data.idxBuff.length / 2; + for (let b = 0; b < sec_idcs.length; ++b) { + let idx = sec_idcs[b]; + let idxOff = idx * indicesPerDigit; + for (let i = 0; i < protoIdcsLen; i++) { + idxBuff.push(idxOff + origIndices.array[i]); + } + } + + body.indices = RC.Uint32Attribute(idxBuff, 1); + let mesh = new RC.Mesh(body, null); + mesh._modelViewMatrix = this.invoke_obj._modelViewMatrix; + mesh._normalMatrix = this.invoke_obj._normalMatrix; + mesh._material = this.invoke_obj._material; + + res.geom.push(mesh); + } + + extractIndex(instance) { + let verticesPerDigi = 8; + if (this.top_obj.eve_el.boxType == 6) + verticesPerDigi = 14; + let idx = Math.floor(instance / verticesPerDigi); + return idx; + } + + elementSelectedSendMIR(idx, selectionId, event) + { + let boxset = this.top_obj.eve_el; + let scene = this.top_obj.scene; + let multi = event?.ctrlKey ? true : false; + + let boxIdx = idx; + + let fcall = "NewShapePicked(" + boxIdx + ", " + selectionId + ", " + multi + ")" + scene.mgr.SendMIR(fcall, boxset.fElementId, "ROOT::Experimental::REveDigitSet"); + return true; + } + + elementSelected(idx, event) + { + return this.elementSelectedSendMIR(idx, this.top_obj.scene.mgr.global_selection_id, event); + } + + elementHighlighted(idx, event) + { + return this.elementSelectedSendMIR(idx, this.top_obj.scene.mgr.global_highlight_id, event); + } + + checkHighlightIndex(idx) // XXXX ?? MT Sept-2022 + { + if (this.top_obj && this.top_obj.scene) + return this.invokeSceneMethod("processCheckHighlight", idx); + + return true; // means index is different + } + + } // class BoxSetControl + + + // =================================================================================== + // Calorimeter control classes + // =================================================================================== + + class Calo3DControl extends EveElemControl + { + DrawForSelection(sec_idcs, res, extra) + { + console.log("CALO 3d draw for selection ", extra); + let eve_el = this.invoke_obj.eve_el; + // locate REveCaloData cells for this object + let cells; + for (let i = 0; i < extra.length; i++) { + if (extra[i].caloVizId == eve_el.fElementId) { + cells = extra[i].cells; + break; + } + } + + let rnr_data = eve_el.render_data; + let ibuff = rnr_data.idxBuff; + let nbox = ibuff.length / 2; + let nBoxSelected = parseInt(cells.length); + let boxIdcs = []; + for (let i = 0; i < cells.length; i++) { + let tower = cells[i].t; + let slice = cells[i].s; + + for (let r = 0; r < nbox; r++) { + if (ibuff[r * 2] == slice && ibuff[r * 2 + 1] == tower) { + boxIdcs.push(r); + break; + } + } + } + let protoIdcs = [0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 1, 2, 3, 1, 3, 0, 4, 7, 6, 4, 6, 5]; + let idxBuff = []; + let vtxBuff = new Float32Array(nBoxSelected * 8 * 3); + for (let i = 0; i < nBoxSelected; ++i) + { + let box_idx = boxIdcs[i]; + for (let c = 0; c < 8; c++) { + let off = i * 24 + c * 3; + let pos = box_idx * 24 + c * 3; + vtxBuff[off] = rnr_data.vtxBuff[pos]; + vtxBuff[off + 1] = rnr_data.vtxBuff[pos + 1]; + vtxBuff[off + 2] = rnr_data.vtxBuff[pos + 2]; + } + + // fix top corners, select can be partial + for (let c = 0; c < 4; c++) { + // fix vertex 1 + let pos = box_idx * 24 + c * 3; + let v1x = rnr_data.vtxBuff[pos]; + let v1y = rnr_data.vtxBuff[pos + 1]; + let v1z = rnr_data.vtxBuff[pos + 2]; + pos += 12; + let v2x = rnr_data.vtxBuff[pos]; + let v2y = rnr_data.vtxBuff[pos + 1]; + let v2z = rnr_data.vtxBuff[pos + 2]; + + let off = i * 24 + 12 + c * 3; + vtxBuff[off] = v1x + cells[i].f * (v2x - v1x); + vtxBuff[off + 1] = v1y + cells[i].f * (v2y - v1y); + vtxBuff[off + 2] = v1z + cells[i].f * (v2z - v1z); + } + + for (let c = 0; c < 36; c++) { + let off = i * 8; + idxBuff.push(protoIdcs[c] + off); + } + } // loop boxes + + let body = new RC.Geometry(); + body.indices = RC.Uint32Attribute(idxBuff, 1); + body.vertices = new RC.BufferAttribute(vtxBuff, 3); // this.invoke_obj.geometry.vertices; + body.computeVertexNormals(); // XX should not need it when we have dFdx/y + + let mesh = new RC.Mesh(body, null); + mesh._modelViewMatrix = this.invoke_obj._modelViewMatrix; + mesh._normalMatrix = this.invoke_obj._normalMatrix; + mesh._material = this.invoke_obj._material; + + res.geom.push(mesh); + + console.log(body, mesh, res); + } + + extractIndex(instance) + { + return Math.floor(instance / 8); + } + + getTooltipText(idx) + { + // let t = this.obj3d.eve_el.fTitle || this.obj3d.eve_el.fName || ""; + let eve_el = this.top_obj.eve_el; + let val = eve_el.render_data.nrmBuff[idx]; + let idxBuff = eve_el.render_data.idxBuff; + let caloData = this.top_obj.scene.mgr.GetElement(eve_el.dataId); + let slice = idxBuff[idx*2]; + + let vbuff = eve_el.render_data.vtxBuff; + let p = idx*24; + let x = vbuff[p]; + let y = vbuff[p+1]; + let z = vbuff[p+2]; + + let phi = Math.acos(x/Math.sqrt(x*x+y*y)); + let cosTheta = z/Math.sqrt(x*x + y*y + z*z); + let eta = 0; + if (cosTheta*cosTheta < 1) + { + eta = -0.5* Math.log( (1.0-cosTheta)/(1.0+cosTheta) ); + } + + return caloData.sliceInfos[slice].name + "\n" + Math.floor(val*100)/100 + + " ("+ Math.floor(eta*100)/100 + ", " + Math.floor(phi*100)/100 + ")"; + } + + elementSelected(idx, event) + { + let calo = this.top_obj.eve_el; + let idxBuff = calo.render_data.idxBuff; + let scene = this.top_obj.scene; + let selectionId = scene.mgr.global_selection_id; + let multi = event?.ctrlKey ? true : false; + let fcall = "NewTowerPicked(" + idxBuff[idx*2 + 1] + ", " + idxBuff[idx*2] + ", " + + selectionId + ", " + multi + ")"; + scene.mgr.SendMIR(fcall, calo.fElementId, "ROOT::Experimental::REveCalo3D"); + return true; + } + + elementHighlighted(idx, event) + { + let calo = this.top_obj.eve_el; + let idxBuff = calo.render_data.idxBuff; + let scene = this.top_obj.scene; + let selectionId = scene.mgr.global_highlight_id; + let fcall = "NewTowerPicked(" + idxBuff[idx*2 + 1] + ", " + idxBuff[idx*2] + ", " + selectionId + ", false)"; + scene.mgr.SendMIR(fcall, calo.fElementId, "ROOT::Experimental::REveCalo3D"); + } + + checkHighlightIndex(idx) + { + if (this.top_obj && this.top_obj.scene) + { + console.log("check highlight idx ?????? \n"); + return this.invokeSceneMethod("processCheckHighlight", idx); + + } + + return true; // means index is different + } + + } // class Calo3DControl + class Calo2DControl extends EveElemControl + { + DrawForSelection(sec_idcs, res, extra) + { + let eve_el = this.invoke_obj.eve_el; + let cells; + for (let i = 0; i < extra.length; i++) { + if (extra[i].caloVizId == eve_el.fElementId) { + cells = extra[i].cells; + break; + } + } + let rnr_data = eve_el.render_data; + let ibuff = rnr_data.idxBuff; + let vbuff = rnr_data.vtxBuff; + let nbox = ibuff.length / 2; + let nBoxSelected = cells.length; + let boxIdcs = []; + for (let i = 0; i < cells.length; i++) { + let bin = cells[i].b; + let slice = cells[i].s; + // let fraction = cells[i].f; + for (let r = 0; r < nbox; r++) { + if (ibuff[r * 2] == slice) { + + if (bin > 0 && ibuff[r * 2 + 1] == bin) { + boxIdcs.push(r); + break; + } else if (bin < 0 && ibuff[r * 2 + 1] == Math.abs(bin) && vbuff[r * 12 + 1] < 0) { + boxIdcs.push(r); + break; + } + } + } + } + let idxBuff = []; + let vtxBuff = new Float32Array(nBoxSelected * 4 * 3); + let protoIdcs = [0, 1, 2, 2, 3, 0]; + for (let i = 0; i < nBoxSelected; ++i) { + let BoxIdcs = boxIdcs[i]; + for (let v = 0; v < 4; v++) { + let off = i * 12 + v * 3; + let pos = BoxIdcs * 12 + v * 3; + vtxBuff[off] = rnr_data.vtxBuff[pos]; + vtxBuff[off + 1] = rnr_data.vtxBuff[pos + 1]; + vtxBuff[off + 2] = rnr_data.vtxBuff[pos + 2]; + } + { + // fix vertex 1 + let pos = BoxIdcs * 12; + let v1x = rnr_data.vtxBuff[pos]; + let v1y = rnr_data.vtxBuff[pos + 1]; + pos += 3; + let v2x = rnr_data.vtxBuff[pos]; + let v2y = rnr_data.vtxBuff[pos + 1]; + let off = i * 12 + 3; + vtxBuff[off] = v1x + cells[i].f * (v2x - v1x); + vtxBuff[off + 1] = v1y + cells[i].f * (v2y - v1y); + } + + { + // fix vertex 2 + let pos = BoxIdcs * 12 + 3 * 3; + let v1x = rnr_data.vtxBuff[pos]; + let v1y = rnr_data.vtxBuff[pos + 1]; + pos -= 3; + let v2x = rnr_data.vtxBuff[pos]; + let v2y = rnr_data.vtxBuff[pos + 1]; + let off = i * 12 + 3 * 2; + vtxBuff[off] = v1x + cells[i].f * (v2x - v1x); + vtxBuff[off + 1] = v1y + cells[i].f * (v2y - v1y); + } + for (let c = 0; c < 6; c++) { + let off = i * 4; + idxBuff.push(protoIdcs[c] + off); + } + } + + let body = new RC.Geometry(); + body.indices = RC.Uint32Attribute(idxBuff, 1); + body.vertices = new RC.BufferAttribute(vtxBuff, 3); + body.computeVertexNormals(); + + let mesh = new RC.Mesh(body, null); + mesh._modelViewMatrix = this.invoke_obj._modelViewMatrix; + mesh._normalMatrix = this.invoke_obj._normalMatrix; + mesh._material = this.invoke_obj._material; + + res.geom.push(mesh); + } + + extractIndex(instance) + { + return Math.floor(instance / 4); + } + + getTooltipText(idx) + { + let eve_el = this.top_obj.eve_el; + let idxBuff = eve_el.render_data.idxBuff; + // let bin = idxBuff[idx*2 + 1]; + let val = eve_el.render_data.nrmBuff[idx]; + let caloData = this.top_obj.scene.mgr.GetElement(eve_el.dataId); + let slice = idxBuff[idx*2]; + let sname = caloData.sliceInfos[slice].name; + + let vbuff = eve_el.render_data.vtxBuff; + let p = idx*12; + let x = vbuff[p]; + let y = vbuff[p+1]; + // let z = vbuff[p+2]; + + if (eve_el.isRPhi) { + let phi = Math.acos(x/Math.sqrt(x*x+y*y)) * Math.sign(y); + return sname + " " + Math.floor(val*100)/100 + + " ("+ Math.floor(phi*100)/100 + ")"; + } + else + { + let cosTheta = x/Math.sqrt(x*x + y*y), eta = 0; + if (cosTheta*cosTheta < 1) + { + eta = -0.5* Math.log( (1.0-cosTheta)/(1.0+cosTheta) ); + } + + return sname + " " + Math.floor(val*100)/100 + + " ("+ Math.floor(eta*100)/100 + ")"; + } + } + + elementSelectedSendMIR(idx, selectionId, event) + { + let calo = this.top_obj.eve_el; + let idxBuff = calo.render_data.idxBuff; + let scene = this.top_obj.scene; + let multi = event?.ctrlKey ? true : false; + let bin = idxBuff[idx*2 + 1]; + let slice = idxBuff[idx*2]; + // get sign for the case of RhoZ projection + if (calo.render_data.vtxBuff[idx*12 + 1] < 0) bin = -bin ; + + let fcall = "NewBinPicked(" + bin + ", " + slice + ", " + selectionId + ", " + multi + ")" + scene.mgr.SendMIR(fcall, calo.fElementId, "ROOT::Experimental::REveCalo2D"); + return true; + } + + elementSelected(idx, event) + { + return this.elementSelectedSendMIR(idx, this.top_obj.scene.mgr.global_selection_id, event); + } + + elementHighlighted(idx, event) + { + return this.elementSelectedSendMIR(idx, this.top_obj.scene.mgr.global_highlight_id, event); + } + + } // class Calo2Control + //============================================================================== // EveElements @@ -65,40 +484,124 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) } //------------------------------------------------------------------------------ + // Builder functions of this class are called by EveScene to create RCore + // objects representing an EveElement. They can have children if multiple RCore + // objects are required (e.g., mesh + lines + points). + // + // The top-level object returned by these builder functions will get additional + // properties injected by EveScene: + // - eve_el + // - scene. + // + // Object picking functions in GlViewerRCore will navigate up the parent hierarchy + // until an object with eve_el property is set. + // If secondary selection is enabled on the eve_el, instance picking will be called + // as well and the returned ID will be used as the index for secondary selection. + // This can be overriden by setting get_ctrl property of any RCore object to a function + // that takes a reference to the said argument and returns an instance of class + // EveElemControl. + // get_ctrl property needs to be set at least at the top-level object. + + class EveElements + { + constructor(rc, viewer) + { + if (viewer._logLevel >= 2) + console.log("EveElements -- RCore instantiated."); - class EveElements { + RC = rc; + this.viewer = viewer; - constructor(rc) { - console.log("EveElements -- RCore"); + RC.Cache.enabled = true; - RC = rc; + this.tex_cache = new RC.TextureCache; this.POINT_SIZE_FAC = 1; this.LINE_WIDTH_FAC = 1; + this.ColorWhite = new RC.Color(0xFFFFFF); + this.ColorBlack = new RC.Color(0x000000); } - SetupPointLineFacs(pf, lf) + //---------------------------------------------------------------------------- + // Helper functions + //---------------------------------------------------------------------------- + + GenerateTypeName(obj) { return "RC." + obj.type; } + + SetupPointLineFacs(ssaa, pf, lf) { + this.SSAA = ssaa; // to scale down points / lines for picking and outlines this.POINT_SIZE_FAC = pf; this.LINE_WIDTH_FAC = lf; } + UpdatePointPickingMaterial(obj) + { + let m = obj.material; + let p = obj.pickingMaterial; + p.usePoints = m.usePoints; + p.pointSize = m.pointSize; + p.pointsScale = m.pointsScale; + p.drawCircles = m.drawCircles; + } + + RcCol(root_col) + { + return RcCol(root_col); + } + + RcPointMaterial(color, opacity, point_size, props) + { + let mat = new RC.PointBasicMaterial; + mat._color = this.ColorBlack; // color; + mat._emissive = color; + if (opacity !== undefined && opacity < 1.0) { + mat._opacity = opacity; + mat._transparent = true; + mat._depthWrite = false; + } + mat._pointSize = this.POINT_SIZE_FAC; + if (point_size !== undefined) mat._pointSize *= point_size; + if (props !== undefined) { + mat.update(props); + } + return mat; + } + RcLineMaterial(color, opacity, line_width, props) { let mat = new RC.MeshBasicMaterial; // StripeBasicMaterial - mat._color = color; - if (opacity !== undefined && opacity < 1.0) - { + mat._color = this.ColorBlack; + mat._emissive = color; + if (opacity !== undefined && opacity < 1.0) { mat._opacity = opacity; mat._transparent = true; mat._depthWrite = false; } - if (line_width !== undefined) - { - mat._lineWidth = this.LINE_WIDTH_FAC * line_width; + mat.lineWidth = this.LINE_WIDTH_FAC; + if (line_width !== undefined) mat.lineWidth *= line_width; + if (props !== undefined) { + mat.update(props); } - if (props !== undefined) - { + return mat; + } + + RcFlatMaterial(color, opacity, props) + { + let mat = new RC.MeshBasicMaterial; + mat._color = color; + mat._emissive = color; // mat.emissive.offsetHSL(0, 0.1, 0); + // Something is strange here. Tried also white (no change) / black (no fill -- ?). + // mat._emissive = new RC.Color(color); + // mat.emissive.multiplyScalar(0.1); + // offsetHSL(0, -0.5, -0.5); + + if (opacity !== undefined && opacity < 1.0) { + mat._opacity = opacity; + mat._transparent = true; + mat._depthWrite = false; + } + if (props !== undefined) { mat.update(props); } return mat; @@ -110,28 +613,110 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) // let mat = new RC.MeshBasicMaterial; mat._color = color; - if (opacity !== undefined && opacity < 1.0) - { + mat._specular = new RC.Color(0.3, 0.4, 0.3); // this.ColorWhite; + mat._shininess = 64; + + if (opacity !== undefined && opacity < 1.0) { mat._opacity = opacity; mat._transparent = true; mat._depthWrite = false; } - if (props !== undefined) - { + if (props !== undefined) { mat.update(props); } return mat; } - RcPickable(el, obj3d, ctrl_class = EveElemControl) + RcMakeZSprite(colIdx, sSize, nInstance, vbuff, instX, instY, textureName) + { + let col = RcCol(colIdx); + sSize *= this.POINT_SIZE_FAC; + let sm = new RC.ZSpriteBasicMaterial( { + SpriteMode: RC.SPRITE_SPACE_SCREEN, SpriteSize: [sSize, sSize], + color: this.ColorBlack, + emissive: col, + diffuse: col.clone().multiplyScalar(0.5) } ); + sm.transparent = true; + + sm.addInstanceData(new RC.Texture(vbuff, + RC.Texture.WRAPPING.ClampToEdgeWrapping, RC.Texture.WRAPPING.ClampToEdgeWrapping, + RC.Texture.FILTER.NearestFilter, RC.Texture.FILTER.NearestFilter, + // RC.Texture.FORMAT.R32F, RC.Texture.FORMAT.R32F, RC.Texture.TYPE.FLOAT, + RC.Texture.FORMAT.RGBA32F, RC.Texture.FORMAT.RGBA, RC.Texture.TYPE.FLOAT, + instX, instY)); + sm.instanceData[0].flipy = false; + + let s = new RC.ZSprite(null, sm); + s.frustumCulled = false; // need a way to speciy bounding box/sphere !!! + s.instanced = true; + s.instanceCount = nInstance; + + // Now that outline and picking shaders are setup with final pixel-size, + // scale up the main size to account for SSAA. + sSize *= this.SSAA; + sm.setUniform("SpriteSize", [sSize, sSize]); + + this.GetLumAlphaTexture(textureName, this.AddMapToAllMaterials.bind(this, s)); + + return s; + } + + RcMakeStripes(geom, line_width, line_color) + { + // Setup width for SSAA, scaled down for picking and outline materials. + let s = new RC.Stripes( + { geometry: new RC.StripesGeometry({ baseGeometry: geom }), + material: new RC.StripesBasicMaterial({ + baseGeometry: geom, mode: RC.STRIPE_SPACE_SCREEN, + lineWidth: line_width * this.LINE_WIDTH_FAC * this.SSAA, + color: this.ColorBlack, + emissive: line_color + }), + GBufferMaterial: null + } + ); + s.lights = false; + return s; + } + + RcApplyStripesMaterials(eve_el, stripes, pick_width_scale = 2) + { + if (eve_el.fPickable) { + let m = stripes.material; + stripes.pickingMaterial = new RC.StripesBasicMaterial( + { lineWidth: m.lineWidth * pick_width_scale / this.SSAA, + mode: m.mode, color: m.color }); + let pm = stripes.pickingMaterial; + pm.programName = "custom_GBufferMini_stripes"; + pm.addSBFlag("PICK_MODE_UINT"); + pm.prevVertex = m.prevVertex; + pm.nextVertex = m.nextVertex; + pm.deltaOffset = m.deltaOffset; + + stripes.outlineMaterial = new RC.StripesBasicMaterial( + { lineWidth: m.lineWidth / this.SSAA, mode: m.mode, color: m.color }); + let om = stripes.outlineMaterial; + om.programName = "custom_GBufferMini_stripes"; + om.prevVertex = m.prevVertex; + om.nextVertex = m.nextVertex; + om.deltaOffset = m.deltaOffset; + } + } + + RcPickable(el, obj3d, do_children = true, ctrl_class = EveElemControl) { if (el.fPickable) { - obj3d.get_ctrl = function() { return new ctrl_class(obj3d); } - obj3d.colorID = el.fElementId; - // console.log("YES Pickable for", el.fElementId, el.fName) + if (ctrl_class) { + obj3d.get_ctrl = function(iobj, tobj) { return new ctrl_class(iobj, tobj); } + } + obj3d.pickable = true; + if (do_children) { + for (let i = 0; i < obj3d.children.length; ++i) + obj3d.children[i].pickable = true; + } + // using RCore auto-id to get Object3D that got picked. return true; } else { - // console.log("NOT Pickable for", el.fElementId, el.fName) return false; } } @@ -146,6 +731,52 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) return true; } + GetLumAlphaTexture(name, callback) + { + let url = this.viewer.eve_path + 'textures/' + name; + + this.tex_cache.deliver(url, + callback, + (image) => { + return new RC.Texture + (image, RC.Texture.WRAPPING.ClampToEdgeWrapping, RC.Texture.WRAPPING.ClampToEdgeWrapping, + RC.Texture.FILTER.LinearFilter, RC.Texture.FILTER.LinearFilter, + RC.Texture.FORMAT.LUMINANCE_ALPHA, RC.Texture.FORMAT.LUMINANCE_ALPHA, + RC.Texture.TYPE.UNSIGNED_BYTE, + image.width, image.height); + }, + () => { this.viewer.request_render() } + ); + } + + GetRgbaTexture(name, callback) + { + let url = this.viewer.eve_path + 'textures/' + name; + + this.tex_cache.deliver(url, + callback, + (image) => { + return new RC.Texture + (image, RC.Texture.WRAPPING.ClampToEdgeWrapping, RC.Texture.WRAPPING.ClampToEdgeWrapping, + RC.Texture.FILTER.LinearFilter, RC.Texture.FILTER.LinearFilter, + RC.Texture.FORMAT.RGBA, RC.Texture.FORMAT.RGBA, + RC.Texture.TYPE.UNSIGNED_BYTE, + image.width, image.height); + }, + () => { this.viewer.request_render() } + ); + } + + AddMapToAllMaterials(o3d, tex) + { + if (o3d.material) o3d.material.addMap(tex); + if (o3d.pickingMaterial) o3d.pickingMaterial.addMap(tex); + if (o3d.outlineMaterial) o3d.outlineMaterial.addMap(tex); + } + + //---------------------------------------------------------------------------- + // Builder functions + //---------------------------------------------------------------------------- //============================================================================== // makeHit @@ -155,32 +786,15 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) { if (this.TestRnr("hit", hit, rnr_data)) return null; - let geo = new RC.Geometry(); - geo.vertices = new RC.BufferAttribute(rnr_data.vtxBuff, 3); - - let size = 2 * this.POINT_SIZE_FAC * hit.fMarkerSize; // scaled by distance down to half size (basic_template.vert) - let col = RcCol(hit.fMarkerColor); - - let mat = new RC.MeshBasicMaterial; - mat.color = col; - mat.pointSize = size; - mat.usePoints = true; - mat.drawCircles = true; - - let pnts = new RC.Point(geo, mat); - - let pm = pnts.pickingMaterial; - pm.pointSize = size; - pm.usePoints = true; - pm.drawCircles = true; + let s = this.RcMakeZSprite(hit.fMarkerColor, hit.fMarkerSize, hit.fSize, + rnr_data.vtxBuff, hit.fTexX, hit.fTexY, + "star5-32a.png"); - // mesh.get_ctrl = function() { return new EveElemControl(this); } + this.RcPickable(hit, s); - this.RcPickable(hit, pnts); - return pnts; + return s; } - //============================================================================== // makeTrack //============================================================================== @@ -190,10 +804,10 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) if (this.TestRnr("track", track, rnr_data)) return null; let N = rnr_data.vtxBuff.length / 3; - let track_width = 2 * (track.fLineWidth || 1) * this.LINE_WIDTH_FAC; + let track_width = 2 * track.fLineWidth; let track_color = RcCol(track.fLineColor); - if (EVE.JSR.browser.isWin) track_width = 1; // not supported on windows + // if (EVE.JSR.browser.isWin) track_width = 1; // not supported on windows let buf = new Float32Array((N - 1) * 6), pos = 0; for (let k = 0; k < (N - 1); ++k) @@ -218,7 +832,8 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) buf[pos + 3] = rnr_data.vtxBuff[k * 3]; buf[pos + 4] = rnr_data.vtxBuff[k * 3 + 1]; buf[pos + 5] = rnr_data.vtxBuff[k * 3 + 2]; - } else + } + else { buf[pos + 3] = rnr_data.vtxBuff[k * 3 + 3]; buf[pos + 4] = rnr_data.vtxBuff[k * 3 + 4]; @@ -228,32 +843,13 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) pos += 6; } - let style = (track.fLineStyle > 1) ? EVE.JSR.getSvgLineStyle(track.fLineStyle) : "", - dash = style ? style.split(",") : [], - lineMaterial; - - if (dash && (dash.length > 1)) - { - lineMaterial = this.RcLineMaterial(track_color, 1.0, track_width, { dashSize: parseInt(dash[0]), gapSize: parseInt(dash[1]) }); - } else - { - lineMaterial = this.RcLineMaterial(track_color, 1.0, track_width); - } - - let geom = new RC.Geometry(); - geom.vertices = new RC.BufferAttribute(buf, 3); - - let line = new RC.Line(geom, lineMaterial); - line.renderingPrimitive = RC.LINES; - line.lineWidth = track_width; - - // required for the dashed material - //if (dash && (dash.length > 1)) - // line.computeLineDistances(); - - //line.hightlightWidthScale = 2; - + const geom = new RC.Geometry(); + geom.vertices = new RC.Float32Attribute(buf, 3); + + const line = this.RcMakeStripes(geom, track_width, track_color); + this.RcApplyStripesMaterials(track, line, 2); this.RcPickable(track, line); + return line; } @@ -280,7 +876,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) // idcs.push( 0, i, i + 1 ); } geo_body.indices = new RC.BufferAttribute(idcs, 1); - geo_body.computeVertexNormals(); + // geo_body.computeVertexNormals(); let geo_rim = new RC.Geometry(); geo_rim.vertices = pos_ba; @@ -302,17 +898,18 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) let lcol = RcCol(jet.fLineColor); let mesh = new RC.Mesh(geo_body, this.RcFancyMaterial(mcol, 0.5, { side: RC.FRONT_AND_BACK_SIDE })); + mesh.material.normalFlat = true; let line1 = new RC.Line(geo_rim, this.RcLineMaterial(lcol, 0.8, 4)); + line1.renderingPrimitive = RC.LINE_LOOP; let line2 = new RC.Line(geo_rays, this.RcLineMaterial(lcol, 0.8, 1)); line2.renderingPrimitive = RC.LINES; mesh.add(line1); mesh.add(line2); + this.RcPickable(jet, mesh, false); - // mesh.get_ctrl = function () { return new EveElemControl(this); } - this.RcPickable(jet, mesh); return mesh; } @@ -334,7 +931,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) idcs[0] = 0; idcs[1] = 1; idcs[2] = 2; if (N > 3) { idcs[3] = 0; idcs[4] = 2; idcs[5] = 3; } geo_body.indices = new RC.BufferAttribute(idcs, 1); - geo_body.computeVertexNormals(); + // geo_body.computeVertexNormals(); let geo_rim = new RC.Geometry(); geo_rim.vertices = pos_ba; @@ -351,30 +948,302 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) let fcol = RcCol(jet.fFillColor); let lcol = RcCol(jet.fLineColor); // Process transparency !!! - // console.log("cols", fcol, lcol); - - // double-side material required for correct tracing of colors - otherwise points sequence should be changed - let mesh = new RC.Mesh(geo_body, this.RcFancyMaterial(fcol, 0.5)); - let line1 = new RC.Line(geo_rim, this.RcLineMaterial(lcol, 0.8, 2)); - - let line2 = new RC.Line(geo_rays, this.RcLineMaterial(lcol, 0.8, 0.5)); - line2.renderingPrimitive = RC.LINES; + let mesh = new RC.Mesh(geo_body, this.RcFlatMaterial(fcol, 0.5)); + mesh.material.normalFlat = true; + let line1 = this.RcMakeStripes(geo_rim, 2, lcol); + let line2 = this.RcMakeStripes(geo_rays, 1, lcol); mesh.add(line1); mesh.add(line2); + this.RcPickable(jet, mesh, false); - // mesh.get_ctrl = function () { return new EveElemControl(this); } - this.RcPickable(jet, mesh); return mesh; } + //============================================================================== + // make Digits + //============================================================================== + + makeBoxSet(boxset, rnr_data) + { + if (this.TestRnr("boxset", boxset, rnr_data)) return null; + + let vBuff; + let idxBuff; + let nVerticesPerDigit = 0; + + if (boxset.boxType == 6) // hexagon + { + nVerticesPerDigit = 14; + let stepAngle = Math.PI / 3; + let N_hex = rnr_data.vtxBuff.length / 6; + vBuff = new Float32Array(N_hex * 7 * 2 * 3); + for (let i = 0; i < N_hex; ++i) { + let rdoff = i * 6; + let R = rnr_data.vtxBuff[rdoff + 3]; + let hexRotation = rnr_data.vtxBuff[rdoff + 4]; + let hexHeight = rnr_data.vtxBuff[rdoff + 5]; + let off = i* 3 * 7 * 2; + + // position + let pos = [rnr_data.vtxBuff[rdoff], rnr_data.vtxBuff[rdoff + 1], rnr_data.vtxBuff[rdoff + 2]]; + + // center + vBuff[off] = pos[0]; + vBuff[off + 1] = pos[1]; + vBuff[off + 2] = pos[2]; + + off += 3; + for (let j = 0; j < 6; ++j) { + let angle = j*stepAngle + hexRotation; + let x = R * Math.cos(angle) + pos[0]; + let y = R * Math.sin(angle) + pos[1]; + let z = pos[2]; + + // write buffer + vBuff[off] = x; + vBuff[off + 1] = y; + vBuff[off + 2] = z; + off += 3; + } + + // copy for depth + let ro = i* 3 * 7 * 2; + for (let j = 0; j < 7; ++j) + { + vBuff[ro + 21] = vBuff[ro]; + vBuff[ro + 22] = vBuff[ro+1]; + vBuff[ro + 23] = vBuff[ro+2] + hexHeight; + ro += 3; + } + } // end loop vertex buffer + + let protoIdcs = [0,1,2, 0,2,3, 0,3,4, 0,4,5, 0,5,6, 0,6,1]; + let protoIdcs2 = [2,1,0, 3,2,0, 4,3, 0, 5,4,0, 6, 5, 0, 1, 6, 0]; + let sideIdcs = [8,1,2,2,9,8, 9,2,3,3,10,9, 10,3,4,4,11,10, + 11,4,5,5,12,11, 5,6,13,5,13,12, 13,6,1,1,8,13 ]; + let idxBuffSize = N_hex * (protoIdcs.length * 2 + sideIdcs.length); + idxBuff = new Uint32Array(idxBuffSize); + let b = 0; + for (let i = 0; i < N_hex; ++i) { + let off0 = i * 7 * 2; + for (let c = 0; c < protoIdcs.length; c++) { + idxBuff[b++] = off0 + protoIdcs2[c]; + } + for (let c = 0; c < protoIdcs.length; c++) { + idxBuff[b++] = off0 + protoIdcs[c] +7; + } + for (let c = 0; c < sideIdcs.length; c++) { + idxBuff[b++] = off0 + sideIdcs[c]; + } + } + } + else { + nVerticesPerDigit = 8; + if (boxset.boxType == 1) // free box + { + vBuff = rnr_data.vtxBuff; + } + else if (boxset.boxType == 2) // axis aligned + { + let N = rnr_data.vtxBuff.length / 6; + vBuff = new Float32Array(N * 8 * 3); + + let off = 0; + for (let i = 0; i < N; ++i) { + let rdoff = i * 6; + let x = rnr_data.vtxBuff[rdoff]; + let y = rnr_data.vtxBuff[rdoff + 1]; + let z = rnr_data.vtxBuff[rdoff + 2]; + let dx = rnr_data.vtxBuff[rdoff + 3]; + let dy = rnr_data.vtxBuff[rdoff + 4]; + let dz = rnr_data.vtxBuff[rdoff + 5]; + + // top + vBuff[off] = x; vBuff[off + 1] = y + dy; vBuff[off + 2] = z; + off += 3; + vBuff[off] = x + dx; vBuff[off + 1] = y + dy; vBuff[off + 2] = z; + off += 3; + vBuff[off] = x + dx; vBuff[off + 1] = y; vBuff[off + 2] = z; + off += 3; + vBuff[off] = x; vBuff[off + 1] = y; vBuff[off + 2] = z; + off += 3; + // bottom + vBuff[off] = x; vBuff[off + 1] = y + dy; vBuff[off + 2] = z + dz; + off += 3; + vBuff[off] = x + dx; vBuff[off + 1] = y + dy; vBuff[off + 2] = z + dz; + off += 3; + vBuff[off] = x + dx; vBuff[off + 1] = y; vBuff[off + 2] = z + dz; + off += 3; + vBuff[off] = x; vBuff[off + 1] = y; vBuff[off + 2] = z + dz; + off += 3; + } + } + + let protoSize = 6 * 2 * 3; + let protoIdcs = [0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 1, 2, 3, 1, 3, 0, 4, 7, 6, 4, 6, 5]; + let nBox = vBuff.length / 24; + idxBuff = new Uint32Array(nBox * protoSize); + let iCnt = 0; + for (let i = 0; i < nBox; ++i) { + for (let c = 0; c < protoSize; c++) { + let off = i * 8; + idxBuff[iCnt++] = protoIdcs[c] + off; + } + } + } + let body = new RC.Geometry(); + body.indices = new RC.BufferAttribute(idxBuff, 1); + body.vertices = new RC.BufferAttribute(vBuff, 3); + // body.computeVertexNormals(); + + // set material and colors + + let mat = this.RcFancyMaterial(this.ColorBlack, 1.0, { side: RC.FRONT_SIDE }); + mat.normalFlat = true; + if ( ! boxset.fSingleColor) + { + let ci = rnr_data.idxBuff; + let off = 0; + let nVert = vBuff.length /3; + let colBuff = new Float32Array( nVert * 4 ); + for (let x = 0; x < ci.length; ++x) + { + let r = (ci[x] & 0x000000FF) >> 0; + let g = (ci[x] & 0x0000FF00) >> 8; + let b = (ci[x] & 0x00FF0000) >> 16; + for (let i = 0; i < nVerticesPerDigit; ++i) + { + colBuff[off ] = r / 255; + colBuff[off + 1] = g / 255; + colBuff[off + 2] = b / 255; + colBuff[off + 3] = 1.0; + off += 4; + } + } + body.vertColor = new RC.BufferAttribute(colBuff, 4); + mat.useVertexColors = true; + } else { + mat.color = RcCol(boxset.fMainColor); + } + + let mesh = new RC.Mesh(body, mat); + this.RcPickable(boxset, mesh, false, boxset.fSecondarySelect ? BoxSetControl : EveElemControl); + + return mesh; + } + //============================================================================== + // make Calorimeters + //============================================================================== + + makeCalo3D(calo3D, rnr_data) + { + if (this.TestRnr("calo3D", calo3D, rnr_data)) return null; + let body = new RC.Geometry(); + let vBuff = rnr_data.vtxBuff; + let protoSize = 6 * 2 * 3; + let protoIdcs = [0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 1, 2, 3, 1, 3, 0, 4, 7, 6, 4, 6, 5]; + let nBox = vBuff.length / 24; + let idxBuff = new Uint32Array(nBox * protoSize); + let p = 0; + for (let i = 0; i < nBox; ++i) { + let off = i * 8; + for (let c = 0; c < protoSize; c++) { + idxBuff[p++] = protoIdcs[c] + off; + } + } + + body.indices = new RC.BufferAttribute(idxBuff, 1); + body.vertices = new RC.BufferAttribute(rnr_data.vtxBuff, 3); + // body.computeVertexNormals(); + + let ci = rnr_data.idxBuff; + let off = 0 + let colBuff = new Float32Array(nBox * 8 * 4); + for (let x = 0; x < nBox; ++x) { + let slice = ci[x * 2]; + let sliceColor = calo3D.sliceColors[slice]; + let tc = RcCol(sliceColor); + for (let i = 0; i < 8; ++i) { + colBuff[off] = tc.r; + colBuff[off + 1] = tc.g; + colBuff[off + 2] = tc.b; + colBuff[off + 3] = 1.0; + off += 4; + } + } + body.vertColor = new RC.BufferAttribute(colBuff, 4); + + let mat = this.RcFancyMaterial(this.ColorBlack, 1.0, { side: RC.FRONT_SIDE }); + mat.useVertexColors = true; + mat.normalFlat = true; + + let mesh = new RC.Mesh(body, mat); + + this.RcPickable(calo3D, mesh, false, Calo3DControl); + + return mesh; + } + + makeCalo2D(calo2D, rnrData) + { + if (this.TestRnr("calo2D", calo2D, rnrData)) return null; + let body = new RC.Geometry(); + let nSquares = rnrData.vtxBuff.length / 12; + let nTriang = 2 * nSquares; + + let idxBuff = new Uint32Array(nTriang * 3); + for (let s = 0; s < nSquares; ++s) { + let boff = s * 6; + let ioff = s * 4; + + // first triangle + idxBuff[boff] = ioff; + idxBuff[boff + 1] = ioff + 1; + idxBuff[boff + 2] = ioff + 2; + + // second triangle + idxBuff[boff + 3] = ioff + 2; + idxBuff[boff + 4] = ioff + 3; + idxBuff[boff + 5] = ioff; + } + + body.indices = new RC.BufferAttribute(idxBuff, 1); + body.vertices = new RC.BufferAttribute(rnrData.vtxBuff, 3); + // body.computeVertexNormals(); + + let ci = rnrData.idxBuff; + let colBuff = new Float32Array(nSquares * 4 * 4); + let off = 0; + for (let x = 0; x < nSquares; ++x) { + let slice = ci[x * 2]; + let sliceColor = calo2D.sliceColors[slice]; + let tc = RcCol(sliceColor); + for (let i = 0; i < 4; ++i) { + colBuff[off] = tc.r; + colBuff[off + 1] = tc.g; + colBuff[off + 2] = tc.b; + colBuff[off + 3] = 1.0; + off += 4; + } + } + body.vertColor = new RC.BufferAttribute(colBuff, 4); + + let mat = this.RcFlatMaterial(this.ColorBlack, 1); + mat.useVertexColors = true; + let mesh = new RC.Mesh(body, mat); + + this.RcPickable(calo2D, mesh, false, Calo2DControl); + + return mesh; + } //============================================================================== // makeEveGeometry / makeEveGeoShape //============================================================================== - makeEveGeometry(rnr_data) + makeEveGeometry(rnr_data, compute_normals) { let nVert = rnr_data.idxBuff[1] * 3; @@ -383,9 +1252,14 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) let geo = new RC.Geometry(); geo.vertices = new RC.BufferAttribute(rnr_data.vtxBuff, 3); - geo.indices = new RC.BufferAttribute(rnr_data.idxBuff, 1); - geo.setDrawRange(2, nVert); - geo.computeVertexNormalsIdxRange(2, nVert); + let ib = rnr_data.idxBuff; + geo.indices = new RC.BufferAttribute( + new Uint32Array(ib.buffer, ib.byteOffset + 8, nVert), + 1); + + if (compute_normals) { + geo.computeVertexNormalsIdxRange(2, nVert); + } // XXXX Fix this. It seems we could have flat shading with usage of simple shaders. // XXXX Also, we could do edge detect on the server for outlines. @@ -401,16 +1275,16 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) makeEveGeoShape(egs, rnr_data) { - let geom = this.makeEveGeometry(rnr_data); + let geom = this.makeEveGeometry(rnr_data, false); let fcol = RcCol(egs.fFillColor); - let material = this.RcFancyMaterial(fcol, 0.2); - material.side = RC.FRONT_AND_BACK_SIDE; - material.specular = new RC.Color(1, 1, 1); - material.shininess = 50; + let mat = this.RcFancyMaterial(fcol, 0.2); + mat.side = RC.FRONT_AND_BACK_SIDE; + mat.shininess = 50; + mat.normalFlat = true; - let mesh = new RC.Mesh(geom, material); + let mesh = new RC.Mesh(geom, mat); this.RcPickable(egs, mesh); return mesh; } @@ -430,13 +1304,12 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) let fcol = RcCol(psp.fMainColor); - let material = this.RcFancyMaterial(fcol, 0.4); + let material = this.RcFlatMaterial(fcol, 0.4); material.side = RC.FRONT_AND_BACK_SIDE; - material.specular = new RC.Color(1, 1, 1); - material.shininess = 50; let line_mat = this.RcLineMaterial(fcol); + let meshes = []; for (let ib_pos = 0; ib_pos < ib_len;) { if (rnr_data.idxBuff[ib_pos] == GL.TRIANGLES) @@ -450,6 +1323,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) let mesh = new RC.Mesh(geo, material); this.RcPickable(psp, mesh); psp_ro.add(mesh); + meshes.push(mesh); ib_pos += 2 + 3 * rnr_data.idxBuff[ib_pos + 1]; } @@ -473,6 +1347,19 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) } } + // this.RcPickable(psp, psp_ro); + // this.RcPickable(el, psp_ro, false, null); + if (psp.fPickable) { + for (let m of meshes) m.pickable = true; + } + psp_ro.get_ctrl = function (iobj, tobj) { + let octrl = new EveElemControl(iobj, tobj); + octrl.DrawForSelection = function (sec_idcs, res) { + res.geom.push(...meshes); + //res.geom.push(...tobj.children); + } + return octrl; + } return psp_ro; } @@ -481,71 +1368,61 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) makeStraightLineSet(el, rnr_data) { - console.log("makeStraightLineSet ..."); + // console.log("makeStraightLineSet ..."); let obj3d = new RC.Group(); - let fcol = RcCol(el.fMainColor); - let buf = new Float32Array(el.fLinePlexSize * 6); for (let i = 0; i < el.fLinePlexSize * 6; ++i) { buf[i] = rnr_data.vtxBuff[i]; } - let line_mat = new this.RcLineMaterial(fcol); - let geom = new RC.Geometry(); geom.vertices = new RC.BufferAttribute(buf, 3); - let line = new RC.Line(geom, line_mat); - line.renderingPrimitive = RC.LINES; + let line_color = RcCol(el.fMainColor); + let line_width = 2 * (el.fLineWidth || 1); + const line = this.RcMakeStripes(geom, line_width, line_color); + this.RcApplyStripesMaterials(el, line, 2); this.RcPickable(el, line); obj3d.add(line); // ---------------- DUH, could share buffer attribute. XXXXX - let msize = el.fMarkerPlexSize; - - let p_buf = new Float32Array(msize * 3); - - let startIdx = el.fLinePlexSize * 6; - let endIdx = startIdx + msize * 3; - for (let i = startIdx; i < endIdx; ++i) - { - p_buf[i] = rnr_data.vtxBuff[i]; + if (el.fMarkerPlexSize) { + let nPnts = el.fMarkerPlexSize; + let off = el.fLinePlexSize * 6; + let p_buf = new Float32Array(el.fTexX*el.fTexY*4); + for (let i = 0; i < nPnts; ++i) { + let j = i*3; + let k = i*4; + p_buf[k] = rnr_data.vtxBuff[j+off]; + p_buf[k+1] = rnr_data.vtxBuff[j+off+1]; + p_buf[k+2] = rnr_data.vtxBuff[j+off+2]; + p_buf[k+3] = 0; + } + let marker = this.RcMakeZSprite(el.fMainColor, el.fMarkerSize, nPnts, + p_buf, el.fTexX, el.fTexY, + "star5-32a.png"); + obj3d.add(marker); + } + // For secondary selection, see EveElements.js + // obj3d.eve_idx_buf = rnr_data.idxBuff; + // if (el.fSecondarySelect) + // octrl = new StraightLineSetControl(obj3d); + // else + // octrl = new EveElemControl(obj3d); + + this.RcPickable(el, obj3d, true, null); + obj3d.get_ctrl = function(iobj, tobj) { + let octrl = new EveElemControl(iobj, tobj); + octrl.DrawForSelection = function(sec_idcs, res) { + res.geom.push(...this.top_obj.children); + }; + return octrl; } - - let p_geom = new RC.Geometry(); - p_geom.vertices = new RC.BufferAttribute(p_buf, 3); - - let p_mat = new RC.MeshBasicMaterial; - p_mat.color = fcol; - p_mat.pointSize = 2 * el.fMarkerSize; - p_mat.usePoints = true; - p_mat.drawCircles = true; - - let marker = new RC.Point(p_geom, p_mat); - marker.pickingMaterial.pointSize = 2 * el.fMarkerSize;; - - this.RcPickable(el, marker); - obj3d.add(marker); - - // ???? - obj3d.eve_idx_buf = rnr_data.idxBuff; - - /* - let octrl; - if (el.fSecondarySelect) - octrl = new StraightLineSetControl(obj3d); - else - octrl = new EveElemControl(obj3d); - - line.get_ctrl = function() { return octrl; }; - marker.get_ctrl = function() { return octrl; }; - obj3d.get_ctrl = function() { return octrl; }; - */ return obj3d; } @@ -554,6 +1431,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager) //============================================================================== - return EveElements; + EVE.EveElements = EveElements; + return EveElements; }); diff --git a/ui5/eve7/lib/EveManager.js b/ui5/eve7/lib/EveManager.js index 8015bf269fc5f..4e31ee20d4238 100644 --- a/ui5/eve7/lib/EveManager.js +++ b/ui5/eve7/lib/EveManager.js @@ -44,10 +44,8 @@ sap.ui.define([], function() { globExceptionHandler(msg, url, lineNo, columnNo, error) { // NOTE: currently NOT connected, see onWebsocketOpened() below. - console.log("EveManager got global error", msg, url, lineNo, columnNo, error); + console.error("EveManager got global error", msg, url, lineNo, columnNo, error); - EVE.alert("Global Exception handler: " + msg + "\n" + url + - " line:" + lineNo + " col:" + columnNo); let suppress_alert = false; return suppress_alert; } @@ -56,6 +54,7 @@ sap.ui.define([], function() { UseConnection(handle) { this.handle = handle; + this.is_rcore = (handle.getUserArgs("GLViewer") == "RCore"); handle.setReceiver(this); handle.connect(); @@ -146,8 +145,15 @@ sap.ui.define([], function() { } else if (resp.content == "EndChanges") { this.ServerEndRedrawCallback(); - if (resp.log) - console.log(resp.log); + if (resp.log) { + resp.log.forEach((item) => { + // use console error above warning serverity + if (item.lvl < 3) + console.error(item.msg); + else + console.log(item.msg); + }); + } } else if (resp.content == "BrowseElement") { this.BrowseElement(resp.id); @@ -201,6 +207,18 @@ sap.ui.define([], function() { elem.$receivers.push(receiver); } + /** Disconnect scene from the updates */ + UnRegisterSceneReceiver(id, receiver){ + let elem = this.GetElement(id); + + if (!elem) return; + let idx = elem.$receivers.indexOf(receiver); + if (idx > -1) { // only splice array when item is found + console.log("unregister scene receiver"); + elem.$receivers.splice(idx, 1); // 2nd parameter means remove one item only + } + } + /** Returns list of scene elements */ getSceneElements() { @@ -427,11 +445,21 @@ sap.ui.define([], function() { let lastoff = offset; - for (let n=1; n 0) cont.remove(cont.children[0]); + this.mgr.UnRegisterSceneReceiver(this.id, this); this.first_time = true; } @@ -184,8 +193,15 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { endChanges() { - if (this.glctrl) + if (this.glctrl) { + if (this.need_visibility_update) { + let p = this.mgr.GetElement(this.id); + this.update3DObjectsVisibility(p.childs, true); + this.need_visibility_update = false; + } this.glctrl.viewer.render(); + + } } elementAdded(el) @@ -195,41 +211,36 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { let obj3d = this.makeGLRepresentation(el); if ( ! obj3d) return; - // AMT this is an overkill, temporary solution - let scene = this.mgr.GetElement(el.fSceneId); - this.update3DObjectsVisibility(scene.childs, true); - + // let scene = this.mgr.GetElement(this.id); let container = this.glctrl.getSceneContainer("scene" + this.id); container.add(obj3d); this.id2obj_map.set(el.fElementId, obj3d); + + this.need_visibility_update = true; } replaceElement(el) { if (!this.glctrl) return; + let container = this.glctrl.getSceneContainer("scene" + this.id); + try { let obj3d = this.getObj3D(el.fElementId); - let all_ancestor_children_visible = obj3d.all_ancestor_children_visible; - let visible = obj3d.visible; - let container = this.glctrl.getSceneContainer("scene" + this.id); - - container.remove(obj3d); + if(obj3d) container.remove(obj3d); obj3d = this.makeGLRepresentation(el); - obj3d.all_ancestor_children_visible = all_ancestor_children_visible; - obj3d.visible = visible; - container.add(obj3d); - - this.id2obj_map.set(el.fElementId, obj3d); - - this.glctrl.viewer.render(); + if (obj3d) { + container.add(obj3d); + this.id2obj_map.set(el.fElementId, obj3d); + } } catch (e) { console.error("replace element", e); } + this.need_visibility_update = true; } elementsRemoved(ids) @@ -251,10 +262,12 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { this.id2obj_map.delete(elId); - if (typeof obj3d.dispose !== 'function') - console.log("EveScene.elementsRemoved no dispose function for " + this.mgr.GetElement(elId)._typename, ", rnr obj ", obj3d._typename); - else - obj3d.dispose(); + if (!this.mgr.is_rcore) { + if (typeof obj3d.dispose !== 'function') + console.log("EveScene.elementsRemoved no dispose function for " + this.mgr.GetElement(elId)._typename, ", rnr obj ", obj3d._typename, obj3d); + else + obj3d.dispose(); + } } } @@ -264,21 +277,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { // visibility if (msg.changeBit & this.mgr.EChangeBits.kCBVisibility) { - // self - if (msg.rnr_self_changed) - { - let obj3d = this.getObj3D( el.fElementId ); - if (obj3d) - { - obj3d.visible = obj3d.all_ancestor_children_visible && el.fRnrSelf; - } - } - // children - if (msg.rnr_children_changed && el.childs) - { - let scene = this.mgr.GetElement(el.fSceneId); - this.update3DObjectsVisibility(scene.childs, true); - } + this.need_visibility_update = true; } // other change bits @@ -301,12 +300,12 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { return undefined; } - sendSelectMIR(sel_id, obj3d, is_multi, indx) + sendSelectMIR(sel_id, eve_el, is_multi, indx) { indx = this.sanitizeIndx(indx); let is_secsel = indx !== undefined; - let fcall = "NewElementPickedStr(" + (obj3d ? obj3d.eve_el.fElementId : 0) + `, ${is_multi}, ${is_secsel}`; + let fcall = "NewElementPickedStr(" + (eve_el ? eve_el.fElementId : 0) + `, ${is_multi}, ${is_secsel}`; if (is_secsel) { fcall += ", \"" + indx.join(",") + "\""; @@ -317,32 +316,32 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { } /** interactive handler. Calculates selection state, apply to element and distribute to other scene */ - processElementSelected(obj3d, indx, event) + processElementSelected(eve_el, indx, event) { // console.log("EveScene.processElementSelected", obj3d, col, indx, evnt); let is_multi = event && event.ctrlKey ? true : false; - this.sendSelectMIR(this.mgr.global_selection_id, obj3d, is_multi, indx); + this.sendSelectMIR(this.mgr.global_selection_id, eve_el, is_multi, indx); return true; } /** interactive handler */ - processElementHighlighted(obj3d, indx, evnt) + processElementHighlighted(eve_el, indx, event) { - if (this.mgr.MatchSelection(this.mgr.global_selection_id, obj3d.eve_el, indx)) + // RenderCore viewer is organizing selection on stack, the last selection will set the color + if (!this.mgr.is_rcore && this.mgr.MatchSelection(this.mgr.global_selection_id, eve_el, indx)) return true; // Need check for duplicates before call server, else server will un-higlight highlighted element - // console.log("EveScene.processElementHighlighted", obj3d.eve_el.fElementId, indx, evnt); - if (this.mgr.MatchSelection(this.mgr.global_highlight_id, obj3d.eve_el, indx)) + if (this.mgr.MatchSelection(this.mgr.global_highlight_id, eve_el, indx)) return true; - // when send queue below threshold, ignre highlight + // when send queue below threshold, ignore highlight if (this.mgr.CheckSendThreshold()) return true; - this.sendSelectMIR(this.mgr.global_highlight_id, obj3d, false, indx); + this.sendSelectMIR(this.mgr.global_highlight_id, eve_el, false, indx); return true; } @@ -395,59 +394,107 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { }); } - SelectElement(selection_obj, element_id, sec_idcs, extra) + SelectElementStd(selection_obj, element_id, sec_idcs, extra) { let obj3d = this.getObj3D( element_id ); if (!obj3d) return; - let opass = this.glctrl.viewer.outline_pass; - opass.id2obj_map[element_id] = opass.id2obj_map[element_id] || []; + let outline_map = this.glctrl.viewer.outline_map; + // console.log("EveScene.SelectElement ", selection_obj.fName, element_id, selection_obj.fElementId, outline_map); - if (opass.id2obj_map[element_id][selection_obj.fElementId] !== undefined) - { + outline_map[element_id] = outline_map[element_id] || []; + + if (outline_map[element_id][selection_obj.fElementId] !== undefined) { return; } - let stype = selection_obj.fName.endsWith("Selection") ? "select" : "highlight"; - let estype = THREE.OutlinePassEve.selection_enum[stype]; + const ST_Selection = 0; // Matching THREE.OutlinePassEve.selection_enum + const ST_Highlight = 1; + let stype = selection_obj.fName.endsWith("Selection") ? ST_Selection : ST_Highlight; let oe = this.mgr.GetElement(element_id); - // console.log("EveScene.SelectElement ", selection_obj.fName, oe.fName, selection_obj.fElementId, this.glctrl.viewer.outline_pass.id2obj_map); let res = { - "sel_type" : estype, - "sec_sel" : (oe.fSecondarySelect && sec_idcs.length > 0) ? true: false, + "sel_type" : stype, + "sec_sel" : (oe.fSecondarySelect && sec_idcs.length > 0) ? true : false, "geom" : [] }; - // exit if you try to highlight an object that has already been selected - if (estype == THREE.OutlinePassEve.selection_enum["highlight"] && - opass.id2obj_map[element_id][this.mgr.global_selection_id] !== undefined) + // Exit if we are trying to highlight an object that has already been selected. + if (stype == ST_Highlight && + outline_map[element_id][this.mgr.global_selection_id] !== undefined) { if (!res.sec_sel) return; } - if (!res.sec_sel) opass.id2obj_map[element_id] = []; + if (!res.sec_sel) outline_map[element_id] = []; - if (obj3d.get_ctrl) - { - let ctrl = obj3d.get_ctrl(); + if (obj3d.get_ctrl) { + let ctrl = obj3d.get_ctrl(obj3d); ctrl.DrawForSelection(sec_idcs, res, extra); - opass.id2obj_map[element_id][selection_obj.fElementId] = res; + } else { + res.geom.push(obj3d); + } + outline_map[element_id][selection_obj.fElementId] = res; - if (stype == "highlight" && selection_obj.sel_list) { - this.glctrl.viewer.remoteToolTip(selection_obj.sel_list[0].tooltip); - } + if (stype == ST_Highlight && selection_obj.sel_list) { + this.glctrl.viewer.remoteToolTip(selection_obj.sel_list[0].tooltip); } } - UnselectElement(selection_obj, element_id) + UnselectElementStd(selection_obj, element_id) { - let opass = this.glctrl.viewer.outline_pass; - // console.log("EveScene.UnselectElement ", selection_obj.fName, element_id, selection_obj.fElementId, this.glctrl.viewer.outline_pass.id2obj_map); - if (opass.id2obj_map[element_id] !== undefined) - { - delete opass.id2obj_map[element_id][selection_obj.fElementId]; + let outline_map = this.glctrl.viewer.outline_map; + // console.log("EveScene.UnselectElement ", selection_obj.fName, element_id, selection_obj.fElementId, outline_map); + + if (outline_map[element_id] !== undefined) { + delete outline_map[element_id][selection_obj.fElementId]; + } + } + + SelectElementRCore(selection_obj, element_id, sec_idcs, extra) + { + let obj3d = this.getObj3D( element_id ); + if (!obj3d) return; + + let eve_el = this.mgr.GetElement(element_id); + + let sid = selection_obj.fElementId; + let smap = this.glctrl.viewer.selection_map; + if (smap[sid] === undefined) { + smap[sid] = {}; + } + + let res = { + "sec_sel" : (eve_el.fSecondarySelect && sec_idcs.length > 0) ? true : false, + "geom" : [] + }; + + if (obj3d.get_ctrl) { + let ctrl = obj3d.get_ctrl(obj3d); + ctrl.DrawForSelection(sec_idcs, res, extra); + } else { + res.geom.push(obj3d); + } + smap[sid][element_id] = res; + this.glctrl.viewer.make_selection_last_in_list(sid); + + // Display tooltip. + // XXXX Should check if highlight request came from this viewer. + if (selection_obj.fIsHighlight && selection_obj.sel_list) { + this.glctrl.viewer.remoteToolTip(selection_obj.sel_list[0].tooltip); + } + } + + UnselectElementRCore(selection_obj, element_id) + { + let sid = selection_obj.fElementId; + let smap = this.glctrl.viewer.selection_map; + if (smap[sid] !== undefined) { + delete smap[sid][element_id]; + if (Object.keys(smap[sid]).length == 0) { + this.glctrl.viewer.remove_selection_from_list(sid); + } } } diff --git a/ui5/eve7/lib/GlViewerJSRoot.js b/ui5/eve7/lib/GlViewerJSRoot.js index 3b8beeedd91b2..dbf35b70c21af 100644 --- a/ui5/eve7/lib/GlViewerJSRoot.js +++ b/ui5/eve7/lib/GlViewerJSRoot.js @@ -41,6 +41,8 @@ sap.ui.define([ return this.geo_painter.getExtrasContainer(); } + get outline_map() { return this.outline_pass.id2obj_map; } + //============================================================================== createGeoPainter() @@ -69,11 +71,6 @@ sap.ui.define([ this.outline_pass.usePatternTexture = false; this.outline_pass.downSampleRatio = 1; this.outline_pass.glowDownSampleRatio = 3; - // this.outline_pass.id2obj_map = {}; - - // const sh = THREE.OutlinePassEve.selection_enum["select"]; // doesnt stand for spherical harmonics :P - // THREE.OutlinePassEve.selection_atts[sh].visibleEdgeColor.set('#dd1111'); - // THREE.OutlinePassEve.selection_atts[sh].hiddenEdgeColor.set('#1111dd'); this._effectComposer.addPass( this.outline_pass ); diff --git a/ui5/eve7/lib/GlViewerRCore.js b/ui5/eve7/lib/GlViewerRCore.js index eac9058485557..37a07ef26d2eb 100644 --- a/ui5/eve7/lib/GlViewerRCore.js +++ b/ui5/eve7/lib/GlViewerRCore.js @@ -5,7 +5,8 @@ sap.ui.define([ "use strict"; - let RC, RP, RendeQuTor; + let RC; + let datGUI; class GlViewerRCore extends GlViewer { @@ -15,21 +16,29 @@ sap.ui.define([ const urlParams = new URLSearchParams(window.location.search); - console.log("XXXX", window.location.search, urlParams.get('UseRenderQueue'), urlParams.get('NoRenderQueue')); - this.UseRenderQueue = true; - if (urlParams.get('UseRenderQueue') != null) this.UseRenderQueue = true; - if (urlParams.get('NoRenderQueue' ) != null) this.UseRenderQueue = false; + let mode_mm = /^(?:Direct|Simple|Full)$/.exec(urlParams.get('RQ_Mode')); + let ssaa_mm = /^(1|2|4)$/. exec(urlParams.get('RQ_SSAA')); + let marker_scale = /^([\d\.]+)$/. exec(urlParams.get('RQ_MarkerScale')); + let line_scale = /^([\d\.]+)$/. exec(urlParams.get('RQ_LineScale')); - if (this.UseRenderQueue) - { - let mode_mm = /^(?:Direct|Simple|Full)$/.exec(urlParams.get('RQ_Mode')); - let ssaa_mm = /^(1|2|4)$/. exec(urlParams.get('RQ_SSAA')); + this.RQ_Mode = (mode_mm) ? mode_mm[0] : "Simple"; + this.RQ_SSAA = (ssaa_mm) ? ssaa_mm[0] : 2; + this.RQ_MarkerScale = (marker_scale) ? marker_scale[0] : 1; + this.RQ_LineScale = (line_scale) ? line_scale[0] : 1; + + let jsrp = EVE.JSR.source_dir; + // take out 'jsrootsys' and replace it with 'rootui5sys/eve7/' + this.eve_path = jsrp.substring(0, jsrp.length - 10) + 'rootui5sys/eve7/'; + + this._logLevel = 1; // 0 - error, 1 - warning, 2 - info, 3 - debug - this.RQ_Mode = (mode_mm) ? mode_mm[0] : "Simple"; - this.RQ_SSAA = (ssaa_mm) ? ssaa_mm[0] : 2; + if (this._logLevel > 2) { + console.log("GlViewerRCore RQ_Mode:", this.RQ_Mode, "RQ_SSAA:", this.RQ_SSAA, + 'RQ_MarkerScale', this.RQ_MarkerScale, 'RQ_LineScale', this.RQ_LineScale); } - console.log("UseRenderQueue", this.UseRenderQueue, "RQ_Mode", this.RQ_Mode, "RQ_SSAA", this.RQ_SSAA); + this._selection_map = {}; + this._selection_list = []; } init(controller) @@ -38,6 +47,14 @@ sap.ui.define([ let pthis = this; + if (!datGUI) { + datGUI = new EVE.JSR.gui.GUI({ autoPlace: false, width: 220 }); + datGUI.domElement.id = 'posDatGUI'; + pthis.datGUIFolder = datGUI.addFolder("background") + let dome = this.controller.getView().getParent().getParent(); + dome.getDomRef().appendChild(datGUI.domElement); + } + // For offline mode, one needs a a full URL or the request // gets forwarded to openi5.hana.ondemand.com. // This has to be understood and fixed. Loading of shaders @@ -45,43 +62,38 @@ sap.ui.define([ // // console.log(window.location.pathname); // where are we loading from? // import("https://desire.physics.ucsd.edu/matevz/alja.github.io/rootui5/eve7/rnr_core/RenderCore.js").then((module) => { - import("../../eve7/rnr_core/RenderCore.js").then((module) => { - console.log("GlViewerRCore.onInit - RenderCore.js loaded"); - - RC = module; - if (this.UseRenderQueue) - { - import("../../eve7/lib/RendeQuTor.js").then((module) => { - console.log("GlViewerRCore.onInit - RenderPassesRCore.js loaded"); - - RP = module; - RendeQuTor = RP.RendeQuTor; - - pthis.bootstrap(); - }); - } else { + if (!RC) { + import(this.eve_path + 'lib/RenderCore.js').then((module) => { + if (this._logLevel >= 2) + console.log("GlViewerRCore.onInit - RenderCore.js loaded"); + RC = module; pthis.bootstrap(); - } - }); + }); + } else { + this.bootstrap(); + } } bootstrap() { - this.creator = new EveElements(RC); + this.creator = new EveElements(RC, this); // this.creator.useIndexAsIs = EVE.JSR.decodeUrl().has('useindx'); + RC.GLManager.sCheckFrameBuffer = false; + RC.Object3D.sDefaultPickable = false; + RC.PickingShaderMaterial.DEFAULT_PICK_MODE = RC.PickingShaderMaterial.PICK_MODE.UINT; + this.createRCoreRenderer(); this.controller.createScenes(); this.controller.redrawScenes(); - this.setupRCoreDomAndEventHandlers(); + this.setupEventHandlers(); this.controller.glViewerInitDone(); + } - // XXXX MT: HACK ... give RCore some time to load shaders. - // Would be better to have some onShadersLoaded thing but - // this is probably problematic later on,k if we add objects with - // custom shaders later on. - setTimeout(this.render.bind(this), 500); + cleanup() { + if (this.controller) this.controller.removeScenes(); + super.cleanup(); } //============================================================================== @@ -102,122 +114,147 @@ sap.ui.define([ createRCoreRenderer() { - let w = this.get_width(); - let h = this.get_height(); - - //this.canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - this.canvas = document.createElement('canvas'); - this.canvas.id = "rcore-canvas"; - this.canvas.width = w; - this.canvas.height = h; - - // Enable EXT_color_buffer_float for picking depth extraction - // let gl = this.canvas.getContext("webgl2"); - // let ex = gl.getExtension("EXT_color_buffer_float"); - // console.log("Create RCore, gl, float_color_buff:", gl, ex); - - this.renderer = new RC.MeshRenderer(this.canvas, RC.WEBGL2, {antialias: false, stencil: true}); - this.renderer.clearColor = "#FFFFFFFF"; - this.renderer.addShaderLoaderUrls("rootui5sys/eve7/rnr_core/shaders"); - this.renderer.addShaderLoaderUrls("rootui5sys/eve7/shaders"); - this.renderer.pickDoNotRender = true; + this.canvas = new RC.Canvas(this.get_view().getDomRef()); + let w = this.canvas.width; + let h = this.canvas.height; + this.fixCssSize(); + this.canvas.parentDOM.style.overflow = "hidden"; + this.canvas.canvasDOM.style.overflow = "hidden"; + // It seems SSAA of 2 is still beneficial on retina. + // if (this.canvas.pixelRatio > 1 && this.RQ_SSAA > 1) { + // console.log("Correcting RQ_SSAA for pixelRatio", this.canvas.pixelRatio, + // "from", this.RQ_SSAA, "to", this.RQ_SSAA / this.canvas.pixelRatio); + // this.RQ_SSAA /= this.canvas.pixelRatio; + // } + + this.renderer = new RC.MeshRenderer(this.canvas, RC.WEBGL2, + { antialias: false, stencil: false }); + this.renderer._logLevel = 0; + this.renderer.clearColor = "#FFFFFF00"; // "#00000000"; + this.renderer.addShaderLoaderUrls(this.eve_path + RC.REveShaderPath); + this.renderer.pickObject3D = true; + + // add dat GUI option to set background + let eveView = this.controller.mgr.GetElement(this.controller.eveViewerId); + let name = eveView.fName; + name = name.substring(0, 3); + let parName = name + "_WhiteBG"; + let conf = {}; conf[parName] = true; + let pr = this; + if (this.controller.getView().getViewData()) { + datGUI.__folders.background.add(conf, parName).onChange(function (wbg) { + conf[parName] = wbg; + eveView.clearColor = wbg ? "#FFFFFF00" : "#00000000"; + pr.renderer.clearColor = eveView.clearColor; + pr.request_render(); + }); + } + else if (eveView.clearColor) { + pr.renderer.clearColor = eveView.clearColor; + } this.scene = new RC.Scene(); - this.lights = this.make_object("Light container"); + this.lights = new RC.Group; + this.lights.name = "Light container"; this.scene.add(this.lights); - let a_light = new RC.AmbientLight(new RC.Color(0xffffff), 0.1); + let a_light = new RC.AmbientLight(new RC.Color(0xffffff), 0.05); this.lights.add(a_light); - let light_class_3d = RC.PointLight; // RC.DirectionalLight; // RC.PointLight; - let light_class_2d = RC.DirectionalLight; + let light_3d_ctor = function(col, int, dist, decay, args) { return new RC.PointLight(col, int, dist, decay, args); }; + // let light_3d_ctor = function(col, int, dist, decay, args) { return new RC.DirectionalLight(col, int); }; + let light_2d_ctor = function(col, int) { return new RC.DirectionalLight(col, int); }; if (this.controller.isEveCameraPerspective()) { - this.camera = new RC.PerspectiveCamera(75, w / h, 1, 5000); - this.camera.position = new RC.Vector3(-500, 0, 0); - this.camera.lookAt(new RC.Vector3(0, 0, 0), new RC.Vector3(0, 1, 0)); + this.camera = new RC.PerspectiveCamera(75, w / h, 20, 4000); this.camera.isPerspectiveCamera = true; - let l_int = 0.85; - this.lights.add(new light_class_3d(0xaa8888, l_int )); // R - this.lights.add(new light_class_3d(0x88aa88, l_int )); // G - this.lights.add(new light_class_3d(0x8888aa, l_int )); // B - this.lights.add(new light_class_3d(0x999999, l_int )); // gray + let l_int = 1.4; + let l_args = { constant: 1, linear: 0, quadratic: 0, smap_size: 0 }; + this.lights.add(light_3d_ctor(0xaa8888, l_int, 0, 1, l_args)); // R + this.lights.add(light_3d_ctor(0x88aa88, l_int, 0, 1, l_args)); // G + this.lights.add(light_3d_ctor(0x8888aa, l_int, 0, 1, l_args)); // B + this.lights.add(light_3d_ctor(0xaaaa66, l_int, 0, 1, l_args)); // Y + this.lights.add(light_3d_ctor(0x666666, l_int, 0, 1, l_args)); // gray, bottom // Lights are positioned in resetRenderer. - for (let i = 1; i <= 4; ++i) - { - let l = this.lights.children[i]; - l.add( new RC.IcoSphere(1, 1, 10.0, l.color.clone().multiplyScalar(0.5), false) ); - } + // Markers on light positions (screws up bounding box / camera reset calculations) + // for (let i = 1; i <= 4; ++i) + // { + // let l = this.lights.children[i]; + // l.add( new RC.IcoSphere(1, 1, 10.0, l.color.clone().multiplyScalar(0.5), false) ); + // } } else { - this.camera = new RC.OrthographicCamera(-w/2, w/2, -h/2, h/2, 0, 2000); - this.camera.position = new RC.Vector3(0, 0, 500); - this.camera.lookAt(new RC.Vector3(0, 0, 0), new RC.Vector3(0, 1, 0)); + this.camera = new RC.OrthographicCamera(-w/2, w/2, -h/2, h/2, 20, 2000); this.camera.isOrthographicCamera = true; - this.lights.add(new light_class_2d( 0xffffff, 1 )); // white front - this.lights.add(new light_class_2d( 0xffffff, 1 )); // white back + let l_int = 0.85; + this.lights.add(light_2d_ctor(0xffffff, l_int)); // white front + // this.lights.add(light_2d_ctor(0xffffff, l_int)); // white back // Lights are positioned in resetRenderer. } + // AMT, disable auto update in camera in order prevent reading quaternions in update of + // model view matrix in Obejct3D function updateMatrixWorld + this.camera.matrixAutoUpdate = false; + // Test objects - if (this.controller.kind === "3D") + if (this.controller.isEveCameraPerspective()) { - /* - let c = new RC.Cube(100, new RC.Color(1,.6,.2)); - c.material = new RC.MeshPhongMaterial(); - c.material.transparent = true; - c.material.opacity = 0.5; - c.material.depthWrite = false; - this.scene.add(c); - */ - let ss = new RC.Stripe([0,0,0, 100,50,50, 100,200,200]); - ss.material.lineWidth = 20.0; - ss.material.color = new RC.Color(0xff0000); - this.scene.add(ss); + // let c = new RC.Cube(40, new RC.Color(0.2,.4,.8)); + // c.material = new RC.MeshPhongMaterial(); + // c.material.transparent = true; + // c.material.opacity = 0.8; + // c.material.depthWrite = false; + // this.scene.add(c); + + // let ss = new RC.Stripe([0,0,0, 400,0,0, 400,400,0, 400,400,400]); + // ss.material.lineWidth = 20.0; + // ss.material.color = new RC.Color(0xff0000); + // ss.material.emissive = new RC.Color(0x008080); + // this.scene.add(ss); } this.rot_center = new RC.Vector3(0,0,0); - if (this.UseRenderQueue) + this.rqt = new RC.RendeQuTor(this.renderer, this.scene, this.camera); + if (this.RQ_Mode == "Direct") { - this.rqt = new RendeQuTor(this.renderer, this.scene, this.camera); - if (this.RQ_Mode == "Direct") - { - this.rqt.initDirectToScreen(); - } - else if (this.RQ_Mode == "Simple") - { - this.rqt.initSimple(this.RQ_SSAA); - this.creator.SetupPointLineFacs(this.RQ_SSAA, this.RQ_SSAA); - } - else - { - this.rqt.initFull(this.RQ_SSAA); - this.creator.SetupPointLineFacs(this.RQ_SSAA, this.RQ_SSAA); - } - this.rqt.updateViewport(w, h); + this.rqt.initDirectToScreen(); + } + else if (this.RQ_Mode == "Simple") + { + this.rqt.initSimple(this.RQ_SSAA); + this.creator.SetupPointLineFacs(this.RQ_SSAA, + this.RQ_MarkerScale * this.canvas.pixelRatio, + this.RQ_LineScale * this.canvas.pixelRatio); } + else + { + this.rqt.initFull(this.RQ_SSAA); + this.creator.SetupPointLineFacs(this.RQ_SSAA, + this.RQ_MarkerScale * this.canvas.pixelRatio, + this.RQ_LineScale * this.canvas.pixelRatio); + } + this.rqt.updateViewport(w, h); } - setupRCoreDomAndEventHandlers() + setupEventHandlers() { - let dome = this.get_view().getDomRef(); - dome.appendChild(this.canvas); + let dome = this.canvas.canvasDOM; // Setup tooltip this.ttip = document.createElement('div'); this.ttip.setAttribute('class', 'eve_tooltip'); this.ttip_text = document.createElement('div'); this.ttip.appendChild(this.ttip_text); - dome.appendChild(this.ttip); + this.get_view().getDomRef().appendChild(this.ttip); // Setup some event pre-handlers let glc = this; @@ -229,11 +266,11 @@ sap.ui.define([ glc.removeMouseupListener(); - if (event.buttons === 0) { + if (event.buttons === 0 && event.srcElement === glc.canvas.canvasDOM) { glc.removeMouseMoveTimeout(); glc.mousemove_timeout = setTimeout(glc.onMouseMoveTimeout.bind(glc, event.offsetX, event.offsetY), glc.controller.htimeout); } else { - glc.clearHighlight(); + // glc.clearHighlight(); } }); @@ -247,7 +284,7 @@ sap.ui.define([ dome.addEventListener('pointerdown', function(event) { glc.removeMouseMoveTimeout(); - if (event.buttons != 1 && event.buttons != 2) glc.clearHighlight(); + if (event.button != 0 && event.button != 2) glc.clearHighlight(); glc.removeMouseupListener(); // console.log("GLC::mousedown", this, glc, event, event.offsetX, event.offsetY); @@ -256,11 +293,11 @@ sap.ui.define([ { this.removeEventListener('pointerup', glc.mouseup_listener); - if (event2.buttons == 1) // Selection on mouseup without move + if (event2.button == 0) // Selection on mouseup without move { glc.handleMouseSelect(event2); } - else if (event.buttons == 2) // Context menu on delay without move + else if (event2.button == 2) // Context menu on delay without move { EVE.JSR.createMenu(event2, glc).then(menu => glc.showContextMenu(event2, menu)); } @@ -277,7 +314,6 @@ sap.ui.define([ // Key-handlers go on window ... window.addEventListener('keydown', function(event) { - // console.log("GLC::keydown", event.key, event.code, event); let handled = true; @@ -331,10 +367,28 @@ sap.ui.define([ } }); - // Was RC.ReveCameraControls - this.controls = new THREE.OrbitControls(this.camera, this.get_view().getDomRef()); + this.controls = new RC.REveCameraControls(this.camera, this.canvas.canvasDOM); this.controls.addEventListener('change', this.render.bind(this)); + // camera center marker + let col = new RC.Color(0.5, 0, 0); + const msize = this.RQ_SSAA * 8; // marker size + let sm = new RC.ZSpriteBasicMaterial({ + SpriteMode: RC.SPRITE_SPACE_SCREEN, SpriteSize: [msize, msize], + color: this.ColorBlack, + emissive: col, + diffuse: col.clone().multiplyScalar(0.5) + } + ); + let vtx = new Float32Array(3); + vtx[0] = 0; vtx[1] = 0; vtx[2] = 0; + let s = new RC.ZSprite(null, sm); + s.instanced = false; + s.position.set(0, 0, 0); + s.visible = false; + this.scene.add(s); + this.controls.centerMarker = s; + // This will also call render(). this.resetRenderer(); } @@ -357,31 +411,27 @@ sap.ui.define([ let extV = new RC.Vector3; extV = negV; extV.negate(); extV.max(posV); let extR = extV.length(); - console.log("GlViewerRenderCore.resetRenderer", sbbox, posV, negV, extV, extR); + if (this._logLevel >= 2) + console.log("GlViewerRenderCore.resetRenderer", sbbox, posV, negV, extV, extR); - if (this.controller.kind === "3D") // (this.camera.isPerspectiveCamera) + if (this.camera.isPerspectiveCamera) { - let posC = new RC.Vector3(-0.7 * extR, 0.5 * extR, -0.7 * extR); - - this.camera.position.copy(posC); - this.camera.lookAt(new RC.Vector3(0,0,0), new RC.Vector3(0,1,0)); - + this.controls.setCamBaseMtx(new RC.Vector3(-1, 0, 0), new RC.Vector3(0, 1, 0)); //XOZ floor this.controls.screenSpacePanning = true; let lc = this.lights.children; - lc[1].position.set( extR, extR, -extR); lc[1].decay = 4 * extR; - lc[2].position.set(-extR, extR, extR); lc[2].decay = 4 * extR; - lc[3].position.set( extR, extR, extR); lc[3].decay = 4 * extR; - lc[4].position.set(-extR, extR, -extR); lc[4].decay = 4 * extR; - - // console.log("resetThreejsRenderer 3D scene bbox ", sbbox, ", camera_pos ", posC, ", look_at ", this.rot_center); + // lights are const now -- no need to set decay and distance + lc[1].position.set( extR, extR, -extR); + lc[2].position.set(-extR, extR, extR); + lc[3].position.set( extR, extR, extR); + lc[4].position.set(-extR, extR, -extR); + lc[5].position.set(0, -extR, 0); + + // console.log("resetRenderer 3D scene bbox ", sbbox, ", look_at ", this.rot_center); } else { - let posC = new RC.Vector3(0, 0, extR); - - this.camera.position.copy(posC); - + this.controls.setCamBaseMtx(new RC.Vector3(0, 0, 1), new RC.Vector3(0, 1, 0)); // XOY floor let ey = 1.02 * extV.y; let ex = ey / this.get_height() * this.get_width(); this.camera._left = -ex; @@ -398,66 +448,196 @@ sap.ui.define([ let lc = this.lights.children; lc[1].position.set( 0, 0, extR); - lc[2].position.set( 0, 0, -extR); + // lc[2].position.set( 0, 0, -extR); - // console.log("resetThreejsRenderer 2D scene bbox ex ey", sbbox, ex, ey, ", camera_pos ", posC, ", look_at ", this.rot_center); + // console.log("resetRenderer 2D scene bbox ex ey", sbbox, ex, ey, ", camera_pos ", posC, ", look_at ", this.rot_center); } - this.controls.target.copy( this.rot_center ); - // this.composer.reset(); + this.controls.setFromBBox(sbbox); this.controls.update(); } //============================================================================== + request_render() + { + // console.log("REQUEST RENDER"); + + if (this.render_requested) return; + setTimeout(this.render.bind(this), 0); + this.render_requested = true; + } + render() { // console.log("RENDER", this.scene, this.camera, this.canvas, this.renderer); - if (this.UseRenderQueue) - this.rqt.render(); - else - this.renderer.render( this.scene, this.camera ); + this.render_requested = false; + + if (this.canvas.width <= 0 || this.canvas.height <= 0) return; + + this.rqt.render_begin(true); + + // Render outlines for active selections. + + for (let sel_id of this._selection_list) + { + let sel_entry = this._selection_map[ sel_id ]; + + let obj_list = this.rqt.RP_GBuffer.obj_list; + // let instance_list = []; + + for (let el_idx in sel_entry) + { + let el_entry = sel_entry[ el_idx ]; + // take all geometry objects, then we have to treat them differently, depending on type. + // and update world-matrix / check visibility + // or setup secondary indices for sub-instance drawing + + if (el_entry.instance_object) { + // instance_list.push(el_entry.instance_object); + obj_list.push(el_entry.instance_object); + el_entry.instance_object.outlineMaterial.outline_instances_setup( el_entry.instance_sec_idcs ); + } else { + for (let geo of el_entry.geom) { + if (geo === undefined) + console.warning("Processing viewer selection, undefined object for element", this.mgr.GetElement(el_idx)); + else + obj_list.push(geo); + } + } + } + + if (obj_list.length == 0) + continue; + + // Extract edge color (note, root colors), width from selection object. + let sel_object = this.get_manager().GetElement(sel_id); + let c = this.creator.RcCol(sel_object.fVisibleEdgeColor); + this.rqt.RP_Outline_mat.setUniform("edgeColor", [ 2*c.r, 2*c.g, 2*c.b, 1 ]); + this.rqt.render_outline(); + + // for (const obj of instance_list) { + // obj.outlineMaterial.outline_instances_reset(); + // } + + this.rqt.RP_GBuffer.obj_list = []; + } + + this.rqt.render_main_and_blend_outline(); + + if (this.rqt.queue.used_fail_count == 0) { + this.rqt.render_tone_map_to_screen(); + // this.rqt.render_final_to_screen(); + } + + this.rqt.render_end(); + + if (this.rqt.queue.used_fail_count > 0) { + if (this._logLevel >= 2) + console.log("GlViewerRCore render: not all programs compiled -- setting up render timer"); + setTimeout(this.render.bind(this), 200); + } // if (this.controller.kind === "3D") // window.requestAnimationFrame(this.render.bind(this)); } - render_for_picking(x, y) + render_for_picking(x, y, detect_depth) { - console.log("RENDER FOR PICKING", this.scene, this.camera, this.canvas, this.renderer); - let o3d; + // console.log("RENDER FOR PICKING", this.scene, this.camera, this.canvas, this.renderer); + + if (this.canvas.width <= 0 || this.canvas.height <= 0) return null; - this.renderer.pick(x, y, function(id) { o3d = id; } ); - this.rqt.pick(this.scene, this.camera); + this.rqt.pick_begin(x, y); - // Render to FBO or texture would work. - // let d = pthis.renderer.pickedDepth; - console.log("pick result", o3d /* , d */); - return o3d; + let state = this.rqt.pick(x, y, detect_depth); + + if (state.object === null) { + this.rqt.pick_end(); + return null; + } + + let top_obj = state.object; + while (top_obj.eve_el === undefined) + top_obj = top_obj.parent; + + state.top_object = top_obj; + state.eve_el = top_obj.eve_el; + + if (state.eve_el.fSecondarySelect) + this.rqt.pick_instance(state); + + this.rqt.pick_end(); + + state.w = this.canvas.width; + state.h = this.canvas.height; + state.mouse = new RC.Vector2( ((x + 0.5) / state.w) * 2 - 1, + -((y + 0.5) / state.h) * 2 + 1 ); + + let ctrl_obj = state.object; + while (ctrl_obj.get_ctrl === undefined) + ctrl_obj = ctrl_obj.parent; + + state.ctrl = ctrl_obj.get_ctrl(ctrl_obj, top_obj); + + // console.log("pick result", state); + return state; } //============================================================================== - onResizeTimeout() + get selection_map() { return this._selection_map; } + + remove_selection_from_list(sid) { - let w = this.get_width(); - let h = this.get_height(); + let idx = this._selection_list.indexOf(sid); + if (idx >= 0) + this._selection_list.splice(idx,1); + } - //console.log("GlViewerRCore onResizeTimeout", w, h, "canvas=", this.canvas, this.canvas.width, this.canvas.height); + make_selection_last_in_list(sid) + { + this.remove_selection_from_list(sid); + this._selection_list.push(sid); + } - this.canvas.width = w; - this.canvas.height = h; + //============================================================================== - this.camera.aspect = w / h; + fixCssSize() { + let s = this.canvas.canvasDOM.style; + s.width = this.canvas.canvasDOM.clientWidth + "px"; + s.height = this.canvas.canvasDOM.clientHeight + "px"; + } - this.renderer.updateViewport(w, h); + floatCssSize() { + let s = this.canvas.canvasDOM.style; + s.width = "100%"; + s.height = "100%"; + } - if (this.UseRenderQueue) this.rqt.updateViewport(w, h); + onResizeTimeout() + { + if ( ! this.canvas) { + if (this._logLevel >= 2) + console.log("GlViewerRCore onResizeTimeout -- canvas is not set yet."); + return; + } + + this.floatCssSize(); + this.canvas.updateSize(); + let w = this.canvas.width; + let h = this.canvas.height; + //console.log("GlViewerRCore onResizeTimeout", w, h, "canvas=", this.canvas, this.canvas.width, this.canvas.height); + this.camera.aspect = w / h; + this.rqt.updateViewport(w, h); this.controls.update(); + this.render(); + + this.fixCssSize(); } @@ -471,10 +651,10 @@ sap.ui.define([ clearHighlight() { - if (this.highlighted_scene) + if (this.highlighted_top_object) { - this.highlighted_scene.clearHighlight(); // XXXX should go through manager - this.highlighted_scene = 0; + this.highlighted_top_object.scene.clearHighlight(); // XXXX should go through manager + this.highlighted_top_object = null; this.ttip.style.display = "none"; } @@ -489,87 +669,59 @@ sap.ui.define([ } } - /** Get three.js intersect object at specified mouse position */ - getIntersectAt(x, y) - { - console.log("GLC::onMouseMoveTimeout", x, y); - - let o3d = this.render_for_picking(x, y); - if (o3d) - { - let w = this.get_width(); - let h = this.get_height(); - let mouse = new RC.Vector2( ((x + 0.5) / w) * 2 - 1, -((y + 0.5) / h) * 2 + 1 ); - return { object: o3d, mouse: mouse, w: w, h: h }; - } - else - return null; - - /* - let mouse = new RC.Vector2( ((x + 0.5) / w) * 2 - 1, -((y + 0.5) / h) * 2 + 1 ); - - this.raycaster.setFromCamera(mouse, this.camera); - - let intersects = this.raycaster.intersectObjects(this.scene.children, true); - - let o = null, c = null; - - for (let i = 0; i < intersects.length; ++i) - { - if (intersects[i].object.get_ctrl) - { - intersects[i].mouse = mouse; - intersects[i].w = w; - intersects[i].h = h; - return intersects[i]; - } - } - */ - } - onMouseMoveTimeout(x, y) { delete this.mousemove_timeout; - let intersect = this.getIntersectAt(x,y); + let pstate = this.render_for_picking(x * this.canvas.pixelRatio, y * this.canvas.pixelRatio, false); - if ( ! intersect) + if ( ! pstate) return this.clearHighlight(); - let c = intersect.object.get_ctrl(); - - let mouse = intersect.mouse; + let c = pstate.ctrl; + let idx = c.extractIndex(pstate.instance); - // c.elementHighlighted(c.extractIndex(intersect)); + c.elementHighlighted(idx, null); - this.highlighted_scene = c.obj3d.scene; - - if (c.obj3d && c.obj3d.eve_el) - this.ttip_text.innerHTML = c.getTooltipText(intersect); - else - this.ttip_text.innerHTML = ""; + if (this.highlighted_top_object !== pstate.top_object) + { + if (pstate.object && pstate.eve_el) + this.ttip_text.innerHTML = c.getTooltipText(idx); + else + this.ttip_text.innerHTML = ""; + } + this.highlighted_top_object = pstate.top_object; - let dome = this.controller.getView().getDomRef(); - let offs = (mouse.x > 0 || mouse.y < 0) ? this.getRelativeOffsets(dome) : null; + let dome = this.controller.getView().getDomRef(); + let mouse = pstate.mouse; + let offs = (mouse.x > 0 || mouse.y < 0) ? this.getRelativeOffsets(dome) : null; if (mouse.x <= 0) { this.ttip.style.left = (x + dome.offsetLeft + 10) + "px"; this.ttip.style.right = null; } else { - this.ttip.style.right = (intersect.w - x + offs.right + 10) + "px"; + this.ttip.style.right = (this.canvas.canvasDOM.clientWidth - x + offs.right + 10) + "px"; this.ttip.style.left = null; } if (mouse.y >= 0) { this.ttip.style.top = (y + dome.offsetTop + 10) + "px"; this.ttip.style.bottom = null; } else { - this.ttip.style.bottom = (intersect.h - y + offs.bottom + 10) + "px"; + this.ttip.style.bottom = (this.canvas.canvasDOM.clientHeight - y + offs.bottom + 10) + "px"; this.ttip.style.top = null; } this.ttip.style.display= "block"; } + remoteToolTip(msg) + { + if (this.ttip_text) + this.ttip_text.innerHTML = msg; + if (this.highlighted_top_object && this.ttip) + this.ttip.style.display = "block"; + } + getRelativeOffsets(elem) { // Based on: @@ -601,7 +753,7 @@ sap.ui.define([ { if (this.mouseup_listener) { - this.get_view().getDomRef().removeEventListener('pointerup', this.mouseup_listener); + this.canvas.canvasDOM.removeEventListener('pointerup', this.mouseup_listener); this.mouseup_listener = 0; } } @@ -612,43 +764,62 @@ sap.ui.define([ // See js/modules/menu/menu.mjs createMenu(), menu.add() - let intersect = this.getIntersectAt(event.offsetX, event.offsetY); + let x = event.offsetX * this.canvas.pixelRatio; + let y = event.offsetY * this.canvas.pixelRatio; + let pstate = this.render_for_picking(x, y, true); menu.add("header:Context Menu"); - if (intersect) { - if (intersect.object.eve_el) - menu.add("Browse to " + (intersect.object.eve_el.fName || "element"), intersect.object.eve_el.fElementId, this.controller.invokeBrowseOf.bind(this.controller)); + if (pstate) { + if (pstate.eve_el) + menu.add("Browse to " + (pstate.eve_el.fName || "element"), pstate.eve_el.fElementId, this.controller.invokeBrowseOf.bind(this.controller)); + + let data = { "p": pstate, "v": this, "cctrl": this.controls}; + menu.add("Set Camera Center", data, this.setCameraCenter.bind(data)); } menu.add("Reset camera", this.resetRenderer); - menu.add("separator"); - - let fff = this.defaultContextMenuAction; - menu.add("sub:Sub Test"); - menu.add("Foo", 'foo', fff); - menu.add("Bar", 'bar', fff); - menu.add("Baz", 'baz', fff); - menu.add("endsub:"); + if (RC.REveDevelMode) { + menu.add("separator"); + menu.add("Show program names", 'names', this.showShaderJson); + menu.add("Show programs", 'progs', this.showShaderJson); + } menu.show(event); } - defaultContextMenuAction(arg) + setCameraCenter(data) { - console.log("GLC::defaultContextMenuAction", this, arg); + let pthis = data.v; + + let fov_rad_half = pthis.camera.fov * 0.5 * (Math.PI/180); + let ftan = Math.tan(fov_rad_half); + let x = data.p.mouse.x * data.p.w / data.p.h * data.p.depth * ftan; + let y = data.p.mouse.y * data.p.depth * ftan; + let e = new RC.Vector4(-data.p.depth, x, y, 1); + + // console.log("picked point >>> ", x, y, data.p.depth); + // console.log("picked camera vector ", e); + // pthis.camera.testMtx.dump(); + + e.applyMatrix4(pthis.camera.testMtx); + // console.log("picked word view coordinates ", e); + + pthis.controls.setCameraCenter(e.x, e.y, e.z); + pthis.request_render(); } handleMouseSelect(event) { - let intersect = this.getIntersectAt(event.offsetX, event.offsetY); - - if (intersect) { - let c = intersect.object.get_ctrl(); - c.event = event; - c.elementSelected(c.extractIndex(intersect)); - this.highlighted_scene = intersect.object.scene; + let x = event.offsetX * this.canvas.pixelRatio; + let y = event.offsetY * this.canvas.pixelRatio; + let pstate = this.render_for_picking(x, y, false); + + if (pstate) { + let c = pstate.ctrl; + c.elementSelected(c.extractIndex(pstate.instance), event); + // WHY ??? this.highlighted_scene = pstate.top_object.scene; } else { // XXXX HACK - handlersMIR senders should really be in the mgr @@ -656,6 +827,69 @@ sap.ui.define([ } } + timeStampAttributesAndTextures() { + try { + this.renderer.glManager._textureManager.incrementTime(); + this.renderer.glManager._attributeManager.incrementTime(); + } + catch (e) { + console.error("Exception caught in timeStampAttributesAndTextures.", e); + } + } + + clearAttributesAndTextures() { + try { + let delta = 2; + this.renderer.glManager._textureManager.deleteTextures(true, delta); + this.renderer.glManager._attributeManager.deleteBuffers(true, delta); + } + catch (e) { + console.error("Exception caught in clearAttributesAndTextures.", e); + } + } + + static showShaderCount = 0; + showShaderJson(arg) + { + let progs = RC.ShaderLoader.sAllPrograms; + let json; + if (arg == "names") { + let names = []; + for (const p of progs) names.push(p); + json = JSON.stringify(names, null, 2); + } else if (arg == "progs") { + let shdrs = this.rqt.renderer._shaderLoader.resolvePrograms( progs ); + json = JSON.stringify(shdrs, null, 2); + } else { + json = "bad option '" + arg + "' to GlViewerRCore.showShaderJson"; + } + + let count = GlViewerRCore.showShaderCount++; + let extra = count ? ("-" + count) : ""; + + const win = window.open("", "rcore.programs.json"); + win.document.open(); + win.document.write(` + + programs.json + + + +
+ + +
+
 ${json} 
+ + `); + win.document.close(); + } } // class GlViewerRCore return GlViewerRCore; diff --git a/ui5/eve7/lib/GlViewerThree.js b/ui5/eve7/lib/GlViewerThree.js index f1c413fa2037a..348adfdbd4413 100644 --- a/ui5/eve7/lib/GlViewerThree.js +++ b/ui5/eve7/lib/GlViewerThree.js @@ -65,6 +65,8 @@ sap.ui.define([ return this.scene; } + get outline_map() { return this.outline_pass.id2obj_map; } + //============================================================================== // THREE renderer creation, DOM/event handler setup, reset //============================================================================== diff --git a/ui5/eve7/lib/RendeQuTor.js b/ui5/eve7/lib/RendeQuTor.js deleted file mode 100644 index fcde8a65e0c07..0000000000000 --- a/ui5/eve7/lib/RendeQuTor.js +++ /dev/null @@ -1,500 +0,0 @@ -import * as RC from "../rnr_core/RenderCore.js"; - -function iterateSceneR(object, callback) -{ - // if (object === null || object === undefined) return; - - if (object.children.length > 0) { - for (let i = 0; i < object.children.length; i++) { - iterateSceneR(object.children[i], callback); - } - } - - callback(object); -} - -export class RendeQuTor -{ - constructor(renderer, scene, camera) - { - this.renderer = renderer; - this.scene = scene; - this.camera = camera; - this.queue = new RC.RenderQueue(renderer); - this.pqueue = new RC.RenderQueue(renderer); - this.make_PRP_plain(); - - // Depth extraction somewhat works , get float but in some unknown coordinates :) - // If you enable this, also enable EXT_color_buffer_float in GlViewerRCore.createRCoreRenderer - // this.make_PRP_depth2r(); - // See also comments in shaders/custom/copyDepth2RReve.frag - - this.SSAA_value = 1; - - const nearPlane = 0.0625; // XXXX - pass to view_setup(vport, nfclip) - const farPlane = 8192; // XXXX - - // Why object.pickable === false in Initialize functions ??? - // How is outline supposed to work ??? - // Picking ??? - - this.OriginalMats = []; - this.MultiMats = []; - - // RenderQueue ... subclass or envelop? - // For every pass, store object + resize behaviour - } - - initDirectToScreen() - { - this.make_RP_DirectToScreen(); - } - - initSimple(ssaa_val) - { - this.SSAA_value = ssaa_val; - - this.make_RP_SSAA_Super(); - //this.make_RP_SSAA_Down(); - this.make_RP_ToScreen(); - this.RP_ToScreen.input_texture = "color_ssaa_super"; - } - - initFull(ssaa_val) - { - this.SSAA_value = ssaa_val; - - this.make_RP_SSAA_Super(); - this.make_RP_HighPassGaussBloom(); - // this.make_RP_SSAA_Down(); this.RP_SSAA_Down.input_texture = "color_bloom"; - this.make_RP_ToScreen(); - this.RP_ToScreen.input_texture = "color_bloom"; - } - - updateViewport(w, h) - { - let vp = { width: w, height: h }; - let rq = this.queue._renderQueue; - for (let i = 0; i < rq.length; i++) - { - rq[i].view_setup(vp); - } - rq = this.pqueue._renderQueue; - for (let i = 0; i < rq.length; i++) - { - rq[i].view_setup(vp); - } - } - - render() - { - this.queue.render(); - } - - pick() - { - let foo = this.pqueue.render(); - console.log(foo); - - { - let glman = this.renderer.glManager; - let gl = this.renderer.gl; - let texref = this.pqueue._textureMap["depthr32f_picking"]; - let tex = glman.getTexture(texref); - - console.log("Dumper:", glman, gl, texref, tex); - - const fb = gl.createFramebuffer(); - gl.bindFramebuffer(gl.FRAMEBUFFER, fb); - gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); - - let x = this.renderer._pickCoordinateX; - let y = this.renderer._canvas.height - this.renderer._pickCoordinateY; - console.log(x, y); - - let d = new Float32Array(9); - gl.readPixels(x-1, y-1, 3, 3, gl.RED, gl.FLOAT, d); - console.log("Pick depth at", x, ",", y, ":", d); - /* - let d = new Uint32Array(9); - gl.readPixels(x-1, y-1, 3, 3, gl.RED, gl.UNSIGNED_INT, d); - console.log("Pick depth:", d; - */ - - gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); - gl.deleteFramebuffer(fb); - } - } - - - //============================================================================= - // Picking RenderPasses - //============================================================================= - - make_PRP_plain() - { - var pthis = this; - - this.PRP_plain = new RC.RenderPass( - RC.RenderPass.BASIC, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { return { scene: pthis.scene, camera: pthis.camera }; }, - RC.RenderPass.TEXTURE, - null, - "depth_picking", - [ { id: "color_picking", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG } ] - ); - this.PRP_plain.view_setup = function (vport) { this.viewport = vport; }; - - this.pqueue.pushRenderPass(this.PRP_plain); - } - - make_PRP_depth2r() - { - this.PRP_depth2r_mat = new RC.CustomShaderMaterial("copyDepth2RReve"); - this.PRP_depth2r_mat.lights = false; - var pthis = this; - - this.PRP_depth2r = new RC.RenderPass( - RC.RenderPass.POSTPROCESS, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { - return { material: pthis.PRP_depth2r_mat, textures: [ textureMap["depth_picking"] ] }; - }, - RC.RenderPass.TEXTURE, - null, - null, - [ { id: "depthr32f_picking", textureConfig: RC.RenderPass.FULL_FLOAT_R32F_TEXTURE_CONFIG } ] - // [ { id: "depthr32f_picking", textureConfig: RC.RenderPass.DEFAULT_R32UI_TEXTURE_CONFIG } ] - ); - this.PRP_depth2r.view_setup = function (vport) { this.viewport = vport; }; - - this.pqueue.pushRenderPass(this.PRP_depth2r); - } - - //============================================================================= - - make_RP_DirectToScreen() - { - var pthis = this; - - this.RP_DirectToScreen = new RC.RenderPass( - RC.RenderPass.BASIC, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { return { scene: pthis.scene, camera: pthis.camera }; }, - RC.RenderPass.SCREEN, - null - ); - this.RP_DirectToScreen.view_setup = function (vport) { this.viewport = vport; }; - - this.queue.pushRenderPass(this.RP_DirectToScreen); - } - - //============================================================================= - - make_RP_SSAA_Super() - { - var pthis = this; - - this.RP_SSAA_Super = new RC.RenderPass( - // Rendering pass type - RC.RenderPass.BASIC, - - // Initialize function - function (textureMap, additionalData) { - iterateSceneR(pthis.scene, function(object){ - if (object.pickable === false || object instanceof RC.Text2D || object instanceof RC.IcoSphere) { - object.visible = true; - return; - } - pthis.OriginalMats.push(object.material); - }); - }, - - // Preprocess function - function (textureMap, additionalData) { - let m_index = 0; - - iterateSceneR(pthis.scene, function(object) { - if(object.pickable === false || object instanceof RC.Text2D || object instanceof RC.IcoSphere) { - object.visible = true; - return; - } - object.material = pthis.OriginalMats[m_index]; - m_index++; - }); - - return { scene: pthis.scene, camera: pthis.camera }; - }, - - // Target - RC.RenderPass.TEXTURE, - - // Viewport - null, - - // Bind depth texture to this ID - "depthDefaultDefaultMaterials", - - [ { id: "color_ssaa_super", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG } ] - ); - this.RP_SSAA_Super.view_setup = function (vport) { this.viewport = { width: vport.width*pthis.SSAA_value, height: vport.height*pthis.SSAA_value }; }; - - this.queue.pushRenderPass(this.RP_SSAA_Super); - } - - make_RP_SSAA_Down() - { - this.RP_SSAA_Down_mat = new RC.CustomShaderMaterial("copyTexture"); - this.RP_SSAA_Down_mat.lights = false; - var pthis = this; - - this.RP_SSAA_Down = new RC.RenderPass( - // Rendering pass type - RC.RenderPass.POSTPROCESS, - - // Initialize function - function (textureMap, additionalData) {}, - - // Preprocess function - function (textureMap, additionalData) { - return { material: pthis.RP_SSAA_Down_mat, textures: [textureMap[pthis.input_texture]] }; - }, - - // Target - RC.RenderPass.TEXTURE, - - // Viewport - null, - - // Bind depth texture to this ID - null, - - [ { id: "color_ssaa_down", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG } ] - ); - this.RP_SSAA_Down.input_texture = "color_ssaa_super"; - this.RP_SSAA_Down.view_setup = function(vport) { this.viewport = vport; }; - - this.queue.pushRenderPass(this.RP_SSAA_Down); - } - - //============================================================================= - - make_RP_ToScreen() - { - this.RP_ToScreen_mat = new RC.CustomShaderMaterial("copyTexture"); - this.RP_ToScreen_mat.lights = false; - var pthis = this; - - this.RP_ToScreen = new RC.RenderPass( - RC.RenderPass.POSTPROCESS, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { - return { material: pthis.RP_ToScreen_mat, textures: [ textureMap[this.input_texture] ] }; // XXXX pthis or this ???? - }, - RC.RenderPass.SCREEN, - null - ); - this.RP_ToScreen.input_texture = "color_ssaa_down"; - this.RP_ToScreen.view_setup = function(vport) { this.viewport = vport; }; - - this.queue.pushRenderPass(this.RP_ToScreen); - } - - //============================================================================= - - make_RP_HighPassGaussBloom() - { - var pthis = this; - // let hp = new RC.CustomShaderMaterial("highPass", {MODE: RC.HIGHPASS_MODE_BRIGHTNESS, targetColor: [0.2126, 0.7152, 0.0722], threshold: 0.75}); - let hp = new RC.CustomShaderMaterial("highPass", { MODE: RC.HIGHPASS_MODE_DIFFERENCE, - targetColor: [0x0/255, 0x0/255, 0xff/255], threshold: 0.1}); - console.log("XXXXXXXX", hp); - // let hp = new RC.CustomShaderMaterial("highPassReve"); - this.RP_HighPass_mat = hp; - this.RP_HighPass_mat.lights = false; - - this.RP_HighPass = new RC.RenderPass( - RC.RenderPass.POSTPROCESS, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { - return { material: pthis.RP_HighPass_mat, textures: [textureMap["color_ssaa_super"]] }; - }, - RC.RenderPass.TEXTURE, - null, - // XXXXXX MT: this was "dt", why not null ???? - null, // "dt", - [ {id: "color_high_pass", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} ] - ); - this.RP_HighPass.view_setup = function (vport) { this.viewport = { width: vport.width*pthis.SSAA_value, height: vport.height*pthis.SSAA_value }; }; - this.queue.pushRenderPass(this.RP_HighPass); - - this.RP_Gauss1_mat = new RC.CustomShaderMaterial("gaussBlur", {horizontal: true, power: 1.0}); - this.RP_Gauss1_mat.lights = false; - - this.RP_Gauss1 = new RC.RenderPass( - RC.RenderPass.POSTPROCESS, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { - return { material: pthis.RP_Gauss1_mat, textures: [textureMap["color_high_pass"]] }; - }, - RC.RenderPass.TEXTURE, - null, - null, - [ {id: "color_gauss_half", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} ] - ); - this.RP_Gauss1.view_setup = function (vport) { this.viewport = { width: vport.width*pthis.SSAA_value, height: vport.height*pthis.SSAA_value }; }; - this.queue.pushRenderPass(this.RP_Gauss1); - - this.RP_Gauss2_mat = new RC.CustomShaderMaterial("gaussBlur", {horizontal: false, power: 1.0}); - this.RP_Gauss2_mat.lights = false; - - this.RP_Gauss2 = new RC.RenderPass( - RC.RenderPass.POSTPROCESS, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { - return { material: pthis.RP_Gauss2_mat, textures: [textureMap["color_gauss_half"]] }; - }, - RC.RenderPass.TEXTURE, - null, - null, - [ {id: "color_gauss_full", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} ] - ); - this.RP_Gauss2.view_setup = function (vport) { this.viewport = { width: vport.width*pthis.SSAA_value, height: vport.height*pthis.SSAA_value }; }; - this.queue.pushRenderPass(this.RP_Gauss2); - - this.RP_Bloom_mat = new RC.CustomShaderMaterial("bloom"); - this.RP_Bloom_mat.lights = false; - - this.RP_Bloom = new RC.RenderPass( - RC.RenderPass.POSTPROCESS, - function (textureMap, additionalData) {}, - function (textureMap, additionalData) { - return { material: pthis.RP_Bloom_mat, textures: [textureMap["color_gauss_full"], textureMap["color_ssaa_super"]] }; - }, - RC.RenderPass.TEXTURE, - null, - null, - [ {id: "color_bloom", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} ] - ); - this.RP_Bloom.view_setup = function (vport) { this.viewport = { width: vport.width*pthis.SSAA_value, height: vport.height*pthis.SSAA_value }; }; - this.queue.pushRenderPass(this.RP_Bloom); - } -}; - - -/* -export const RenderPass_MainMulti = new RC.RenderPass( - // Rendering pass type - RC.RenderPass.BASIC, - - // Initialize function - function (textureMap, additionalData) { - iterateSceneR(scene, function(object){ - if(object.pickable === false || object instanceof RC.Text2D || object instanceof RC.IcoSphere) { - object.visible = false; - //GL_INVALID_OPERATION : glDrawElementsInstancedANGLE: buffer format and fragment output variable type incompatible - //Program has no frag output at location 1, but destination draw buffer has an attached image. - return; - } - const multi = new RC.CustomShaderMaterial("multi", {near: nearPlane, far: farPlane}); - multi.side = RC.FRONT_AND_BACK_SIDE; //reather use depth from default materials - MultiMats.push(multi); - }); - }, - - // Preprocess function - function (textureMap, additionalData) { - let m_index = 0; - - iterateSceneR(scene, function(object){ - if(object.pickable === false || object instanceof RC.Text2D || object instanceof RC.IcoSphere) { - object.visible = false; - return; - } - object.material = MultiMats[m_index]; - m_index++; - }); - - - return { scene: scene, camera: camera }; - }, - - // Target - RC.RenderPass.TEXTURE, - - // Viewport - { width: predef_width*SSAA_value, height: predef_height*SSAA_value }, - - // Bind depth texture to this ID - "depthDefaultMultiMaterials", - - [ - {id: "depth", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG}, - {id: "normal", textureConfig: RC.RenderPass.DEFAULT_RGB_TEXTURE_CONFIG}, - {id: "viewDir", textureConfig: RC.RenderPass.DEFAULT_RGB_TEXTURE_CONFIG}, - {id: "camDist", textureConfig: RC.RenderPass.DEFAULT_RGBA16F_TEXTURE_CONFIG} - ] - ); -*/ - -/* -const outline = new RC.CustomShaderMaterial("outline", {scale: 1.0*SSAA_value, edgeColor: [1.0, 1.0, 1.0, 1.0]}); -outline.lights = false; -export const RenderPass_Outline = new RC.RenderPass( - // Rendering pass type - RC.RenderPass.POSTPROCESS, - - // Initialize function - function (textureMap, additionalData) { - }, - - // Preprocess function - function (textureMap, additionalData) { - return {material: outline, textures: [textureMap["depthDefaultMultiMaterials"], textureMap["normal"], textureMap["viewDir"], textureMap["color_bloom"]]}; - }, - - // Target - RC.RenderPass.TEXTURE, - - // Viewport - { width: predef_width*SSAA_value, height: predef_height*SSAA_value }, - - // Bind depth texture to this ID - null, - - [ - {id: "color_outline", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} - ] - ); - -const fog = new RC.CustomShaderMaterial("fog", {MODE: 1, fogColor: [0.5, 0.4, 0.45, 0.8]}); -fog.lights = false; -export const RenderPass_Fog = new RC.RenderPass( - // Rendering pass type - RC.RenderPass.POSTPROCESS, - - // Initialize function - function (textureMap, additionalData) { - }, - - // Preprocess function - function (textureMap, additionalData) { - //return {material: fog, textures: [textureMap["color_outline"], textureMap["depthDefaultDefaultMaterials"]]}; //grid jumps on depth buffer - return {material: fog, textures: [textureMap["color_outline"], textureMap["camDist"]]}; //grid has specific shader for extruding geometry, even if implemented, it would jump around - }, - - // Target - RC.RenderPass.TEXTURE, - - // Viewport - { width: predef_width*SSAA_value, height: predef_height*SSAA_value }, - - // Bind depth texture to this ID - null, - - [ - {id: "color_fog", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} - ] - ); -*/ \ No newline at end of file diff --git a/ui5/eve7/lib/RenderCore.js b/ui5/eve7/lib/RenderCore.js new file mode 100644 index 0000000000000..815c299388a7a --- /dev/null +++ b/ui5/eve7/lib/RenderCore.js @@ -0,0 +1,9 @@ +// Standard import from ROOT build +export * from '../rcore/REveRenderCore-min.mjs'; +export const REveShaderPath = "rcore/shaders/"; +export const REveDevelMode = false; + +// Development import from a RenderCore checkout in RC directory +// export * from '../RC/src/contrib/REveRenderCore.js'; +// export const REveShaderPath = "RC/src/shaders/"; +// export const REveDevelMode = true; diff --git a/ui5/eve7/shaders/custom/copyDepth2RReve.frag b/ui5/eve7/shaders/custom/copyDepth2RReve.frag deleted file mode 100644 index c95c4605eddb2..0000000000000 --- a/ui5/eve7/shaders/custom/copyDepth2RReve.frag +++ /dev/null @@ -1,36 +0,0 @@ -#version 300 es -precision highp float; -precision highp usampler2D; - -struct Material { - #if (TEXTURE) - // usampler2D texture0; // this fails in MeshRenderer, uniform setter for material - sampler2D texture0; - #fi -}; - -uniform Material material; - -#if (TEXTURE) - in vec2 fragUV; -#fi - -out vec4 color; - - -void main() { - #if (TEXTURE) - // color.r = 0.5370; - // color.r = fragUV.x; - // Logarithmic: (?) - // color.r = texture(material.texture0, fragUV).r; - // Linearize: (?) - color.r = pow(2.0, texture(material.texture0, fragUV).r) - 1.0; - - // Or is this something with 1/z or w or what. Argh. - // Perhaphs the best way forward is to: - // 1. Implement picking on reduced target, say 16x16 or even 8x8 - // 2. Attach float buffer to picking shader and write z that you want in there. - // Or be a total pig and attach 3 buffers and store world xyz :) - #fi -} \ No newline at end of file diff --git a/ui5/eve7/shaders/custom/copyDepth2RReve.vert b/ui5/eve7/shaders/custom/copyDepth2RReve.vert deleted file mode 100644 index 0ff3e68b0dc71..0000000000000 --- a/ui5/eve7/shaders/custom/copyDepth2RReve.vert +++ /dev/null @@ -1,20 +0,0 @@ -#version 300 es -precision highp float; - -in vec3 VPos; // Vertex position - -#if (TEXTURE) - in vec2 uv; // Texture coordinate -#fi - -// Output quad texture coordinates -out vec2 fragUV; - -void main() { - gl_Position = vec4(VPos, 1.0); - - #if (TEXTURE) - // Pass-through texture coordinate - fragUV = uv; - #fi -} \ No newline at end of file diff --git a/ui5/eve7/shaders/custom/highPassReve.frag b/ui5/eve7/shaders/custom/highPassReve.frag deleted file mode 100644 index 2bbecb46cef67..0000000000000 --- a/ui5/eve7/shaders/custom/highPassReve.frag +++ /dev/null @@ -1,37 +0,0 @@ -#version 300 es -precision mediump float; - - -struct Material { - #if (TEXTURE) - sampler2D texture0; - #fi -}; - - -uniform Material material; - -#if (TEXTURE) - in vec2 fragUV; -#fi - -//out vec4 color[2]; -out vec4 color; - - -void main() { - // check whether fragment output is higher than threshold, if so output as brightness color - float brightness = dot(texture(material.texture0, fragUV).rgb, vec3(0.2126, 0.7152, 0.0722)); - - if(brightness > 0.75) { - //color[0] = texture(material.texture0, fragUV); //texture(material.texture##I_TEX, fragUV) - color = texture(material.texture0, fragUV); //texture(material.texture##I_TEX, fragUV) - }else{ - //color[0] = vec4(0.0, 0.0, 0.0, 1.0); - color = vec4(0.0, 0.0, 0.0, 1.0); - } - - - //COPY - //color[1] = texture(material.texture0, fragUV); -} \ No newline at end of file diff --git a/ui5/eve7/shaders/custom/highPassReve.vert b/ui5/eve7/shaders/custom/highPassReve.vert deleted file mode 100644 index 0356489792427..0000000000000 --- a/ui5/eve7/shaders/custom/highPassReve.vert +++ /dev/null @@ -1,23 +0,0 @@ -#version 300 es -precision mediump float; - - -in vec3 VPos; // Vertex position - -#if (TEXTURE) - in vec2 uv; // Texture coordinate -#fi - -// Output quad texture coordinates -out vec2 fragUV; - - -void main() { - gl_Position = vec4(VPos, 1.0); - - - #if (TEXTURE) - // Pass-through texture coordinate - fragUV = uv; - #fi -} diff --git a/ui5/eve7/shaders/custom/lowPassReve.frag b/ui5/eve7/shaders/custom/lowPassReve.frag deleted file mode 100644 index 69887883b2210..0000000000000 --- a/ui5/eve7/shaders/custom/lowPassReve.frag +++ /dev/null @@ -1,37 +0,0 @@ -#version 300 es -precision mediump float; - - -struct Material { - #if (TEXTURE) - sampler2D texture0; - #fi -}; - - -uniform Material material; - -#if (TEXTURE) - in vec2 fragUV; -#fi - -//out vec4 color[2]; -out vec4 color; - - -void main() { - // check whether fragment output is higher than threshold, if so output as brightness color - float brightness = dot(texture(material.texture0, fragUV).rgb, vec3(0.2126, 0.7152, 0.0722)); - - if(brightness > 0.75){ - //color[0] = texture(material.texture0, fragUV); //texture(material.texture##I_TEX, fragUV) - color = texture(material.texture0, fragUV); //texture(material.texture##I_TEX, fragUV) - }else{ - //color[0] = vec4(0.0, 0.0, 0.0, 1.0); - color = vec4(0.0, 0.0, 0.0, 1.0); - } - - - //COPY - //color[1] = texture(material.texture0, fragUV); -} diff --git a/ui5/eve7/shaders/custom/lowPassReve.vert b/ui5/eve7/shaders/custom/lowPassReve.vert deleted file mode 100644 index 0356489792427..0000000000000 --- a/ui5/eve7/shaders/custom/lowPassReve.vert +++ /dev/null @@ -1,23 +0,0 @@ -#version 300 es -precision mediump float; - - -in vec3 VPos; // Vertex position - -#if (TEXTURE) - in vec2 uv; // Texture coordinate -#fi - -// Output quad texture coordinates -out vec2 fragUV; - - -void main() { - gl_Position = vec4(VPos, 1.0); - - - #if (TEXTURE) - // Pass-through texture coordinate - fragUV = uv; - #fi -} diff --git a/ui5/eve7/shaders/programs.json b/ui5/eve7/shaders/programs.json deleted file mode 100644 index eef8be861b9b1..0000000000000 --- a/ui5/eve7/shaders/programs.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "custom_highPassReve": { - "description": "High Pass Filter fo REve.", - "shaders": { - "vertex": "custom/highPassReve.vert", - "fragment": "custom/highPassReve.frag" - } - }, - "custom_lowPassReve": { - "description": "Low Pass Filter fo REve.", - "shaders": { - "vertex": "custom/post_process/lowPassReve.vert", - "fragment": "custom/post_process/lowPassReve.frag" - } - }, - - "custom_copyDepth2RReve" : { - "description": "Copy depth buffer values to R component (R32F) for picking depth extraction.", - "shaders": { - "vertex": "custom/copyDepth2RReve.vert", - "fragment": "custom/copyDepth2RReve.frag" - } - } -} diff --git a/ui5/eve7/textures/dot-32a.png b/ui5/eve7/textures/dot-32a.png new file mode 100644 index 0000000000000000000000000000000000000000..21487416e45748bd4ce513598f05ddbbf31a3059 GIT binary patch literal 5404 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 literal 0 HcmV?d00001 diff --git a/ui5/eve7/view/Summary.view.xml b/ui5/eve7/view/Summary.view.xml index f20c20e4bb628..4905b11a369ec 100644 --- a/ui5/eve7/view/Summary.view.xml +++ b/ui5/eve7/view/Summary.view.xml @@ -4,7 +4,7 @@ xmlns:l="sap.ui.layout" xmlns="sap.m" class="sapUiSizeCompact"> - + From 26f79b5172c80e13da98d8da4413b27c5496f70b Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 20 Dec 2022 09:28:57 +0100 Subject: [PATCH 2/2] [eve7] fix compiler warning with REvePointSet --- graf3d/eve7/src/REvePointSet.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/graf3d/eve7/src/REvePointSet.cxx b/graf3d/eve7/src/REvePointSet.cxx index 6af817cb3efd9..6b8413ca9e014 100644 --- a/graf3d/eve7/src/REvePointSet.cxx +++ b/graf3d/eve7/src/REvePointSet.cxx @@ -45,8 +45,10 @@ REveProjectionManager class. REvePointSet::REvePointSet(const std::string& name, const std::string& title, Int_t n_points) : REveElement(name, title), + REveProjectable(), TAttMarker(), - TAttBBox() + TAttBBox(), + REveSecondarySelectable() { fMarkerStyle = 20; @@ -65,7 +67,8 @@ REvePointSet::REvePointSet(const REvePointSet& e) : REveElement(e), REveProjectable(e), TAttMarker(e), - TAttBBox(e) + TAttBBox(e), + REveSecondarySelectable() { fAlwaysSecSelect = e.GetAlwaysSecSelect(); ClonePoints(e);