From 939340325396c80e8798ec43361ffd983ce325c9 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sun, 6 Jan 2013 14:01:15 -0800 Subject: [PATCH 01/19] BulletSim: update DLLs and SOs with better debugging output. Add definition of hand crafted avatar mesh. Not used yet. Comments and cleanup. --- .../Physics/BulletSPlugin/BSDynamics.cs | 4 +- .../Region/Physics/BulletSPlugin/BSShapes.cs | 145 +++++++++++++++++- .../Physics/BulletSPlugin/BulletSimData.cs | 2 + .../Physics/BulletSPlugin/BulletSimTODO.txt | 12 +- bin/lib32/BulletSim.dll | Bin 551936 -> 551936 bytes bin/lib32/libBulletSim.so | Bin 1715168 -> 1715168 bytes bin/lib64/BulletSim.dll | Bin 700928 -> 700928 bytes bin/lib64/libBulletSim.so | Bin 1857493 -> 1857493 bytes 8 files changed, 151 insertions(+), 12 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 13c253963b..a9fbc8bb6f 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -137,6 +137,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin get { return Type != Vehicle.TYPE_NONE && Prim.IsPhysical; } } + #region Vehicle parameter setting internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) { VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue); @@ -546,6 +547,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f); m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) } + #endregion // Vehicle parameter setting // Some of the properties of this prim may have changed. // Do any updating needed for a vehicle @@ -925,7 +927,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // TODO: Consider taking the rotated size of the object or possibly casting a ray. if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition)) { - // TODO: correct position by applying force rather than forcing position. + // Force position because applying force won't get the vehicle through the terrain Vector3 newPosition = VehiclePosition; newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f; VehiclePosition = newPosition; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index c75eb9bcb2..cc725e8bbb 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -27,24 +27,19 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; +using OMV = OpenMetaverse; + namespace OpenSim.Region.Physics.BulletSPlugin { public abstract class BSShape { - public IntPtr ptr { get; set; } - public BSPhysicsShapeType type { get; set; } - public System.UInt64 key { get; set; } public int referenceCount { get; set; } public DateTime lastReferenced { get; set; } public BSShape() { - ptr = IntPtr.Zero; - type = BSPhysicsShapeType.SHAPE_UNKNOWN; - key = 0; referenceCount = 0; lastReferenced = DateTime.Now; } @@ -63,7 +58,7 @@ public abstract class BSShape } // Compound shapes are handled special as they are rebuilt from scratch. - // This isn't too great a hardship since most of the child shapes will already been created. + // This isn't too great a hardship since most of the child shapes will have already been created. if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) { // Getting a reference to a compound shape gets you the compound shape with the root prim shape added @@ -71,6 +66,14 @@ public abstract class BSShape physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); } + // Avatars have their own unique shape + if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_AVATAR) + { + // Getting a reference to a compound shape gets you the compound shape with the root prim shape added + ret = BSShapeAvatar.GetReference(prim); + physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,avatarShape,shape={1}", prim.LocalID, ret); + } + if (ret == null) ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); @@ -228,5 +231,131 @@ public class BSShapeAvatar : BSShape return new BSShapeNull(); } public override void Dereference(BSScene physicsScene) { } + + // From the front: + // A---A + // / \ + // B-------B + // / \ +Z + // C-----------C | + // \ / -Y --+-- +Y + // \ / | + // \ / -Z + // D-----D + // \ / + // E-E + + // From the top A and E are just lines. + // B, C and D are hexagons: + // + // C1--C2 +X + // / \ | + // C0 C3 -Y --+-- +Y + // \ / | + // C5--C4 -X + + // Zero goes directly through the middle so the offsets are from that middle axis + // and up and down from a middle horizon (A and E are the same distance from the zero). + // The height, width and depth is one. All scaling is done by the simulator. + + // Z component -- how far the level is from the middle zero + private const float Aup = 0.5f; + private const float Bup = 0.4f; + private const float Cup = 0.3f; + private const float Dup = -0.4f; + private const float Eup = -0.5f; + + // Y component -- distance from center to x0 and x3 + private const float Awid = 0.25f; + private const float Bwid = 0.3f; + private const float Cwid = 0.5f; + private const float Dwid = 0.3f; + private const float Ewid = 0.2f; + + // Y component -- distance from center to x1, x2, x4 and x5 + private const float Afwid = 0.0f; + private const float Bfwid = 0.2f; + private const float Cfwid = 0.4f; + private const float Dfwid = 0.2f; + private const float Efwid = 0.0f; + + // X component -- distance from zero to the front or back of a level + private const float Adep = 0f; + private const float Bdep = 0.3f; + private const float Cdep = 0.5f; + private const float Ddep = 0.2f; + private const float Edep = 0f; + + private OMV.Vector3[] avatarVertices = { + new OMV.Vector3( 0.0f, -Awid, Aup), // A0 + new OMV.Vector3( 0.0f, +Awid, Aup), // A3 + + new OMV.Vector3( 0.0f, -Bwid, Bup), // B0 + new OMV.Vector3(+Bdep, -Bfwid, Bup), // B1 + new OMV.Vector3(+Bdep, +Bfwid, Bup), // B2 + new OMV.Vector3( 0.0f, +Bwid, Bup), // B3 + new OMV.Vector3(-Bdep, +Bfwid, Bup), // B4 + new OMV.Vector3(-Bdep, -Bfwid, Bup), // B5 + + new OMV.Vector3( 0.0f, -Cwid, Cup), // C0 + new OMV.Vector3(+Cdep, -Cfwid, Cup), // C1 + new OMV.Vector3(+Cdep, +Cfwid, Cup), // C2 + new OMV.Vector3( 0.0f, +Cwid, Cup), // C3 + new OMV.Vector3(-Cdep, +Cfwid, Cup), // C4 + new OMV.Vector3(-Cdep, -Cfwid, Cup), // C5 + + new OMV.Vector3( 0.0f, -Dwid, Dup), // D0 + new OMV.Vector3(+Ddep, -Dfwid, Dup), // D1 + new OMV.Vector3(+Ddep, +Dfwid, Dup), // D2 + new OMV.Vector3( 0.0f, +Dwid, Dup), // D3 + new OMV.Vector3(-Ddep, +Dfwid, Dup), // D4 + new OMV.Vector3(-Ddep, -Dfwid, Dup), // D5 + + new OMV.Vector3( 0.0f, -Ewid, Eup), // E0 + new OMV.Vector3( 0.0f, +Ewid, Eup), // E3 + }; + + // Offsets of the vertices in the vertices array + private enum Ind : int + { + A0, A3, + B0, B1, B2, B3, B4, B5, + C0, C1, C2, C3, C4, C5, + D0, D1, D2, D3, D4, D5, + E0, E3 + } + + // Comments specify trianges and quads in clockwise direction + private Ind[] avatarIndices = { + Ind.A0, Ind.B0, Ind.B1, // A0,B0,B1 + Ind.A0, Ind.B1, Ind.B2, Ind.B2, Ind.A3, Ind.A0, // A0,B1,B2,A3 + Ind.A3, Ind.B2, Ind.B3, // A3,B2,B3 + Ind.A3, Ind.B3, Ind.B4, // A3,B3,B4 + Ind.A3, Ind.B4, Ind.B5, Ind.B5, Ind.A0, Ind.A3, // A3,B4,B5,A0 + Ind.A0, Ind.B5, Ind.B0, // A0,B5,B0 + + Ind.B0, Ind.C0, Ind.C1, Ind.C1, Ind.B1, Ind.B0, // B0,C0,C1,B1 + Ind.B1, Ind.C1, Ind.C2, Ind.C2, Ind.B2, Ind.B1, // B1,C1,C2,B2 + Ind.B2, Ind.C2, Ind.C3, Ind.C3, Ind.B3, Ind.B2, // B2,C2,C3,B3 + Ind.B3, Ind.C3, Ind.C4, Ind.C4, Ind.B4, Ind.B3, // B3,C3,C4,B4 + Ind.B4, Ind.C4, Ind.C5, Ind.C5, Ind.B5, Ind.B4, // B4,C4,C5,B5 + Ind.B5, Ind.C5, Ind.C0, Ind.C0, Ind.B0, Ind.B5, // B5,C5,C0,B0 + + Ind.C0, Ind.D0, Ind.D1, Ind.D1, Ind.C1, Ind.C0, // C0,D0,D1,C1 + Ind.C1, Ind.D1, Ind.D2, Ind.D2, Ind.C2, Ind.C1, // C1,D1,D2,C2 + Ind.C2, Ind.D2, Ind.D3, Ind.D3, Ind.C3, Ind.C2, // C2,D2,D3,C3 + Ind.C3, Ind.D3, Ind.D4, Ind.D4, Ind.C4, Ind.C3, // C3,D3,D4,C4 + Ind.C4, Ind.D4, Ind.D5, Ind.D5, Ind.C5, Ind.C4, // C4,D4,D5,C5 + Ind.C5, Ind.D5, Ind.D0, Ind.D0, Ind.C0, Ind.C5, // C5,D5,D0,C0 + + Ind.E0, Ind.D0, Ind.D1, // E0,D0,D1 + Ind.E0, Ind.D1, Ind.D2, Ind.D2, Ind.E3, Ind.E0, // E0,D1,D2,E3 + Ind.E3, Ind.D2, Ind.D3, // E3,D2,D3 + Ind.E3, Ind.D3, Ind.D4, // E3,D3,D4 + Ind.E3, Ind.D4, Ind.D5, Ind.D5, Ind.E0, Ind.E3, // E3,D4,D5,E0 + Ind.E0, Ind.D5, Ind.D0, // E0,D5,D0 + + }; + } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs index 662dd68fe6..c7a2f7e585 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs @@ -114,8 +114,10 @@ public class BulletShape public virtual void Clear() { } public virtual bool HasPhysicalShape { get { return false; } } + // Make another reference to this physical object. public virtual BulletShape Clone() { return new BulletShape(); } + // Return 'true' if this and other refer to the same physical object public virtual bool ReferenceSame(BulletShape xx) { return false; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index facf720267..7b59e60e6e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -1,8 +1,6 @@ CURRENT PRIORITIES ================================================= -Redo BulletSimAPI to allow native C# implementation of Bullet option (DONE) -Meshes rendering as bounding boxes -llMoveToTarget +Avatars walking up stairs Vehicle movement on terrain smoothness limitMotorUp calibration (more down?) Preferred orientatino angular correction fix @@ -135,6 +133,9 @@ Eliminate collisions between objects in a linkset. (LinksetConstraint) MORE ====================================================== +Use the HACD convex hull routine in Bullet rather than the C# version. +Do we need to do convex hulls all the time? Can complex meshes be left meshes? + There is some problem with meshes and collisions Test avatar walking up stairs. How does compare with SL. Radius of the capsule affects ability to climb edges. Debounce avatar contact so legs don't keep folding up when standing. @@ -274,3 +275,8 @@ llSetBuoyancy() (DONE) (Resolution: Bullet resets object gravity when added to world. Moved set gravity) Avatar density is WAY off. Compare and calibrate with what's in SL. (DONE) (Resolution: set default density to 3.5 (from 60) which is closer to SL) +Redo BulletSimAPI to allow native C# implementation of Bullet option (DONE) + (Resolution: added BSAPITemplate and then interfaces for C++ Bullet and C# BulletXNA +Meshes rendering as bounding boxes (DONE) + (Resolution: Added test for mesh/sculpties in native shapes so it didn't think it was a box) +llMoveToTarget (Resolution: added simple motor to update the position.) diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index b01896e547633fd768288a3e270662612d240067..093ea3102720b75f98a3a9a5b0d6c64e2cea20d2 100755 GIT binary patch delta 7748 zcmd^^dwfjS+Q(<@*_VY}1__BolgJ=Q2#I7SnTg!o6Cxr_+ql*x&QYQgmo}{&i6f+n zZY)*jEyty)(nd$^P&9~3TQoFfMuSjVK?yCQMc&_<&_@1x|9|K6`OaF;dM<12XWjNB z`SyT(d%*H~{du8w#h=M~{T8YYB^}$9CQ=>o)ai8ns4fMCE}QChk`)1M)fJ>G8Kmwe zW62JixA2`n>}svJnFOiJB(D(vzSMBB=6;3rMw@CW2!bQiC^{02V)50{hpIhwPt|d` zZq=QGY*mtrLPBkqTrLperIx!Hi9x;UwiWf|?mbce%6&Vczh?M{4~65oS{^y^jabJm zM5Ojp7i-Q-y1ur%2LF~m^?j(}(D7%VQN=g!_opPi_|8$eUJdcF^rm6dP<(`s;+c)J zJW9Btq4O~VHSh*FI-O{kZLLpFCmq-#hqu#q+Wg8qjT%$hjUa{Zwm{aL!xTJso_ZeJ3OC`jJb}KJje2UUY8Pi zs@4?p`ZdS$B4=Byvu(MvUTwP)a=#^#{G+Ku;NMB6jY+=IwrOf*q*nDZ$ICqb*K*Yw zljr}%#kR&wDU!Y+&YzgngKM>em+RK?~$Nh@9+?Mu-)vk4_G2TR`siWh~KI?;# z*5A(CubqF~&;VOsU@WK*9a6ozBi_SUhY&XeOxQOCHw_mqElo2Hf_bKqp7%9omBKkS ztK3!_FA-9s-cBgTcWGiMskdE9+)0Qd{nOe zez&2auskQwAywNF2DrWph5M%qDiU`aL#W~YL9!=UePcmHD=rm0lyW7= zUoPl`EL~VI0?Q(_z+Z6bX-h4bMo6ssRpCdZRGqSL2r=3|UzknEI5lw5cv7w|T$J{z zS19rkbu`gvG4fg{uZ8nk7ha3yHD6v!;k7orHj&pTujQcT98p2NN1n7h7^7L5a--l6 zI;Ukpy>}TOU9MCXtXhgXlYDhT(LhqEZY>(`)2MLT7pM597w6Iu#e@7uq-N=Qt6dh4 zBB5&D;#iWY?pqul^t|m~cF51nT@4Kb3n~oJf98FiwNmw7vT{^KZ?u2&1@boPQEJu| zF74&gKrtmt#D2W?DVK)R5m`nqt>@BP!9G}bo`u9pL`nB%zV~oyV^U=N7bb|`-S;eF-~)}=0bb* z!DoFMJ)Y^+)uq7#&XS7KF~lbr%kJ;fr|A(DtXe;xhNZLh^Xnwfw#pVFv@UuSz-EzY5uy`sJCYdA9K`2kInPXZ@v|t`aH{W!ihu8TH2oc z{`nT$Zzom#^G&LHdSkPegzs(7x7hSmtD5(=`OK_lMxD>T+H6j3tL&`^7RUxib9Y-p zbxpH~w$O_KI3sMSHL=Zy_~B3QHR~(pN*ciduU;+2sp0o?77q5JpMP@UXGd{@3r;kt z-Ztl#!vq(UwRRJtUalR^51>9bw!qDC(+P4nz#@lfBt`Bb;c(pXh%otzwY^|759w9ZmYc)B(&sh4lnebqV}$DtM;sK590y#eW0ILKN7c< z?ZlH$FrqK!kwqvN5lBfVr&D^0BR$PACc`l%^T>d4x-o~2A0^K5d3M$5)E=H4*)@U8 zCU$MCol9Y`)|;q3CNt4_yW@g@Px*O%;SY?JTg+yy-A(#xiHrYz9bwoM* z6%rgwa zvCs}eI?2-(brAOSL4f#VA(vRRvSgu5#$aZq2^)-3EN12q>#7%eX#?g7(IVj_ z4A|^yF z%MEuNbVxO9_XB}v@Ye@|xm6qfrr^}N7oQgR`;3X1^!lYp!4bXw03jx=NYc0F>t?O2 zpXH10+yeD4kt&uIs4s!k+Nj?L<#$GXA~*8rqJP(o1Zx|^^@Bx}znb*vEl?)L>!%7R zKTXtsO;OHG)(`K3;Kyd^yXZ04+q3olxFDF}4gDyDnf!+SV-GGa)8hp|8@o*ZGiH$u z*sM>2sBYG8?TIp`N`Hw@vj17V8!j2G*Hyi9O}t#IUu}SV=b_%kgXFL$uHp$K=(w9W z>;=9?Ld5>QXdXXU%psNR%wVxElIb!;+|tM#5+yBwl-7T zCZHTRR_qXf_!IKQSl<7}1!5qT`EJGClhokLt}PL7;GwFD#sBg}Hs@WDZ)&Y!nYfV4 z3*QrsZ7@??H;Dt>QQonMUM;c4TkaRbMfBV6px6uju^ETN=@{9CLl8YQw+iuLD_GM? zF@rzd_!{v838Q&@RlKZ6drhs_1xJvk+!eR-(SH7$_yL98ut#DLm*+edC8YR^x1@=9 z*VQh|QbbFbd$*Qu888#HlQfY}_iKSt5)ZSdhcuKAS`LwB@^WrZ={w968`MWSAsjP zPnY_z3rSLU62w|0O9?2FlclBT;>TnO>1B_TrFAIQ4wLR6+&jai2}nM>JwobDDz$)A z$tX6)S~9;edVadp0zsCfOOsI4rArBo`gLzR`_iVpC=G0l@H?}lLCDMFEJ@!A0ZES3 z4`N)7bi5U`ZuwFi#FTugAI=xHyin?kL2yx%7D^{26021$k+S?SBc=i z(TCX9OgVu0lw$DEl}hc9Z7t0v_2=nZyHh&A=RQ-DX7e@k{cee$_uBj~CEVtA?a^MT znwO6KQYl|!OAbgo+oI!sKSSehr@%Unad@~+vZ$^T= zTaQ)NZJ=D90^93M-o$;DZINH~aNeDcbn(K0^HLCdvPYgro%5Q}fv^?nQct%1OWBN3 z{_>?9iqw1UmHUw(En}~Y6&0lI+b93PSB`o>&Ty^f*?Pa4@T11U>JP|oQs-iNXg|fu z4ySJXMNoyDjJcX!A-@gfy$X3B#ldMiA$yZHl?Zv^$%PY+ z^=u(HGa1cI^>~cqvxC zQTur-`MfOepmf3pXFvHVIS@1al_~Dnpyj<35gU~q4pka>(OXGH;nhd!7GKpJueC!q z>vZ@XH#D~{tCY)8jq<{Vx7!q7%yOD^{mZ1doufZ377A|ipLHo&d90w1(ieU1MmQYu zKlM>U$spztrt}IkAsBux4%mdL>rc@GBKn-S7hhaD(Wq;T+a0>bu3^C!=Ik&fgp_KV z!j#8)!M_K4WL46&hmp#!gc!76%*tnaVqmoiN)PRzRVfrms5UTG*`P;>#{bCJYm0^` z+8~n1`l(7UOwkNgc^{c;u~Ny6Q21K1cdXJDextm=_s*tol_Kmh7IIQK{!F>+JLP9W zlC)ncm9GS{oPG4Y5=T;4?f1x93bRxx!6ZeSTBQ^cQl{PdK`9eR8vDquBp}WWyD}QN z80=7H$oWy!us@&ecPRdH5w92Vy6@_9N(u2|AD>g~h_m2FrJYQ&+549;liBR-C1nzB zd+TLoxSp)hmR?mhw7?L0-A34Iw)D0VB2Ww4dt2#A!r6`6c))SYr%vfZ-eJS)Ft9TA zW*r)@uuphlU_aI=i6n{n-%(yA>sjF)r3*REKDncWVUQQ^D8orG>vdP@K{D7Yca?Bb z$d=zluu^vDuF{oMYd7vHp8EM`;;F6<#Ny9HU4Q}nkwA49z&BtUSPt?)78nBJK``(I zF5n3K`6n}$>MVeND&nZF5?lZ`!4t3(w1e#nAPB_7)A_noREoehumuLo!M{NscpYSc zF(3^L0kI$y_yZ4c7ag7kyTEF&1W?5K2OcCJECH**U!MlWFb*`z?8$wlohSZe=KRwb z-gtDg!1BHrht}n#5{ug{o1r6qK1OdHyV{lh>{3StC{YE7@pJXGgd zY?^`gB0BbR8yfDe(>3+evH!H8!7QLHZKqvqLnXOmj)@xlyXp*Qsud@{M^eLb$onCi z81bfKG0)?@=_{?*L{LLrq|TrV(;4#n>I_Njgf|`PdBsc(q5J<1zyqygAsuKm*6~yn zEhsGfD|CjvYY?RdIFsIBFQz z#GEH78*(04&n9t~rfTj|1D9>hT=qgf=5*Ns7qX3=@}+G<>YG@J@TACtFR+7YU=!dG z#qrdjXEWgv)zpQnGNDR(YQ|+fZ-d-CMC^Dr-IuoWE^1=SpJfeXXIE@#N9yNU-bBqa zQwez!I{+2-+NF-PtvAKoLE-%H0A7$YfRVYrfB>;g)I30nQxykQx2B%lJs(3aYG%F$ z@_M!y<`GR*-24j6E1Q_}&TAmw1O^;JjW*7w?AFwdx6=toELH@s^A8_cFywI7(GN4& zRK?Z)FrV1OoOhK2IUgKjQ{mFBshYba!KJi`3-6)aX}$yIICoC@(YBuT%`7O!ZD0?% zMZMQx6=-PLD_2in2R_)Sq<=#Qq8Abw4&S zkcQ((ch<`SVN>C(yOuxxJ5n8Q3M0O3gpvBW#y*>Y0{R-PdWo#5#ag%>tugd=r=ZFMsCj78I#Ap zK7RU?NwHc?7dlxCcy_cOABum2Ae)~*#g0U+ABgKkJKUXqE6hI!{S_c2;aUORfdz~J z6Tv*N2&iBK_zdg?mEb4vCvZ)sIwLTH!5|$>0dIk|;4^Rv{0trek6~2T16YRP9!y4M zJeUp&0RvmW9&i-=2yOsjIMuZV-9ao!15>~}uncSi<-iWEfHKmfgluEKoWQjECt)aVQ>~)17a%GwFf~U3=9U7!E8_fRImZ;2PeRJ z@GFo^~PRNe%O!TaE2Z~)lBHBb*)y+n1M zz#o{w0Pr%H0rJ6n;J@0aQ2K#8UWF1xJKoXN9YO$`8cW;b`ubNa?T6w(Y~y7XO9MM5 z^=Le3d-jV)H;ovdJ8jyeoRL#sYm64}^hu7VUGWN@8&3m`UW1~OqSK-?qO+rOqUT0q zbB5x*61NRY8B1SaXC~8jN@m!&u!&(=?6=9(%u2^n%EG76r1|CH72(IikB1lbE$VBC z@Q5%*1Vw~K42sBz$c&g4ksXl}kr%NtVpBv(L|Me%i1LVI5j7EY5x+->ku=gJ(h%tp z=^GgynHV`JG9xlGa$MxZ$Z3&rG{*W2WP#YEzBris`0FG<%r6%t7X0bGSLloM|3s&Nk|jwdGxX9%4mD^m1xnDY_Nn{ES5}5mZiwD(z4T1ZmF@{w76KktR`!$ zb&xg9nq$qg7Fyr2uC%VRmROHjtE|6Ubus=iK{4SmX))ts@?+kK*%nh4V~@F_9m=9s G{r>_Rn8Awx delta 7749 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 YNW0FNCrp41F;~q5ixhJ;=7$r10U`Y>S^xk5 delta 1354 zcmZuveN05b4t&%8snW?$x4LT%20l~5?C6b0WJbLA`B zh-LoH*-)rypjIUcs{_yfJ&>l?k^53)F&HDBlYJ2fb1v-QL~o?%_0g(2vKJ=kXdU?p za;f)SvI9Kd93UnZCM5HFB!xxwsYY@azLvHgBpeI3Xn}()zz&)%kZpza`jrq&h2})z zgbusSp9?-I3O$iDOBDJd>BFKh6iLmZ@O>oJio(rEst|=mIw}yu|1~~z^1qF*Ya%be zJ^E@Babq*THIbaSe|BYclFeAGc9L#*geEnU3fM!Nn@J74FWqe>3V@3=x|Jlu3{7h# z55h7nl7o>Rkb{+asWJ4WK!gjLCvKkt9ZIWEf| zA16bQ=);k-0rjVs`WXEDf{#{qkTk8l%#8qErr!`;F9$3Q(C!YhVXdTDEos6f>1GFc zIomw`ukLhy=K{W!sWU&~-!Dwi(re@vo?+1>NrQ1(KS?b3B2G*at+rTxba@t~=)8?% zG2=v`tmWz?K|~{B5U~gj5r$LvKtJ7_#b@FrR%P+&kV-qV_%g83SSzxpC99R+va8q)R-A!tC-#sH z7_%GxER-pC2+WBL;{eN9RNG<21gP$GKxhd0hj78jFj4Ib4FXhDFy>DplWaQ#+2}%% zF(0R6E?%3_5h?9*z;+3(o$x%!)-M_z!1toi1q}*$#XS!AV-UHA>JIaHxP_9#e8Z#t z@Lae(&i?GTSnP)dv)qW%zVlL;E{17>{dS5>X{^Y%-oL!&F_klnc^N)Bn@ywHQz95eCV4+IYGCFZFpaJpcdz diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll index 20eed6a2e5df51df23a84bd1f47224f018e3799f..7956447aec6e29bda0168d7c0e4af02ca416edee 100755 GIT binary patch delta 55169 zcmeEviGNMU8~4n)XT8~OBv(X)1R;nDqSg=<8Wki&?HUzo7ZpS;i3*}<6BiSzl@vuA zgwUgw3Zi0O9v zjnm3ZcZGZD|MV1AyV~BNZe`_PNIvz1l``;f;+g9h`;T&<89Hd$6VWW zglO_}Lu6f*8|Ws|x{?bw|DsQM7YU^bQ@wwbVBY(OLI_CC< zi;nD%T25{5*8eBo?6BM~EDdErZJqmL^`Vm2+@@&wB6|2F{|W0GG4Fw2#8PRTtzE5d z{^2}t@!`ltK6td?!H1F2+^!z+Ux0#ku^Xd>T7uHlcA!=UeVIGJ_aBj_+hXd@pkB7q zbs1&0QT0a9E4JhH+ES}ctlySyv~{fCicZc=sNad2HwPGXM=UAK4Niqzmi*h2uZ^uj>vuM?PyAy+m=VnrrT`Q zx?iKGY^B|6(;c~P@3>Ps#OB|_i;mB2+oLrSxsxMX2y}by`d(Wo?Q823)tNT6EsyG= z&3<8@LN{zhQ4Q#xTqU}NiTdQWi-C#exsm-)MLpY>?{;f)^%+-mpIXHKDaJotOVE#! zuQ3JPr#|44CtRZZo$J=WE-%(%zy;dRrVji_==jXG_T8>c{(Ag>wbAA;ySUOu-M?&Z z{U_T-4w_3R=H4CjLZFjv#Y1n>PjinB8>i8_xh+ShQOuqEF(vie+(PFyO1v1cbUPo& z#lQ05^??vW8dhM8sC#bV*p`$=+r;smsC#bs_gC*x5+3=rtk+}H$3rGMm( zn)SUxTjlzt1d22vw|nXjBJFBZm!_-#?NUkP(tuK1*3t-i*!J7fnzV*Z`g9|`XxsAX zAiB$@epW_+&eR3- zF=xENEaHp>Oa^BZFl%iSmp4#fY*k6b($JT-HOm{)fwuhRJ?K!IWyM;r8(VmWbQ7ez zTi3kE#cu^-CjMgkam5F8t?iAKZ_*6g$dwHRaf|KKmDNL@WJ7)N>Ey&(Byw>?aOC2! zzzDalzeFsGsSk%>y_BPplKgM~`>*qVN5b3RpS2INp|*=F*C&-ACt~r55*3*cK1VWq z@lh^$_RW9)g)iSGr{vsSsHlqC&gK86sbYXun-#fvB-V^o?EQ2~lf@h(Q#w z*!Bd6!% zaeK!RXR7X=XC%(rJb#Q(N81x~ZE-(7R6qRK$XRR~bab>T*b_ds<44=jL%F7cc0oY8w;*; zY^A3s(6zb4&eRa-g52q6y9l&T?%s3DDD9ow>q09N?Vg)*@eoDc+m{uA&dKe4^`bzB z<_7*MP}(@RNzqeEvvd7#u2EDa)@Zq(?Z~|Xwe@>OBFeVnw~K23VMZc8ck2CAK|MOy zNEO+nhZ$-^dm=1%!^7QF{a~Pxm7F`W)Qi%R+}V#lGpV~{4B^>a&!@voYM*E$H7@s~ zvU!v)%>DQIAWbic5($-Y?>K&HnN8fe&v6@Pzxbd8;Y_^HE zHf45KS)PkvW%2&h!mgU=B05+fCDIy{{>+jjnoJL_lxZmam35Tq0eYAHE7KJ|(Ql!V zi|DYGu2f z=)3d>-OZUc7wHn#*+LJ}|5c5~ zsPwEJ=>tPb%cgKki+tE=e_CBXU5l2Q=)dfMA01BHFn@nM@rx#vPkfGMvGBSyLMv!& zbfHVWzM?K2AkcIA!}|2RK>yVbH>BSPbhN=rG>!aN3(jv*qD5h8DAX;pm$K z%(W}+Pd{Lzy3zpJnJwu`2V%_b@YF%(+zlNzf{pD4&5rD|Zge62PWO77{vpsGb>$sS z(VLO9fk=I3>Fouyy; zfPSmGp1%tHW(PksBF4jPSv&?klkJM9@2jE6dh9mvJfn4GPu6lW&7#Td!ekmzul0uwz1=Z=N>}V=Y5?u3jOx>Cq{Zk*YjA|xY!X~Uh|NO{o zD`+p;Ucb45R@dkd{rQ*loSCNTr#90cDLtq!|CX*X(KUMWZPY3Yt{Hm7F6t@JGJU8{ zyNYy&zHToqQ0P5<{9)RTPo0fN=p{vNCN`7*t(E2rsc6HU^8K1+KDLdzNK$t5~P zUwNJmkuXIbU!o^ex>7%IlWLSM(U0AtX(sxQKIkre(~IWo>Hku1g?^)dD+!yZ7wx8y zqXNMXx+M>|!aEp)s|a!K|8_N=oQn~dSbV3kUlrkBdW&6Gg>UFdwn!7g8k)kP6z(?g z8ph{bxLbeUKfBlk7w!+|K|*vmw>XlOLfLhVBkv3pT`=5c;cb)YTqlJknP@A@^dDRV zFCjcBRDoL8esC=x7vXq#|}B4vE@?e+FdmdaSKFDx|Ah|0_J5Ii>}f`S_u)<^=ftA4etHcqAga;y*GtU0$r>hZzH@T&@kQFUXUp)*XkhDG=Ji5 zJiH~;TMz3X>=Wn!_Ohe!7d@#z?Ic{SPABO(#d+lC}EMn-Ro#!8=b_qjuAei8(GJ(LMQrxK4+{j7114gGETtJK%Y2Xc%u!i z&%RhM%%nS+_XZ&fJu!BJuwI5iw`^fJ=Je!jbYFI+YD0WB`m)!_|vuecejO0vUVg^Mt@mc4(b^Xgb50KzWhXp$1_fUB8;ckSn(5~BmJAz zdn(+dzv}Wc;iU}!Bfb=xdVL-xll$S1JG)uh@P8si-sP@QEaIimjP}zLUkahs>00)k zhv_XWi02-rU%luocBzS}HQmSDgG^TXHES4T^2Hw1HOSP#y@x+<%D@X4zM%*Xt?l(? zL8iJMG*jQz$y6xQGx~yVrY=-4$?U}2rctz>-Xg-ZpVBdUX?Ifxfd=YLBTb_P+FD=P z%Y*5NQU z>UGbXhVZR1>4IsCGw+O>rkxtS!fxI-y+=Ltw;q^!OSGB3?2!q(dWwGbFViNKUfK1L zI8MM!zaxm~!WOKaB(CFLQxcnSS1*anWqL|abrvUybf{it5jVL}KbGYucEQeY+fOW! z__H(+vzlRA#J?-1(2M%hcg1Zcnxk(RBu-K2PTl)`@w$Q?uxyOzVxqm7JV9(vJ=j|l z#QwA)TRlOXNzbtA6UDWd=if~fH&8lK9}+Kyn#>0-iS}-Vqn#p7Q0YG1eU^AgrN8Uv zQ^Xh-^iQK@;#vj4!?9K3QtbNuSBq=tcKyj}bUOV*fA+bUsUpN+>&2yBG*}PVC*Gl2 zt2CI!DXp{a`GXitsYUO4K06G8?@O3KmNVi@ z?2N93Vm%jHkF~fa{($FubWM~feaQa0E)J%B^uE7}9+b|}KPVD=34Ed56gyCQS$}*> zTtsOUn|)i1sH2SLO>pm7dOzy1Tl690qQSl2)yHnbO5OUmx~CW477G=(C9xtV*HXjd zTcl@b&Gfw5(kQCF(UdpKv0v+N`AH`zeVe(~mFmC? zyir$rff%4kJqa;D6a9?(yIGr5gQ?DIKJL`9unp3h6WUOQ=-pD!GY#M>)n8m*Y_=vcA5~w zOiq&$M82e#OE-B~eQcF<7jt;~YUxW#AF-%4QW|gj8YxJm2XxmgsjDa;-tMYkE%nLHwsxN@`0ZVTSyUKKILPx`z<=@ZOj-SsEQTc8{C!1EG3`y9RZ&**tm zr)j2@$HZ(}Ll39MrM#J%cYQWKRX=mYct~nk;Uz*VO5j+i%6$-eQccd_ zGeMJ^@lZ^Y$BI~Ztz2as30Jco7P+R=^w9DSn9SlWa!VmJl%=7c*bf%DK>#swe?*iq z4=!T6Q+!5hnCRCHLzc$b}ddS;NG=UBDlDE@Cx>F6g zp+xKIEq&!3#v<{TC5et<4;skrX>%6XP_7HN)T5!?PlRXBY9g0uyy>mv0X$r9C5Oq> zUk~XZ@3heI>|Ufi4!3P4y?;-6Clxe7cj+y6#69Q>y=S!SCeRXnXkYmonfBDxLGmgS zJla=7<(mp^qt72H{~>BcH~H9ET*BGxvGP2c$LKh@x(Bt7Xuoyn)UJG7rTcp0aq@A> z=i_*J8-;(`Fj2lkJL^j($!}2j&AsvRKQjH2>C@#Z${fPaV zD%V8Z`gbY@!)ePQf!sHyB}{!HXNglGU_-NL4cTxMSM?c-Wdy-}^gou!ZWhEN|E-bl z%81AAu9IV_fP+K#4f0~aJz|sbSbyQMe#B$pp5yjL`4HmuO`GI->Z7j(zHYu@EjQy? zm+EPouk3Nf>4gR>h}N(XgeY&CXn?+{ zwSrq6IL%OHib|X7Yq~0XM9lL3?CK2mxh?yfykiLQ=tFg{(0rh?O6c6gccCLh*iN_)x4={riM@%yGwD+|j| zim~?utWuH`c+U-)%4QxK_5Mmp;-}>^>lIaUf4YtucUXSi7oSA2RulA+&5A{#=h^cd zWePv5Z&%vWY_@s364mkO=M){{9*xt+fU~@3E<@lJc&H@2186ge=X3WrKspG{aDO>q z>@SzLverA4UQw-$A_pTDUy4}N*)z3stq&96{wTK=j;xaH?YYSkw`NV7BWYM`+5{Z8 z*0d%V!&=iC;CPubc43F|L5oeRd2&c+vBc8eXo?L2!+AA{wH)`=-YIz>aI-t5N)$AWnuf2 zk#suydY{sQFTQ=sntJ`ypxG#-zFYqj(D_hAyyO;h2xWI5W7;h2CIKvCztV~aO#79I zGOfWv^Oa%n(`)jTJUUt*dPJE=X|aCeM`f@Hhpf=!%CFRuq@d<-x9H>C6CDIM&?Q|D zKB+8*i~gAHKZORIWu>Q-b>fjZl(02tlp1W?X{C>FU=Dk9N(t1PoKb#oqW#%#mz4!L zwNJjHbflYE&K0FLB7rkkl>P$!T5ot=Q3b57;36f8y04kYpEmjhT+mT;P-w88enZ(J z;@Yg!9i^uUNA{)nlx>0-$2$I|tfTL-o4+Yb_{s9Va#0jqhU(o*m1Y7hU^D(yS|S?T z@~2YY{Kp{UK`hok^(%iWK>|I()Tc@bjbo3VD!+P04zxe#Pn90`nqK^jcgavTx=dLT z6xk0QykLtx!s$9G+^t#BbQipL$&2A@yIIi`5f?#4yoUm^nBQ|{rQrH5+x=V#Za4rM zZVS>;5_;Epp;l-68ilSKg|72LzB_cczm>-VJ+7NxDl0U%O>bk|`k@~X(pmzxvW>DD zg|?K*>Nxi?-FOcT#G5Ji3B|a28psk9wKer;*@_x~;O{560uz18E}GT8w2>abD||)h9GiaqK><&uOTh zGx3||Ch7_9vxC$_3XghQs_)6TeC*s#HBp+VcWtlMru<&IgF2V~!tQiX{lhg8E};Gn zLL&YwP|(Qz5edW5Qt z%_l7O@eBBYC55UZX%M>|s@A1bnc7jE?DLqS?2a76tdC(f*;Dw-0!)g{mL0Igiw4>fKRBvWU%oq}F2>AE_@eR2TkKpGa^! z$ICDe7_0qUP32+Sb9I*q<304Hx~^JMnV*?=6{+TLr|;s+1pX7|pp6~W)j|Ju=oLBW zNeA8SpsO8pj)RVM(03iQmxFe+Q+B}FtR2h;z`79Q0EM&3DjDhoO}Y`YCJfu6ZRTIFjQX zG|oW>IcOgT4Rg?72d(d*#6hn*o;S}yzjV-L4jSla#vI3BEVomUtf^~G_qVcAcP+$< zFHP{;h!B33m=5?2)rgknzb3lJTGt=q6AU)aTA zZm$}NU}w3JP5&d#v5PPMkGRM#RxPg@L1*@Wkq%YF3K|$=7iU%x%MkBot9tOx^nBGw zCenV$=0D}V!Rg8W(Zd_0NUe^$bM?yNywhs2=8>9L+k`4&-bu5N4hH2vRjsc7AH8fg z7scI+tB6GudBVjWR&FWKtFl7=7-8%zN>n4>m13@@db7VHwZ^nM3+Sm$QSvbJGlpvE z{@yGvS`%F^B3=Gd!jARS8sePzsHav(X){bCmSKiwa4$`Ck3zcqXEgXy);wDCU;}z- zHSi*IaxWCw4RKnPB863olvEbE$cyY|k9d(b@1d6W?6vgPMDHl1%YPEVZ;C}47{G={ zXux~6#AxsZI9CGNM*<=tZ0$ZJ(l5>_7cGd+Pi0slUZec(V7KUv*<4$m(f-9 z0@=W57%gLSqhYiFIhkC^cuZq-imE7=va?*dfIWw@6864EYP?@LIs=d{|A_~W{))lq z#P-4Cgt13`G#{s&x6C9DS@1EH!(XKshQE(}`1|mI8G(TrQCUG0(&ay+!56WveYGZh zTlz8g2pEgDCUH`%B zI%z?aoB-DA>HRfaq7<;Kftp8X!5FBb5mAUErvZLrc^b|$(fmI$EW=c^x2l{y8i?(} zkGTxe2Ga~SW)M;tZ2ll^iq&r%>=;iVkkUSsG)I^V#lc!Vybo+XSn~iu%7?`c)>La+ zxppEYg###YM+~0EW)IfRSQExq8j2lGNji|gh7Zx!`Hr5z+X^4B8|~VJ<)7h1ipEp2 zbrL0`ng39&C5}Y>hHCBcW-NWE<`uBdC_qSZ0wrswqDy&N6NpykR4HSphHBezR+v3Z z^8#5hOq*m4ngm5vAZlC<61IerGeD5;{hl_0wt5iHTS3SjoVPR9Q*zOf=6oiQ&(pkY zBBo%ujBSsFJ2O;yC#9++55>8O2kcTTCPE;i!_i5B%sN~Mtxj+opv53S{+=!_8ft7OB zd4x6-7X$l8;K@tag%R3fbinX9^dtHqPW!BSpnmuM8#t=V|Qkky=e_ zhbnn_kY4~gutuY_-qy_WtQeWZ|3gU@kja*g(wYUR(_s;h`@;*Y8{kQ~D%xBjV>d>j zk?M3dtUznXd_TaBmFy@t>|dPkf$==;c-Gfs2?_~=4womh2_I-aZC^NwhyF*&TR@2; z&H3)Yd7d_l#C+a~Ncm<%$B?swA7CmN>PBrU@q;=y^Mc*umZLCn_yc`O1(FMN;Auz0 zUX!6=$ABpyW4Fg?)8V3qj>i&%ry8$yv6jwqJVHWUlGaQhM}bmy zZM@c`(SX_Zr(K2X!5@Kep5|j1<3^+qS5hHo%_nFchB}o6BJU954WHTq7q38$!wlz- z0vT+=1kKAkqukmP84JpVM3t+vFD7VxaVzr(Mui%4*w!?yHuL*N6Sep@0~;zBIYGvv%=8jd{!Skq~mmpA~W zSbRUt%7!B8yPg|qE|cbQCZZ(}%q9%SVu2h7*0c0!+6Zf{g^qfVWl2NbKrN5SLC*8ht{Iw-dkQa(Mwqe$q5$!%?F>9*JQwrX7~@Jx zGX*0%g-u2Y96FcI!1^y_`+510PweIAZ4}5tz>nSI<^8yrmydHJ@euRu5a$08%EzCOfq)}WG?_OHWP=di;l|Ob_(PMfPtTBA9&>Ajnt8u zSZ=l0?=S_T&C=?4$1H(GcagXwbd=y;s~wOY730{!;tH zHa{$oS-=Zkn71@UC6zMv<#w&M=7)SPtIynLYfY?iyr{7bWfFBvAR~b|7Cl>QXw59o zg8vCVjw}G1^K7l2`wK^f!|+;b3Q)oh&gQ#cc?ry_Fo=0JcE8!0PiV}i_9h0MgwqE) z@HDR%J@$&sh-h4mUi5<=&%-m%(V7J8c9__CMj)qv_2niouPbCMeU80n=V;rkvp%bQ zxYA3g>@xa?jY+}|Sje+5Kes}37}!@u^)(rHXVBqtOwS~(z11%b#rcLAV8OPPPiexL+viihAa#RypYBurtouc&*D05g3v_J$Dm0Qop@wZ&Y zPNiUnM#PbVy(Eme&&T4!qMxrd3|+s%-nyC9Oyn~ljiSLTOFg2c<6CC zHV1C$BHJ<_=HUV6Yjr|HR@x1o@Gy~I0Y9FuV(=BY!(bltxExM!0SxA_&;>Xll-S0z?mC%wG2?FB7>31hE@1giHD73lO#i@mya1yhL)4 z%eBx83pJmBGDpD?-X`(^P;8_zZHuExrEJ1NSjpsB{8Y2MuAzFXripJl3o&0Y=NDq# zqMsLPeFs`r*&8FaG?6+$S%y8my^V=H15$X}QzV}Gp;$Q`$t!X!HeRL;aMe+c^PPbL zmY#}Dq=1WgJ2Jd0RoSsrOc|plZuv=jq84``IW0_tx5C0+rfTc(S#8!L^hyZ(ei1r5 zgk4w!{~f}fF2dx%2z;V-!i$IDpJ*NdsjKZx&Wu2{KsHbF9>k|@6*9K!6U4D8%zZJx zkNn(TBCM~8JO@%(cZl&Z;^f7+V8umF01-hNHlVyJS%jI0H-KO_P3yv=>NFlzXR%(NYd&H4g35KZi6ms9 z8jL^Z>jAMm%{v{oUz5RKl$Muc!_zU93mnBCqNwsY=4O@R6*88Uj!rD#B|JMr=^;?- zD|@}1H?1)dm8Tu`z9vJxg%#!44@p{GmNP;VS@1GV#b+~ZmtjhcX9JgM^;$h~*!=Pnj<$Gyd8-!<_&=n@a&0t4ZruD#(HML;| zW8^@5m#>2zPnmcoxDq!UVWbde5}R!zvjL1CRIH_3#W=VjPS}dB21?m>8-^c2sto~S z*m|ykmvP%MD}f-M=A8il|C$V)K%m3r@FUByyXQEHC+skhk-6}u6~$dhg^bN!j^2UG zT8`sO+y+!&xJM0@sHAisX2E{Ly{~}Q8a8X(jnh6+k z!>VhQB4-)8IDJJOt=4aD#?KW7$eL_BXcbPPG`oWaj@Jlk0& z&NyMHI8lh0ft^r{n}a8SN@xaDs2jdi`bmv4ntmD^0Z`(oiSxD3n82+?M5}NGc#$n#h1)r7#;b6j5d2NKqDlyUZL|r1xMDRn ziDaH-oSV_u5G(oTChl-=aXZn-#`XJz)d&#_%MHN!q#<`Rfann#)ycOG+sKM4#(W3J zx9s6++?EAb$t#6iyKU49FK3M{&%!*)g!nQL%X)r}fP5d%;-2rWi8KT@vALgPl5Q$5 z!JXY5EDvPuW1C?tphmX6M9mTt=?%Q#s>bQYom9x!J*fI}Rf@RDd`K=7YvZ~$HJFn#(r6S)PX@iZS5Jnm~Uv@8cYT#le<4O&LF*jtuy z-$bqe#pT8GWl|wyXQ3*PE$rbMt!DQ`N0EmQuttA}3vi@4UmJ+xY2E_#+-ov~YZc{K z^cR?s+#GchiBpJ1oLVS(yk|>chCbv0%iJ$8^%1vxp*8HjWUIZc-JY7rR{-H@<07p# zc|`^bgC3j6Fd)$=g9s_ZTq$GjU&2`>vPNHG1|_ntUm{*I6#0-O)FHtr$PFxEGrrVX zwyM9)Ua$Le6R8UnbDKsNyOLLAsQ1n@eCY~U*bNxBT6j^en&3tXA?DdQ46em(8-o5! zER{AaAXBU3n~(!VcbT|n2*fEB9gEi#!Eg}60UT^I5uAjvMVSang0|aRvmOW5X4ORU zgqJiLUV~J~*o{oBwbfANSs^t^8q_lZ>;PH(5Y4S{w-)D&fx_F%>LM8cpsp;fUeg?& z&3Ckf`Uv_0$ot+R`4lkZyvvL0kti?GkmLN2gtG)HC?~k=;iQZm<9Ud8*J(-Bm+XW!$Mm$kDUx@9 zB|F)tqcks;zFu3`HhCAXju7vDBAEh==V@L5Z;f7)VTUWkr52aN6>Y#|-N%-0Ky6t0 zoRqO6T!^|iXt!|PbL?vbRJgX;h&#v>-CohW+4uqhi05fX%U+XV=|(|^%i*Ut;<1X@ zij8ogxIo;9*&K#1O;J%8lQtQ1rRgTT6U*KWi^d8GAo0sZvKzp@xk;Pk9r8W0j0u1N z3c-u3)gsvlAhO=94R$Z#+31AkUy5WTkk6KGMx7XIPT=1+<4~8l$FB5X6Lzu9B8lT^ z{s8bj6*3n64MrL#^l!9D__eVM-{6`POW<3rOZ8cMEA`WMiDUyXYcGEZQd^7v4ru^^ z^tbSNyZ73CUf#EO^;KG44jv|6haP~y=v&+X<3(9EsvE$%W@~k-G4NEqxWb;hEI&i&3kja%0@n&OpLi@I8@8Lpx@)kTTUgUtZ6@RcByLCb&FM!hh zj@F|UugSQlg$|dCZ2MNNH4C1NxHD+0ruxQlL%uTcO{k5p)myj5%1 zHov^CGpGy5;c3Uzxb?p>UKeCsj`7*54GtJ^z~0n(=P-a;e4D+ew&6|W zd6u;e)8{-pxJ~;OLEnTN9261zfwZl0&~EM412hH@dAhtY|10B-fsE_cVA6JNO29=& z@y;$1NdN9k!d{d|yE2X~$%LO@>~mEXQA%)og0)Yc(X|;w_Q&4qKeB2BbOCoc9K1 zvFKdQtI@3TDiCA7qH0u&AiUfhOV7pN1+ncY1#%22_aHb@UZf*gQ+s|{c+;(cMeT`fm zPA0U(R1U#ZZdJux^IaOV*80&=Q>q7vY9^7gVDu%swhM_`yv9ci~V$^D5VcfnAJTmw$BRl8y1B>R51{qk-%ZfHY}!34jpC~zk6!zFTkgv34K z_ga^x1w0G=`D~m-+K$H<@^mek)H**)vEJMy@_g>!E_=`;I0o&37u(I2?!ic6|Js8$=^ai$pAY$Hyai0b%$qOqFnTZE zwr%2BXw>Rdi40tXiNw>+B5~d=6M2(TDQC0y!r&%-#a^_FKlDEBTtMk5UK*1i-=7ra zO5}~57+*t13_cC%i){OT&BOa57b6yNg{m0mSEJqcps|1&XB>7n z`H&%3CGtLCm*H-jC_?YS{S3xZY1(}oq*=*!NT*6@UJP5~#7x4$R-rnIqLCFcB zYby4?MD(4TOzK$h8izGMggcs9Je$AUOhp%a$fPZR8}UP!XZy;t5bhOv%H%14h~W^P zAo85O<`N&7yovc0$X*`8(Bb(G<3TV%K>Qy#l)BcDiOEkUCmm_dHwSXq?8BJcaNvj0 zHyCc7dZ1rAto5dik}ue6nE94WwgYiIt>VZyS|wt>7pai5=p!hdtWP+CXSU}53^hJ9 ziTEyJm`s)c*l&K+?xKQKN9}9rsMgGCxd=svXAGVqldb?lxue+E;&~Rj^^F9XbODA` zQGHDYCu)HXmm{t#&_=jBU$PhfXFBdr@Ztz}?FE`g^|Ep+D3OUQp6z@||GPlLFGHrW zw#P9;5qKWgIt2{4!Y%MvvKsa$v<(O`WW?atB8}MnIG!6#K8_y8@;?EBh~k9SEOcji z`M3t;w{Owe*?1qqWybYS16=!%D&b%kIdDhgcj+VsrBz5y; zG9E}}Lr>yH*`5X0{hf>$2$PSia<5cYt5U{Roz%`*mtR8`?`s^aa(>1{01*A2!e}7I zK85Nq=TG4*UhBF;aWw9_FX5p9_}|l*r!nPO=;*Y|GT8&*HPC6;zy<4R*hpnlk@C&w zikRbWh=k3En)v(tu8H+xPbKUattQdUMV^PRd0Ns8Ty?L@~7XGfoRNy4f@?wQ& zwHj?hZrUI7%W4YA1^js0v07e}VL~TEhs!Y!f5Hk{?kN6)heFN)$yJKKCgbY?I$Vy{ z{iKZxXmiV6JE@IVu0VaB=C$KNDrC(49PX&NYL#Fpo{vp%9fhm`mN?9Ceix9)qEW<} zSZ*y&t5jt(&S`bJJL5izTlmgTA=iN?4hx+B3n(ft8e`9YMHZ)4mSZ>0AvEIVjPFKT z*uNVo@>hshS0O_j)o|Vyz{YeQ4U6DnW0|0)a#hy-JY0G{n{6lC&uh(UI^TvOPSw@& ztK)s6Cn;^Jkd$T$alXy|KCkU*`h@3m|B;GMO*<=OAz;Y(yoLH!fhp2D9fE zFy(Nc^)pr!-kjUX(4XNdciwTdD+E3}61S58ZaaR~erdGxuA?TbO22Um=>!-u<1wnp zSk^D_>bRu;gTK>sydNm!*^X%%{2~5k3V<6nWQ9iFtE|K_MIo&LyNnq8SfoR^4!3?DTP3V>9YfCi zFQJdHtzFW5dKMX$2;S17`M}9?%eX~&Rfgsxe+;m*+;TB2CzvbcEDM&ch6!VW<7yG= z+zgJ)sLg8mt@4p9Qx(zz$q$EJkk?YZvk1Pm z#pn~vt@vDp%mnOmV(?dXDdarYVy;5QqVH%&aW3(`i^kx1au==G#3tO;8v1Vf3wAtl z(@4>iMfSr!S&2Vl2Ow0tt9cB($W^LT6@{$#0I{sHbu*#24G6BHTOnhqd@hmRkBKvrL9uQ)iV2qSt)pFwTh_r5sYF# zknSPu&0~}AA@zSF*1YGAr$E1#ey);+Yw$@m+j0*Jz>i(4B;J01L(v!%{;`EKDT1Q> z1wOiEF27+0NAYZjTk~71l0g7Mu-|Zdkj=BOf+l9FWFdeF@EZd5jPeq2j!_Wv?Cigd zdlDC}o|dJOBLKoQs0$^3+b{p%B-OUz|JY4#{R(RZ2_}VtNDlbwVJrY*B)Sc;VSnCjcL%hSe$aQPr8^tG$@rgW^)A<&} z@48B!|B6Pi8xOVqcs1Pi4}>XrL;Q#4W5V?s+xUmplQa5Xe`p7(cQG~FCs_gFO47J<`@_Jf1;0Z5d0JM;#Up+v_HY0xaRgWp{QdDl)B+?VbS=$^H}TRHNHFx z8+nntMy%B|0vY>klor5}ALAN0r$PgpjE6?105~N))&^VsM2B63VDUF__X1$fKhee^ znwtUQz5&H07^?x>$?9$o{=%VJ=wsIvD8 zFfV_fgX_zsPxCDo>aHl&gx0{^v988-8o_MHz+ww+Ur9NtgalQJyaUDKCnc z_c=OiH(T@^1#zAA950ICpq^`2=nHo0ZwS$55Z@3d^bzj2Mkk2my%4yW7(}4>b@ARn zi0=Ont_iJa&W_H83o0B+h|@553O4(N*1G!u7kd|f)tiuSfN&#?Nwmq!NFRnQ(4oWm z#3)=K@wD-URCQ7zV=rIeQ)5Hb8y~BtdJ{`xP2-!73e&9lOL)`)de@iO#Ay_p{4e&D zM7EieOm^d6JYoTB`X8=Lmaw7!;Suo~k&{xkf(vV~Bbr~hF&jb72nJc6dJSZba} z6PPpzo6wI}) zAv~YQKBHk_DIp&LIH#J-tZ_pvkUo?AIhj10Xq;bU%KM=n#3g818(cGREviYL9B`ss$i{{?mA=X!$nAeGr z5n)C%B=b2O?Vn2KaTulHvbh;XDP1;q!YG}U&0WwMt77hNU5;OlG^)ZX&smQM3BYMt zF?(6_%Cm4LD#{|{Ie;BfL191kNWmDu9jF+cQxBY|MFjbzuL(D z%ij1y6NIA#1D+FLCW-omYUUF{(r?Ir0#s{kCiQ`KKqN36NB|ZA3|I$j2Mz!yfg<2B z;MBxSe1Q<4J1`I!1xyE)0^a}!fE&Owz&Qx@0PTUuAafEK3T84e2Urel1ik}~1HSlJL)TU>@)(kO8a*wgG#A)4(<0H=qm0Fcfu35@}c$pJpME4BEOrHf2;C2AT_`fX@T18AwPZ_+Zff zz!DgZ1x*98QCSY?eIOa~yf?6=0tw(P%?OzUjOL91{R0>RJ~)_=F+h}xWf#?gkW;M? zQDgfo1PyA7jUIe3=(|7!FVK#ValkB8mf0Rd&;bq%jSdSXWN1eu(YPYe?-3|mg}fqQ zfP6$4Au~Fohrn|>2XF>Y-a@pLgnv$=KoRKrE`;Rsp6E(Qy>19I!3Tq$1D1df>rTke zKo}Yn{|>sg2i}=Mo(VcX65R`? zcofQm-HK=wJP&xw1Kf&07IGfs){Vxoz!^>iv>WgMJg2*Wi{LrE2NZzkRP2La4F_)l zP2fCl9H?_&jK3ec+R~ShUO*~uDQMZd7!L5HKOuvG2Q|>8pfLkb0r*(Zqd*9Hq7d{M z5Q(Sp8%RheAPDj>(D#8@1Sb{29&GZU?`I$a{LcfE2)T?z2ojt=1nPq?1yu)O%)keO z4hQPPXfo*3!Ke&Ib3iWwwIDAA{T!!<^?1sPQv`T^nwSX80?+AuU_AIVP#&a)C!y7q z(W=>qT4%n872#nfoSp`Xz!!mD9)So66&HbiHwwLk2rUnE{%GicPXpD);8GR(7SN-B z%6kYjZY<^m{t8Px=+EOZ1kf)6P3k-WYaLz6Y0+dXIq;me{SadXJ`D8g6m&gWT@eDJ zFb@U4oQfd>&*^R;5qv3V{513s^f_G#6re$zt_F63=QMdbCbb8)UC!JF#v_B%XTT8f znTc>Xzyq{84|LfKyeUF&WrCgpiXpdrL`WAP9F;|Z&ICGuPX=89T!l_1=q}(qw*&eY za1uP3Nyx`DlQ8!D$z~C9Y&NyPH)62jUFFegs$4LxE`H8Iar_cw_X*a+EJ_>Xn@BsRy zpz}Y&1cW>dbR%#cbrpg3N(T>}oOG=JXGokx23ZC}KtA}&Bca(i8a{@+q#EKp&@$j6 z_%ItG?#l@_FFXL~9bg~IhOL0x0Cs|p1)UCLgHK+G`2?&7p9gv$SmOl$UkaurgF8h= z8t8Li33!r$u?JGXSDa)~89&kTlWq$5swZB4@;&qg<^<%N-dl?e27D=KMiwSG>|}yo z1@bWgjk8}868u2O4}pumLQBDO`V?3Lo~$FJ#d^4B6b=L34CJ72#hDU1{A}s80ZR%z zr|v*J_+(JeuQ6fK2tUwG8?l1G=YWQ9!ej)W6b0tuW{e+iDd_QUFhTg!e~ZZlY(iyG zppCP!@$jAioepF|UU8QKIlohh&B32~;yR!+0Tp~Q=wl!a`eZx)?d?{$19T@)? zW;16<@GdwY@SOGl3c*K#9tILopb&KMcX%>hSjPn1jS9gReUDka2VD(5Z9hCFAmU*P zL9YWZ+@S+n`v-Vc@P43g043nb0jy)=G6M-tCjckGbNUsq8$74|@-T70b9x_01J9}3 zLHKs?6_+UB`DMydAQn8Qhk!`%oK8JNNE`5+IvvIkBq70>W`G5J6zIr&JOTK4P~j-t zCwjsUv{eCSzXkIE^rz#PH;|WtDksoTt^?W{$N`^xk`M-zLO&B!2TH)lpTdv=Mfgix zQKzx~Q}ItM|Fd0%gxG)x@R^_t{`#u$gd)A0O!GTdI!h{&uQz6SkvIs zKo0<$IDd(dmp~?-m|VsR0G5DH1J!{P@P(kES0Lv-2f7dN#OlZc4ZWI#mhz0Nn6W^a z8(Iyz7bpOq2l@|?0Y2;+0@3SeP<8YcXkTDC_*hVWkz0(vURH6Ti>K!oyLSL*@SHvX zUf^E1;+_R=*eU6z;|d3fs&_X0j)oMY0HPyKKLSd?bLxK^s{=fzrvZNN!|5+THuxgY zT6gfoXf&rg@1lX=IsE~M1z!YOat}iijwdJu^Zk7kLPj3wC%o0lT5YX(pj$4R}uX0!w&V&>)JxF9iKe6aLx_5Dq)RA|=a!0xk#rPNJj@fw0cY^U%>U9Z{(=vJ$#^G9E&-W%C!Xv~$s+tktZeZ3Kk-Nw zuoHa6U&;c{|6;v!kQ&~xb{~ayiz?@obNx5U#Cu|bF6VpYnjGDOSW)WqtQ5{DbsSZSO~|~ z2;pYULUW%8v2YMVbfXC2m=F$b6T+bzNvv_VH4eg!& zuV446PVYC~^*vh}i~3e2%Qm+wc?lo?epOQ9MO?Cl>OkSx+Rug_;~QY%2R1YuJGN9U zi5Ky(t<-7ajSW@FM>0BRkZkSUZsQ&BGLGBU2JtMO-QOhAFupEVk#7(2%{)+# z-{0La;|IG`$-$Fumd z6nPo@W>oNsH8@FD@wS7ilILYLFXH>MCe~x6{M!k26%Ibw9_VEdvO?BbRKl$fsY=F} z5B48p5Aa}z4CZNNI35+^zI(Wh=D|l~0x#g;vEDsz!qa6TZ^5Ufz>8z6a!K70Uf7@> z_mibOgBM7Nx8l>XI-Y=U9OXX2OE}K7R2QQbtJUH}^RY@&x z!ZRn@E4*uwyC`#;7I#1y8f&Jwx63eIz&B+iuRpsg*;PjKW}JSG6QAdrrh2lBH)z9WB+E1BRwaLt zNxTK0lzBY(iL~-y-FYroUXR_+w->`vE2?A>&*HtZls8=9M3)sjk9{w6{8z=I3oVk> zyo`PR>Wt+XoFeOZTJG=Ux=Puj=gm0hI_F2$p2NjAIvII# zljACrcv?)5ziDoLUBa~fV9o2vYjeKHpYpoAYqiCwx){|MhyJndo2rbCf0Sj6@{Cm- zPADGSR%&_iIp0hs@`mTDlKo{e&)}IdmABySGM%^KKV$}PTH=S4qK%04So?yFpJkBn;{CM!%4{Q(*J|g3I0dM=zZs%>dOqTKz zUh7gZTz6SgA+JgPX2(N%3GmS;#jOru5Cs z-7Ra)2iv8Srv)lcvwO`Q_+J~%gJY#HZ^kQR#A)vTdGeWLJdk{Dx62sbij7}*2RwL^ zWO)`VWiEMei*=4Y4~~_|ycw^T1w4;Y?=tFPrgbmPM@`HFa$mYU3=Y82Qsluiq`+J7 z5$Wy81^h}Hc=DCqD1&%f0F!qukQv1@KaerzgKK024}Sk^hn45j8kYuLOJ;U0ml@?T z3yBh2)n)HdY7<75^Oo+{IMTKIBO-Ty0=8AUU9>eek8Xg)Z&Ten=&*MKC4Nt9+y zOJnjVk-2Gi^#*zHFc}+b@ZU0nr-dEvX7O@$?whihr`P?z zvYAm*vqSot5B^7Lc`)yHH`jV{2`}u~Eg54zt(D26hNk{s$s8Wc%M2cTqpn*ri3g9} zNTIfzL0U%hjJirW&3imBf(Pq+*$5tdb>nWy8ta1}N{I(QmLgC5de%4qonBGJ9}e1IB93c zzr&!#Ac}}aq0zLMD34;Jd+wr$9S`m+{djPs^x|o`P@Yy6bmT5frpiJTgJY^4ilo#=5 zDwQthY1LA9+qAeTSL8I84Byi`FbEzkYdj(NcUcuLgkQ@TYm&WuQyIsbnp|4@*yxxa z)-AbI=JT`$Dvw&I%VaSRmSrIiem&ftXfX&mex&QWrLMwj_*(OM{FjV3-=Gfa6v^_m zE-LR@H#KUiUOvi=#e8t4Oy|MJWil_~0S7p&JcBRid|6?TR!f~y_y4M?My1u{KpSU1 z*hAJ@9~>^7@j|#nnye|}*OK9x-+1mJ?wIjhJW;YS|65JLq=o15+x5zkZ17i!=0$SacsBb=_9?FSdTSF+0dBBj-zeJ+2*n4cS?8i zdd&UK@$YBQV(^~y=ViQQT(_i=x8buAMPq~8{6Q^A9^65O@w9j>k0P=+%2Xb_LniVz ztUtyrnFo)R1w4C9u3NJGvEG3}22YbBZ^36}IWOY2naq;1M#1H{)Zn zj+gPbr}}17Z8WZ&=my2hxc#48HM|k~pXRCw$Kzxo&tmRYUs?=`e|ATj7xCLu zjd@y^_&h(v=1i_HW*j_8)>;%iLpphIvuRF7-i)V9lravjkR~2H_(E3=Z^lccnHTVV znGpWhZ|WNJsBgUWbjN?1LC7I8k>~MAna>+8GA|2wGhQ$4ybY@^cD#8#UMS0X8NYvt zjpj}Gw3K5_t2Zf8NjZ3y)N=Ii{|0O0$(MS^mpM~C(1N`#cf5H$ULu2eD^8i=3JJ$Y zCBqB&n=2f9-i-g12|T&dPdhS^XYfp!%=0+pYWIor-RGOgp|a2e!B1rl4^~~{D&Y0F zqb%ZSX=ffKo?nm^yh7f=(;Cmy>MFIK@4wa?;=yfZxpd=o@c5iBV?1y?ekj9u@Sy9w zW1g0F=3PraM_K4iu6Mbb5B8SHJh;0|;AuhU3%tI$^@X2acr#usS?hxb+~GXn!LicBoAI7IUEaKaGw*UJc^*HJHlDP( z{wr0W|LSiq+cp%M?pkCz3QO;Iw+-@y;8^M8!5#nR%;&-3vX}=im4!SlBklF0ob=p# zJf8=X1@>ajAS4P)N0I5Yz%-9S({t_>9^C1E?|=tSm8rZ1Z0ANI|7aOR_SH4ok(&Af;g793aJif_q0 zUdA6hZuj!EQuRgtddA%P%GSYOJY_FVcmEIh^J4Gb6M|RDVqU^;J#B+LxgJN!V4l{v z=27cj*w6Xq))&VP{`fhc{pN!gN?#t_WQjW4PMj7lVlbx2s=20s9 z=%UMp2ak~!9=t}fJdfXf!N&2-i(W&P^7@x_l#mk7;+4|L88vX@F;2G&3K-S;H~($WO!P+n@0ujDKeg8E}3pH zCZ2$m`rN#0{qCskz1KgST;_wrWF`+DFVlDyn^w3t@K$_B$~-OX-RdtLU-zLVy?Ahi zth>bhzeL8o;oW#3i}%SS9xT7<2TmUR)?27F$g~QnNKhtTrKUqjHA}txXYYec;e^2F|Ys9Psv|< z*Sv_0iJGfCcw1HXWDIY^XJvA%!NzWCg2oy=yIc2M(rS?H-aWZi=I}f|EAx37x2RUK zkvHIx($1UldRZLn@d1h2=)qs~P)v~rN6J#37Cyh+PgdqC_0jn`J#yWXFHQ6}2zFDt zyf+UHkZPWmH0NDQo_8&Y9_7;Ck!jWhKafd0_^qA_s`B7=GKM$eNm9%iWXYYff~N)0 zXVg^+p}%Fm9}k{Vt1vMSo+fLp58fc1@m#D_N4F+;>&Er~4}K&=dE+MCllx=_FJW#= zb<*<&4Hz}kqo#UVJDo>e^(Uo+2fvbb9{ka!4ij(0ze@EL&T5<|O+45~t@QppxV7}= zjd+5Lyu$rIEvW8+t_9blAp1(0WIp(TjOW2Vo7s)L0e>OOdGKZ_@ZkN@#?$)hSNb!g zx%G9~gFE+j{A)e1vl8oj$y!ec9wVJRi&1Vp%CVjWc zHxz1j!!h^2%ItL&KCqQTY)$Z-ox1z)1I~P$C%Fj*AwwG#edfWVWh8IIWzuV=OA-6* z?D+8}{8IYyw7&Y)N(297L$&v=72MP6?bb&X_k(t`mpJAsrQc^5bS(iNCE{Cu);*bE zKDbE6^5D;Rw{bjplnmurd~OdFt8GN_=T1_|@Wws6C$GyGp6unO^b4=a+wku)_!{^B zkn?`&Jh-N=!Yg^FMZw#pg9o3L1w8nY%;6<`cesu4+%jG_!p8BmKs=A)@wfe|doq&; zAC)LBADorjUv+4MS$K<#CKBc;1DMXZ^EA*?%jvu$H8bfGdx5-tdN_R40~3p24Gzx3_o;-g|=MKh>akf@;t*hZiQOrG2tXhu3Gj zC%a0CXK}W4^0ZF=b#;{*`sGv9G3LQ;r)nL*gHuj-Q{utfWsT>i#q`aeG&$$R&+?58 zf_oaz;KBW*g=eO8PacwWyy5Kb$q`b0y^X-d(wi4?+jHC#coQBeV|j4cRN=uBWDw6{ zROug=Gf2zz^C;WDR9blO4avq6&bLct7BAx-)9fMMgg47V-iANAz((=3&OeWO|En&v z=XmgQSjG%N!COd{sJm^W{EZq|96Kd0ETLxWx>I zBk$1Qk%9@;rE@wDRC%(!w`% zJn&?5>$@ZbbF#*o;8a<~gEz}k-iF2N9D*CY4sJBtZJ1*&8FquMHV7UlLwVZkfOl=3 z(6w#C-*56x<~X^m2|hf}8{olnZ}kRv@D*v{^|y6T{w)1(a&F-@GWMoiT}6h^_c`r> z;BTdor~MOn^ikOTPM`NYcz}%OX(I(54HZglZp%FQp0x4c$CBr1%Y-@psDHN&$oUd| z7NXz6tJ0STKag4;O#bFPM*gnr5x)QI!R?MPag2uA<0~O z2v3t*-hy+bp10wo2Oa;u@xX&VM%&$Fc-jPlM>B}yWh4)tE<l-3$K@2p2sE9n-_88 zzxyeOryVDFbe?!khVWpg4CL9z{m>y}c-n6wXOQ-#;L)FAu1w{@+higSJ}%?zxJljO{vWb~%QXf19 zS-e!n@%$_92Qq;t%eyB#$t0e^oJ{2{_<=0v!P4tCjtA@Ba0le|xQDFaO?ZN=<3;>J za@Du_2BmMhWANyb@r3l_!J_oy!H*=9JOTgLQo|jMuawp1E95J9+FIlGx=M?UOaEh|d2p8W;%Sc! z9=$gH_@)iz!GFon+ui@uwi+HtyKnI5!tuDw;K9W*l?S_bx-5A;Mz4)%u#xuMFdzLm z-jYrpJmhVMi3exPVqR&$kuwsnywu?wlR0T6wL?kmI4nxE^WO2bFU+Va$czU3uop!e@NghxLPv2 zjN5)yV>$!JHI$3trj1I_cnfJgrA{vLfd3DOtyh zHP#>1Q|yth^Wbvn&C@<2Jo<^WDDF3g2d63}ID)t0 zh1!Em4PW0X!1c-mmZ zi$)lfFooiPdke6t{q3B^T-ohsx8lRHm2Wl4~%?kjb)^2eNo=OtF5~>fAT2QtB#v!2^>#5Zq-qyOB5HY{~QNkm}^3pV^JP6(1k!Z0AL6 z9^vHVWxRHj7B)PO*N(QK_i|h!gLv9qYXf6F4m`!B$Q$uYX}I70Kje+c zj<*Mb!~f!V#}ly97sjHlePg1h%;sk~e&&N)%19m@CPR4U z!=o+DMpwGLd2oA~7VB}Oo-~Vj<{Dd6X;AZkPc4%ZuB}e`@!%Bc#gkcjBFPBeh%d@$ zUc$Yuv)g$S&XkEfkI~^Ky4|FmZg_OO88FAW!h@g9wTE&BA)DUp9qCrvzfmo)O?ZFsw5&x1c~)9Q#fV{U~n{U7oRB>30@ zTkU~hNtW}5d#n9-Y{%A`3|=fF`G(dy{;*+g>-+2p^T9zfhBx9#GL5ILcX+hm+4_EW zz=z`Je{#Ich(%cGyAzA})(f4OJb1ix#2Wng0as1CL-U|}gEa6ww#yhEd{>6?;1@DD z)<5)3M;`va_C1b&wCssKKJT~NQXcH~uwBiA)iR#fv31Y?4W^C)}Rric~W#tN}DM0Xs}ebX^&(U5AN`t9?5hb94V7|8MoN1M^fSq zxW9Drq_<8-Qszx~maO9~sOwTLiB?R}erXeZmiqGG_obF^=)dHzV$IzpBg_XMlp(x; zU&(l$=)|srYGtuy_%vO zQ#4;n+cJ64XwP(L{~rF*$zLMkGg9Eekw0`!@L=lz=K&AivR#j)nFpVgkv!OIpk6$@ z8DEyn6Yl>dvdQ*(Z288Gc$kdm&3My~oS3`~kNdGh!?SpYOpke7D>Ha9$ehgLWxQqw z?UZ;P_tqk6Ax~RHJ?Za-8ha$y?&A2DJ#g(VJ(BGQ+g&{PSLxtsZzxZQ9#PRds-1W3 zAr-x(MoILT3jTLUk7N=L-taSrl?UIF(Y$^S8z;S<;<=$clHH}ZK?bjt2A;MqD!1vEfCG&Q{UoD(!H^qtn$7jp!usHpKlSiCVh$^AcAD3GCg)CcMvvrtS?CGDA5C_1;K8G%mAB$&Q=IucIorED z$9WU;_+P1i*8EgAt#h5>Jh=NbhmQx3lD@ndqpeo7*s8SG%3a_a7(}0~^Z#m#c<{Ye z?~(_%ytGF$hBx5;(!n!$xh&_6mpKollQ##Y%*)t+ws#-%I7Nna*l4_4M)F*dd@7?2 z>ho@$H@Fme3+Cq7AfCr3WgahL)m%4_SdVYtY=d}k#I5!s&)^$(*(=X^Zkz6EcRQbW z@;9Fk_c)RHH?6_^`=3Gd2&?o3CfA6nY8i}9{8HemAAZ>^Pys}C>G)RZehV_DmHM5f11Jd&BlU1e7#3f zZ9aICtnu973@P#8Oeyl@4JTU3UbH@o-B&uPc|A_}x98;I0rK8|+*sm)H@|5>R`iPr z?|R|~&SW0U$vSI-XMgOQ@nAuUJQyv?qFq_qs*Fd=vg6jeJ@eo-GJywkcla{aU_;9? ze~|dn_1#UoP4mGSvV!L^dY45Xv$Tg9k6vaCwKd5k9(<}!&21jsZX+F%dGP1b#51_t zw`-El7u^4wNLK%+>KDBOd{cV!wC@>@{%412WIKolv(k^J4bXTrLi?MH=fO>S*Cbh(yUd&LxIMHM<5?WL zcTJL8ZO}}jKU?&0OZ&9(=;QY0KK29;4jxvM4CTR}%OKu_pUZTf{IVwbKxXkWZaKUr zSr}{Z30caEc+e>45N{q;lS?LL9Lr^{d%WTxt?76hE|bB$gu}-8#=MBT9cn{)6FxWA z;R`>)JJUICCQrM_@#rSEd9w=KJh-joc_Z#G#bxgQ&1CQ2>!jv^47STEUjK)h9P4bYm@B$utijCu0yh1v7aQ{;^U*o~w=6snI zi|~C}&da#tAGH?XX{)>CP9k%U$vX4F4q45MxUo9p{j5oQ+wrcw@1if>qcXyLaFGn* zxsZ2cpg|e;`jdWwya{iRX}k@0I?a86H{#`gu1V(b4Q=uKC8fDa_dN5#J7pCQ-XlwS zuu~TC=F@AE>(6lf`@ZTMpHY*%c&1~_OStb}TtYm9-#W{g%v)Tl|DIBlOpW#U{j;51 zya8LJ!0XScNp75K&+s;U;zAn{>v7%mnxy}0)?B0oq73Fu7kSYG4O$I?3omzUdGG@n z%F9@Pg%gJdmtE=j@e)S2zv%wgwd-GW|EqNTGap=it#iow;Az*}ARc_Rc zniOvNigSSnca$t|!K-93556P|c<}XC9RE26C4*y^J4<;M`@HHn@wBPo>+X8yo|jr4 zyjVxWHP)ov4b4ZF!{i@6<#@1O8hLOJnZ|?pKZ>Z~;OZKnp znM^du;V?7JNk3mU4C}d07UovrLi{;j+JWOjMvLxo_1~I(a~{dZ5lH?I8>T=+P;xT z8^;f&g$H-v)ZU6U_@|sN(cCc_I({e%V-fDIIpe%ogl9>Kr%fCGsvks59qBmng@cTWh@V#AtQMSAMNAV^8$Wv zbLRwaz`x5PUc~#i(8`aOapn(tCaZWJqaS26f&8W~WUkUIGMYx-DfJ!*-XjSQMt8`v zCwJ`=nij3xv7jNxwctY@U zY3ISe%X}V;R*$oI+Vathrkx>qbcnoL2J_&3(m%d2u9V)qj2{ee735+-p5M-{~BMP+Ub%<$ICvyc9Qbo0BPloqx2sV`7+U9I36qGcyQx0U1~gS+sUJS=M%Dm2Vas7UcxQ)?_9?>^z!u2H0EBB zzVG_yR{W3D^0vS9OwM-V58=7AQ>Q`KPM*>5^OzR9&V29^8OMWH$!H$jZ;D$iPg`@Y z_OIOL*0=c#4nEtVv?h3rEakzI&UXA48IWYaIohuBMtuHUXR;S6;`Qgbc6o4%%Y9=W z+(Cx%MvUH_^LX0B^F2Qwm|NfDGZ-yF2bxb?gYv8AdzbIsVb^(baLC^rZ{CC_+~Wl- z>e~4;?eJ+nI(_c4&`(D^*#3Y|LLO{=$fq6;-qYSQnZ^tFfvn@DhwaTr9FE-kz5)5( zqdpjTBc3J=yak_>fxL*{dCXfim+;WXoy4o#sg3KlzYl<4oao=BiGdzQ{q`>poDT{giC~rVYAG`l&$%WDxi*TisdD8)o<-xu&&)`0X z*Czefc=FiV3wa}MHrZj~1-$Dot`y#eugNN2!fkxN)v+F5lj_`O z9v~aFxI^*apQboYJb1434f}+Kf3ev-IIfR-Q-*`9~>sVc<^{x=egONT>p#bx<)-v#Eoxu;_!O>mCWS9f5=oG z-2E0O4sXJP|p2fWuI<7oz#mu86^Il6_mOOa0%#8I|>81RoA6WSM9?g|r%sCU$ zpg9^f|E%ad;K4m*ttSVMkWL;PFUxrm?|i|Yv%U?Z3v+a7PP;Ml=+rz$E&*Ev>=KW2S{IQrmxBQna-e}LH#GTNCxpNKK+>!hIe3(|N2zqwfIY! z#Yf=nGM~@Kr)2?8KKF?#1wH}$ec`i!2Y(`EJ_0XW=Md)%W{@_~qrSq=c|Bi;hkof( zi;u^=jOW4SGLbibRh!%()A(Y1SLX56IQwg_%hRUUJeprGmku7xz2Qr{K?zUn?vBT^ zSX=Ewq?^5hd8y~YN2Gxlu)2p@5wQlRN+WN@+a$|_`JVp%kO%wMsV~4Aall6YzezX! zUqXKTZ8s7R1ox5_-h?MhJI`+H{w|Ao0q@+z6~No@eOblR9^G1NM6d2ClJMYkDO=Nu zcS?i#HhjN6=N%Z74KCZ%je+O!bs5ULboZ`w`M&f!E+zB9*JMPj!7Vql(Yy&Oy}o(y zJ(=u?8=a0?+5jPT#Fd#_Q|WhzH9U=R`A}AsHULLKgGjGO2Z#g5CPmB|~^U z?jkKbgQrL)TJzuyQsiy;f~?cVqtw?1ZSE2-erzGRNyf*Y-*AmA|F}-y%BQbtJfN=n z{$AH_)UeSHC*9F9d9tYQAMpL*o7d~@oH=vm+*!|@nR^q-g$W_uE7sgx_N1rqUIY7Ew5cVtwuMj|AtiqH59sG~S5^bzI;EZLE>bgE)&7R$TcspY zC+^MirnxR(YTvVinjf(5iasW0{K&s-(RkZMC82O`iI?{$3SK>#{UZq$ydgv=Witv^R9^P4lv5yGoQEwtwJe_)P8q-Co=R1y>aLufuk%J-W)V zDh=C1H0iM^vMtQ2?=I2W{$Y4({Zdq*YrsQyj}Gqr@Nb+GyD$VCU)q~VHx)}`QB zmEzG&8hAAKFUvyB_;a%on+#Iu)vWxMFGNmS=g|i3ywLClvw}ji2J}Z zJ(ffSMMW;LZOnSFOE9Gu>}g$>`F55ZOENuk-CHA(HK|7nfo5cFdV2?@o$bA&I?@{U z6;Yk_@lPF7=&ZdUsxJK^OO0+JQkSgPV_+gMt4Civ!qfiQ|GG9h`8QYem{P$1Da1cr ziqMa;n>htNrrhO{5-$1Yah7|(+Pqkc{ugK$yEfo`q3PfD_5bVA=(mUeR~z;JafmBy zRQ<>9-fxn9Q44Iw$Ms(6KtLn$&XO}gk*X>W2?G0GI7nQ7EjH)e&qJ&@>mT*bg zGMs4tX?Y9(2#B1kFmRtn-fuXienE!ADq{pJj zkRFS|>qokG`8jf7Ob{G`?O3KpigLev@uK64BN1=hnYACXp7x8YHYHs|PUNB!MH(_8 z{2j@NMMt^h?=N1wh*;EBg7l$N>83-ZK}2>r%H=ymt`NC7MF$)rD@4_uqAwgGFNo?n zMGT_IMfNAm3&U$oYpheBtgwtT5{=Aq`#iNO{W@y-&vnK4fXj;1ud3V_Q+LLpq zYy0P$i3?vJai!YUd1m6E-Rp-)t-m8N$sYH^eeInO%$%9_fk)%Dzvh|=7yI#}ZRpoo zVqOCE9>5h|ah{#+A0Hb-slDXbKXhT%-Qx=ckA+F5B0QbLopsBaf67K_OZ%GB4XAF< zJ>5`vKHFY=dOTg4HS~;+K&NI+JKI^HowD|wTTW?*thX<;5^3A4`4CgNrJ}Yq#uOgR@_IB>)H+3* zsX-(ood^nWftRMp62cb;>S z%@%2EF}0h5p)375 zc0wf=(%%n85ppte;ou;xpL6G{$G%eOhkh9y9SVW3C@e7E)~HRO-B_H3-lJn#Zx=d} z$}HQ3{*QiTxVzHk5}n05TIoT$mx&%UkfyO<4_cc}VUZp*h(@vT9<&h+GFE%gA*iMC zPgNSD(St@0KNwQ$G=W=M=*Lb6(rU(Oe_AZk`|SGwI-J&Lfq`i8-9{BHK1Y|ah}tw# z&u(aTq1$d_Wo_DDpofk7LG-*p?;D5f(Jur#+L+Lgo{-SvuQx-*_83o_(NmQEW*l!$ zR|`Jfe9^54WCqKf!98?uoO77RB(E7WL+MR{_BMvKrYi(`fR(hRi|7#}`E?pVsny7M zgHD$PyQ^bxj-FSYgL8__>WHc?bE&9ml;PKj=278+%x-n2LwUs`XfN84t%#s8^gR1L z0)4ZSRp~IA z(_bk~XEjF9ZuEb~=n>R|Ry|py!pA?0aIb&lPJQmQ_Zc6@Q7=ks8tX?=L7-dMhSBsj zI?lK>ntrKO$-M&oW(VIhBgQPYJOP8A%JwACceEzRdgwmjJfpQ0FV=Dr&7cYF!Xz45 zr_nE!3RQO`ri$+M4;C$*X_k>s8R75IkE+o*>}U#25}qG6FmBjd(OT1}@tjK`nRa~3+?IJJ%bKGQ|TpR++q4UpE_HP&`Xr&8gq}*4;6aaP*2m-BAsabbe47(ggP@= z$t613SaqHbmN7*hUZN*7`jPSd4XRT*%Q$wEri%2AG4MC~nm65TEPGCURl3IbQWmyS z?+0B}a#SGrLAT@pzwi#m;HpBL$9-fv#RyESHAb;(s_>kiW4~&`7j!>cs0-os9(IOO zg!_Q27@u7HFDr&}mBr4yGf;HHa9e~oMe%S4l_iO^6c2sg;5b-*7V(nTYz z?|YcNVGYxb@OHv}fkv`t?S+5ne&bOG;Yu|+%=qzt!WNkt#`}YXZxK1N&|$(?^fbFU zO!(V#>|KR)bg%z!7x((d@6_b|Ho&+WD^#a^VZ9^FqO`3se*}gA>u%miAy#;BlieCA zG@+j{&rw1w9d0C!5*7;Fy~YbW=`i-y7-1=8to^$}2inh=^R6%j(H$!pE8u8gOc*CL zZ9~1-x=q4Nx{3L27NXD-?`{@0DKO~1T^NozJ!w0-Z*96_dUqpdyRfk?U1r3c6J~k{ zkK)+%Lg8z;gcY}hK)TfU=9X|t(f7nE=r60=E+g%(FkXevS4xBgv~gOAFpi#Lg(X6J z`Uk7?NVq}I7|P$mGX?%fdL}gSUKFK}I}r~%x?9`ue#ZFidk3Gd}-gF$h)JSYiw=<8%qK&R%^%{#cC}CY1i|st#2;@~6Z~?PDkM;xTH8DwfS7N$~f^^TFbJ z$>M(BA#k%8TZW5ADgDWqI6^$E(2tFl?}|s28n0cIiTg)C@({LJ*ydk(A`J<1E;nV~ zlf-Fo3`-}8R!ZHB^!G%ZTwRULlf`+pXo}ImE^ZR&G~@J25dqH(qYV=$QmPwU(?mZj zXzo@Kj^(gn*&%*zq5j4$Lp&QLxZxV^*$sSD;snF-OJUWT7O<=zU5&qtH4= z?epSbzBMLY5O=up&bT4&*69g$s_RNak&+ zD`hmpv`F}$G@s@fkNzj^6luD#d7v~|rJD@jccfoc?0}_XBsYXK1-OUhT>XT?gGTuV%kZ%lUGt| zV{5(T6wBgPjv=vm8%=%WRBVb(jhveDD5}+L!mH)nuZ>Ot@(F~YtV(UU7Q8^y+VWGx z0FCO%hynbJronP1rK5~%b>+7yPuG`yL@F3>Hj)iWyRsLJ<IOJzSlYJz>1x zQC^_X&V~>v>lWIjnk85f9gH(qKF>q*ILji{;%SVwg#(a-zhS z^a}X~537%@mVd(>-nB;ljMD2YYOS2g>%LZQEYY1tl?=IyBs|!{`fip3Y1b{&Hp`-= z?PgRAae^FT%-t&gK|POb6v@@OBje34 z<-Q{IGS+=1Kb5hat=TQttMVjGD3c{Y+ zr{uRq&vr>%x!Gm+kA`utikRY%F)v^K81vYu@}ul4(3M90^D;d91f$na=y|c(RB_cY zX*<=7u#56U0WN;WWw{F0q_O{+JXD|!4Zj=mJOLADeW5%QW}X+y?$lN%9z*oeB}cc9 z3yS2;s>5pEmgiDmw&}JU#eLard7&$9ZzMgGcZl4p{v$WiXq++Nh0Fwc(s)3X2%+Bk zZf1WCK8;4^0SAlR{}3FWYZkI_`G}ojYBx5?s`#=glHyA{v89p{jx$icq*Qac9a*MS zz#d3S3t>kj3-eHXjrOu~nO14s71{HSprW`ct;6c7O3l{OA?xVgFQ@3)Z9K`{LqzT0 z3_$*!E+Uz1;cdAN?v4jU=fUvXRjYiQ%~X}{!rxt3o~n#*%#eYy-9n~%VjjAsBAJao zcgS!r>-Ih*{E*Y%Es+h^l)%PsyUE66hhxcwC(>;_Hbmf+I9*FQT6RIlMU#l5gJe6G{jbj76m0k2}!^KCbC)27% z%Nokp<{}AHWSI_Nck3!|&_Gtdo>Cibse3)8uLRGY(MT!PdDUAf{du_FN(on}hY{LN z*=?nR+3!7+v10KH!RXgh*-eGV)Nt#iw8uT@6QgIe;x5pO#*jYB7dT5B+CXKs2#>a5 zh;l=v^^FflDt}6P{tZ5MR<}?#`(0%o&17_}QqA+hEptTsZbYYc;o~a*Vl*799H)Ff zj#GA0_@~Vil-snqv3R1=l)`WBOHiID^b=-GQ^IH-do)c6LAC2nS8CQE7r3?l2eEkN z{XcU)ItSZK9#2OZ!>22Qsqo^Q@%;>C0;O>3GnFK28+Hzg5$@4P(dR>ROjl5xZycJX zWKep`h?}ECi#)WOt4yYLqu~e2PQk0?2}hB0yvQM5#FlH^o3ES^aDFUJO)+)a# zh{t~0sKip?*%qVQW@VA!(PpdJtbfp~AJ8n^bKKgZ974Rlb*nN@yZ*Vr*UcK%avRz@ z+eqD}L`t+jyR<`jz4q$$0-37faYOU(==`a+MUZf&%Tu!wR-@Hd3T^|Uje<<{EnRQa z{90*j!M4+VztVu_8&mcxMGD_44l7Z#im~&s@&zoj- zJ7q|Zl;?FVdz(9I7u?ox^;cI>;jhNVrW$GnhQsI@sP3(T(+dqz5v^e(2vuJbsm<8h zTE(pnoMxChS)+l*+Aiu|3A4Q4Tj~;#hjBgCkrK9>?!DDX8iJt%163iw^BDqM9N5+d z6*yzt8k5a{^l;-}>^n^aBqxV4d*cUS@47M(<~Rzr-UZK_qJx$JSK zI+>rP-ry#S+?zECMAEd@#19;| z*2DtFwAMre$IBG43ty|FTcoYw$-y0^BI_GwRpvopI!}2vxUyGWW57FVGn!4vQfoDT z(=6AY7UjHd7Hf&LqgWGgWyO5KJBoQ5-({(@XutPx=s**ujzS#-UXO(k@OBj8)XckL z3et|QmDR>EPTH@ zl8$Dd?^j#!#kXHwTj$MGXf_B9a_@HnI`2vF@$NB)P<8_{rq04{Vq<9s)K)xTI-pKa zsK&x_)uHgyYjf2c+TR#*M4dL3w^tgz$iHR@G74^M{kI?g@OL2v`y<{BX< z)kSd8bJ>AYsK7y1d`jIY?U{qa=h`!>58HWK?Jew_!ycSc>l=;EsNcEJH`yPT)sJw^ zHt83&JzdQ*e^F~95;*gV+E1XXjC#MSnt;_6QlLgrkHr&sYonjS1sz2Pg#{SPuB$sF zT$^>ct@adgWMA^Tx>G0_!P@_!Zlt}~jX%`I{A77Yy(kIK1{>Xq)n)?CW;6a$TjKU} z$6soY<=Q~AAy(TBo$|(GscS%ncU#c!{ z+^#P=_@f<;2&c=$2=`_M)7l_S`@iZ#f$lZLXX;AbJ?%}5TVM18LRxF^XKah2 zMWHUGiZ<3`Kv&*F1Mp_bV|*d5p1QF_RclQ>*mhM5M)3C|T!Bb$v5OY15A`(~yJ*d1 zT#^j7YHhf;T@V^@IY&L``^KtGOfKf$#15FvR`MKX8laF$xf@|*^2gPbsOVUdo54W zwmdUmmIWB=I%}GOW6~ClX}yHUNyd>=8l(I|^NgnAJ&-Z-oEB}d{5#2U zptLPyIajr&meUg*=~lP{xT+aATpO7;G+b{>MweUK>mt2pBo}FGL^_c9-POj?do1Oy z_B!IN!*{h+B5o%J7HfV~e=&;pT3G2QHs^uXO#j)D%r}#@*~JIiQ;gMxzqAq=e&={8 zCc*+%^Rbq~X@Pc3-2o;1UhI}e44<2LY%a^lRkIm|KX(hPP*So zw>s&^PCCI!2Rdn2gzUO^QcY*_qVuulv+z&vn$E{MX_Esl=<)krA`mU4CanjvRn#wwQpx#rR_9i%KoRbcA(ikUw%Sk&q zX^4{sIH{MD7CYN{(n&L&^b;rjz)5`^)$k${oZ~RxNf+0)EDN-;Vh=skhL20|rU+vQ zOarV#bYeByR@GZKWXbLHQk)THgz0{iTG-0=dML>8_IewZ*-ls7ro|6WtT#De+Itfx zjJ7aa2feZC=yAf*JL=xXunu}h1s5I}UG!QshaKc3m0jS!&2R(dd=|3svYpIl-5_q8 zqTurwBx6Bk&@|8hQ2xW^wPm|6Z>xr?@^pby&xh^mroY)XRCkD7L3!~A&;U^WQ&ybI zOCT@%!L<+ZXU*Tz>v<$rsSxu@&0@>m(!D*l|3|#rAujxn_^v}7;{LLcHtae#lJ*~Q zx2j|Ibz=y5c{2YuPds_c&ikETKpt z7kk>cB_Hn!%e)yO>^e$RE%vDpbG1?*X6dOn#NA=5p88}p6H`8Ah`ub)m*q$6l3O0q zWj`0$rJi~{ekOcducg)>ikzXQW=NDSTY1v?(;s{R>lm$jviP@kAH4IN%S&v9IN4Dm z{B2$G$f;E1VnvZ6USunyz4$|h!Rj#cA>qAr$v2#*oj>v5(_(c}3>wKMMd{wlLL@f5 zm-)bDymU5lQXQr9QQDSYsr1VwS4E+ek72iFDadEO>NR3$!pP{S z3$h;b@leV3=6$IMOnW-o*`E>*MBB-1 z$sqlVEpA+eq1fS+ECb@$q`~^e8vV!fy21lwqh4FF_|sfS!8l63nn+21)_jQG5{I9W zL-aTB4lHAc?j5|{EI>$dA|>mm;88rS3q&tih5a6jiQvOLhNF{wSnzQD45r1S;d*P3ChzDoZJF;mEtd2p#6aI-qs?Ro z-qHJo`A3dboV{1GL6fp$~a{mXhS?Qcq20n^!}k-DcaeG03R zr&nj`BlRHL$x6kN|Dj|haFSgbsryxXS}7}_6efVDQs>yQf$;Kw+6G>Rc{k4@RMqrBm62JiQ)kG8+3; z!Zb&@q0e!W2L|)Bv#qbl5>*lo9WGB`vq$TGZSOjZhrOVr6L8U)=6p9Gm!~Zfv7C1y za<0YHG3D&+XiNoD-F%ux0-(;#++~ls<#0?K-k{H@K(c_wJngL5D>76p89H1Z&U(h{ zezt@%b#zs%Adr;+>Kv~(^0?x(Tw4;zYd{|RmY2vYE0Jg+R)~2vrX{l4hRkr(#S_o( z2Ke)|xiG4eS7eF4q!>lHJcNaf(OWmkG>c=!mbej;=t@YO3rST4(ibKid5AvB(kx>P z{0S!Z7`>J)?tT7X_@(m!B*7w(6+j$&Fb4jl4YR$gHv{SNE<7`Q!@K$mK5xf5=IvOl z{~~sMELKwydo)&`1{XDc9F`b7)i}Mgtzee335m5yYBPZx1q#@`aeAW$k+U7GU5$&t zAAnGv=3^M+PUO%kq+HHAj@La+b*czN`C3RYeQFC_u>$E%Gn_vPq_ElJb#LF4GHWFY z7L*%_DpO}W#_N4>|3W8Vj%st*cd2?!7V?EI=?QHFVh4J#juWs(d$0i$^hRwn=Qyf4 zzO6vgfVDi$CkRIT6&WT-EOfXWT{}T<>EWN`D88zlKxT%)Q879ZGg9MX44)JlNbt!9 z;o1JI!9@L2-+Z2>NkmKZAyQl|k{%(DQC$SSMNLGrPD>4<8thFQ*r3Q%cZHhw-gC0EV-|4W8;ymv4R_I zu8`(&BGD5GW|Kg27764yu!3bw)koM|7dW4TENd#B4Y)GjY5D@h6dBV{;jL`XG`)_k zpiB`R8nOoK62JtWhLvo^j$Jkr3Lvijsadb-j$L>`ErJc16FZ(_S(198Bq>F}c^yi|ljB4Vl%O9{?bxh~}0&jj)TVCMNK#0P8N z8s|y0P!AFgInPIb&CvZk5*IouA!Z0f1!7sZ8E9lI7fYCpF;z%$xy_%JJX#10+7r!50Dfyzg)&A7i4n0S|Mj1Gj(s9X~L|#7a=ggO~WD1)ct(p zxHVof*A2_nm85?ykbMBW*i0O)@|+KM-z|{q00w@hW8jgCcTX2)V!64pXD|igJ4>(S z8?hJ`JtX3R&{2kaP1q}t!$1U!o&~M>Z2T;Y{(QDZYt=I9m z>wMr)yug|aTx4fw^WCqk1ZGt@#5^0j-)!A4EaDSK6&s(#$^aVkG=DC7>?N5Q(Nr;e zF@Wmi%lZ{ABD^@hmzSLle)Y zBx7$r&lT~U!M|bG2N16(!;fWiaUF%!DYO#5#Hxr$tCIj^bG7sAda~ZRLHIIW5;HWO zkn}&%$ABs016x_fdd|i4SVNWm^8?+pNzrnr^9)sq zrG>+JM#&dEq+G_X&c_aoh+{tXk`Na7 zAr>DN{fBzJuoWvEwVPR0Bujy0p5{&fjgvza@{*j_Iz}Z4(BpD!4%|{6JMbaQ!vlP% z*9r?-$adFb8^E4k!2#45qW5AK`?Q?zEKRgJh*sE+WVR3quLWUl3-yiofOgkH^hyvrwh)~i#BMEw{|;jE$Cw-#fsgeL zc*QX3W8E`&`Wi=-(<4Qa2dw33J_7L)DVMQrA0v)UWPywLZRDqp65)MB@)$^D10cp{ zhjSO18rDV2R$^W2;j&a|RaVERpXzOB}v& z2~LEFJwHKH5PN=tE}8$CvwE1f>GMUB1z>aeMDJs}Tb`wnkcAjz08afAxVj#!$x^f& zHkNW5>z!2z#nBv}JZ%DCW2tVpHBPTEAG<;%DFBXusd`IWVp$fvc)%)=v;q(jq+$cg ztdvEVNPGbVyQz9-9#t>nQFR6z`l;?0zFH(z)?gK9;4v6~&es8=c$#-QY`-FdzbGy% z$0jYqRL*u5zmKBor)WT>;^i{73lGM%3rcu(gwlP$b%W!%oEO)Mq_lFu|H|;({PJ?_ zbdp}1;l4>?;mdUmAINlDjwv;mjajbOX;tX7`yIX*I}Mz4ra6BIpO@|CX}$}^`)kPAVRp=5j2uXf`5R%!OCer~Rfs!|Fmk9X ziQO)e*#JfmDz*ZyVjkQS67>~^2q<6&?HGOpsdfa6A)6eX;J!;Fg8_e@HocTWUXh^_ z2z0m{eq;r9_jG6R#IHp%G7H|cyto@Fm$4-)&^vHhD{y>?*^CF6?oo$t)kyJv%z^`^ zdtV8y#cb|MOmJ+7EA?i+=gX919L>x8UL;omIG&Z5TN&DuuX>grDbGGXYa>TCGy4$aRJ;PGK|+BcHg{ zQQyudQD30HGtK$gKmv6;uPdHXE@Nk5f?tHC=^bp1w>dR1&>?3< zvJikLT8%5fJhpB%Zs)KWuf}~sz!znT8s0mJq!EC)VhuKl1fFG{n^Dv#b2=JYj9f@@TK#)C{_%)e#?9= zyqqnnEDQ4}9pcMC6dUp>0`l!Vi+jG`L{bk(W6M6pBuy(T!JXZ0EDvODXWznDu(sV% zqI!`?dI5L2s(HHcAmuXl7^*e6Dn;C6Ii%!E3B-I*`tKsS0JLGv*TNCE+0JTC$30=} zS{y)EI8C4aLnJqWWS-`uf+oEpL(MXw!{rEy)}m&`&f;lzMDh!8zN~nzLds?AI#h+? z?dcoTqsW&~vZI5U!P%W5d^BMsjo1MItVtI&o>C z=J1{^h8cRF2Q16hVd^7pS*O?QHtQ?LgS$Qw$p)a9KghgDt4Usx!NS0YA{h$An`ICo zrCBOuEbucpt9aJ-Gt8iP*7q~SOQs?plEhjh1O>T)S#05FddpT`I~~vUcr23IzH3J~@H#%+Wb<*JG9Bp+g)jl^3Qr}Z_7yamkK%|08Yd$Wv9`o^{i zd-&rB@$D;-$-rQq<^}N9=oJ}uxO`k{aXDPkW=z)YY~5x&4J)4$e0#@*c=l%fCbhCl zpCh2cwapgXK_(iG2hE!;k+nc9PdjV&iVRCP3OZa4KfMLb%4eImz=h%haSLX12z#^z z4+>$nt>#<_+lqH$YqMd|Tp__EVTDAp0qmPw^@+Yg-y+ML02rW9ytrB;ku3ls>uvfV zkBdATozVOOpG-r@bBAjsEgn0P`bMnyVy2~#PBq40DMomj1B(+BaIXK z7y3l}g4nGuaLtJ&@TK0l+PHlc`l)*)vKbh+kG}+|sV97cG=M<*OZdF4`y4(m|4Y33 zDkv)l50hX(4?tk_C2oN6qHH@J7s>i=*K1YH-0!HM=RS#e18euQe`oMFEZbpaEzjhu z1J_#wnOq4G?{@4?sNW8K7%s%;?m%ntA_wI4k^>H7H&009DNw-E&f$7R#yu@`xV+>5 z6TZ?r)rk2H`ZW|%Be5pdR4uH^076bnq%(k1@>hDjw!66sdi~}ZJOW7P>9R-sSH>R! z8P~%T6^J$n%$zC#93Bb2}jUBy5rK|v(Oj-dw4msYP#SYh98Jv%6 za5mRMUkllFk?VTT#c_W?>PnR!Kq zUZ^O?-;-5u!mCj1BNI1YnXGWy;(S#g*_r0NFEEY`&(ggcjN@WHYzQr1QsuHZ#NK7* zSVk5G&z~JcDUeG@dHBPB@@EG4$z&aX+l1YwQUefM=ne(rd_M4-m?R?1rcE zVKca*4_mz(F^-R^NXS7b_|=xNyiW04tg_vD>l$V*k0KLWVk(DXDz~a+uHzn^*<62c zJ}JeML^VUdg`h9ly*)U?;MVmU{Y`u@J^UNY#1uB=8#wtCu5V6{R-|!jnY;z8HErPa zRw#bs5je@dg(^L1T>3^=DO!Bp!0okvo}f!V=%!lDe_!N!f=`VIzr}-@LRoelWd-a{``BaOxlja81l5g zLj2FqQmwhORV{A5TAs%;zJlSoyQIy&^>+g zxELWw6{t#a0Tt@(-T`c62uQ!f5aKoBcRCB!&N%IE^&^9?$mAWsA;XnyS(?%Babr^^ z&b%xW?_ba^Uep}@j-*`1+J28~ARl)1dyKMAJ~xi9@NUcG&>flhFg?edwZS=hXOC=W z?!$*zMI|z>&S&JH#kfJw!6;)ukg8V9mCWF!DakCA$=ql73?!dD&(YteCt1&f_=4)> zS;tcj3JNI&*0L!Farc5f^B^1}-oGEzd-;a`2qhOptSQ*`ro)E2LTXtREToQya5XcI zXY*H@Dd=KPg|r255q=2sY%ly+MRPexyVl;uVH@qFt@`PI(*D< z7!AP$0SUa~RO(VoA!2|+_B+#@Zw{ohC5JJ&;lK~0Z!p|Eb;bDou-=O{NVwp5!pu$z z*#*S#v<812uMsKNo0QAh@FOUlV9Y*(Hrsap1T{W1)A2dPP=zc8u;2Wk|Aq%_JL*_V zNA+elauJHAi^6GdvO>B5h~V(CK-*ZJg~4l@sF2P;k4mbq$lye+(BX1~b9wp*kEhP! zPp0A81n-M*#h#~oRx2vAf)eS-;@M9x8kS=^e%mpbbvuq3ig@$5-XS>h7hVA#LRLl3 zqHaKtDI+Go9%+Q^$I)(7`2>0#%bybj5-0R#VVlaz$JHf&e2LE9t`JjZ-ul$Vtxvs| zZZ*7S^diMg&~?pJ^0bgyF6euk z6yDte>!=@WR|d#{UFYx#_mE zUZD!f?15eYaLMtL{&Ry(zd4_TRT(f=B^>}$W;UaejP3dvULAMy7x~?Mk@-Pc<4G!c z4VYi#G=kL;@RE#}`C}kMuz3-8*WQJm9WlrOgU?Q34MfZ?UL@-GvBmC@NyQ_2lkg) z#^u4wGE^V=V}MO%mJ4Ay(NZC2yI|R7nlL9gZWN);&ETYrr`gCK6-}~EQOWB7oc?8G zg_LDsStL(Y$zA{#CYQ1GX7Vh|uzS-n<7QwvIMbZ33E-ysGQMX@DYJm*y`*Z^MIi+! z&gD30@&^`_sl(S;@wb6q2M}Xl*6UUCzf)1ofLSVe3-G^Vx+>q)B$XTiR&X6uit{&t zWH$U4Ea7A>HhUfK_TjM0v$>oHKrz)wxe1o>i(V^Qy6dpCXs$}O0mY_eO!)Iv$V;+H zW?zy!%|@XtmrKm!3SQ79v*;^0?_(FfqK~zadw7U5GFtkjN>*<-d;Y5K-6Vo%aof{z z_Z2UZ>Dw?vp%TL7+%{e*M?uEr*rcxNox=)v7QrnMMxS7AC1j~&Cg6}0lmBIpN^-d# z@BVmvtA%`1#?D^FUSD9`yNcsJUI9g4!*s-(plkXGkKM)GFd>IrG}6#jBZFC&0<2`@ z7GNc(vxP|6(zy~3i(+b!SWP2~0BnW@_zWTOfusCkt46AOXozB-6(9!9}6 zG4ioo$*h$RNrfU;#>LKcT)q{X<$18uhSS`ca%q8vKUJ;CS*IYLUif|EAZgk@gSlc;SMPq9+R+hkddNf29sUsP>!gIUtX# z@OkRoWulNZ3@CxT>`|3vm37mhw-X2`t@bVfBw=VQ|^f4}9en;4w$>#FZ|Bcu(A3JM? zelPx1BlXrIhGYkR#{#gjdlkgj`Y#mCLE+z6xRL@WD(f_Y>&rhdgTr~Y)2#)p*T_Hs zA=n?dJy^@Lu!1I}Yh(d{3GfF3_LQ;`aE?(B^X#?%n)f7bTs<{IBS!#)X;2p~{%hX( zt-6EP9HCDf7Qfnnr2+)-G=DgH>lGQyr$UFzaVEP1KZFY0fyI<3{GPWi+Yghk7duUU zwnZaZz?DiSUy;FNGIY4S_z8P{N3V{Xd*8d5UAVx%i_`k{r>GEjREg0fJX<4kzSa2A z?ylZwNa!<1-4~tF$mc+SGtK#ZfGbZsJMtA79{f@{u9vHk1eD`)wC|o?FF4Il)W9iS6mytY+(3n8C)>4c}tekd3YY%qW8gX*UIN0q0` ze#%r!6hrt&|AJo{?I^_%;4+~U2kCL@YVxDe>%EPEy%}=V)R!3wwbZlUZ#1 z3p5cg4>>7do4HV97dQ!7DO>OxHY?j&M$qJy>n!tV91Elt&zFfU&coT}IK=M<35fyl zW{z6?@VY01>%T;7`IYp^_vi=!(Jr+l1;TfCcW_pQj{ zVsAj=>O8E1x^&*tR5#_!!-Bf_^K9PNq4@OmIwn7g;6P%ryy=_Dv&@JYZ*b$?NWo-+ zS4-GLjdd2(N>1>zkTiD5#p2gJ2W;h^0{@D1B7P-0Edak{S4p-VzfPSAS!5+yEdLGZ z0RB6EWB&gyw>!G;|LtFHulnjQw;Ro?SnN`u-T8~{CmT3^qZ@xYf^b~K5T^%QNW5{Y zs^x@`bQ$@DfY8uFynu#4J0Kc}17-jV06UNY>;(1$`M^!!3E(Fh&NClXbE%!MgdcSd4L_r0KNlG0at;0fYRJTssh147!VB%Ny0y)fyuxe zAO%k=E&)l{~zDh(~Qq1C*hSrC>z*|>(L$+-

O2{ZrY~|oHy%FvC60+Tf4y#Vc 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 diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so index 5bb28ed1dcccd58589896aea99b4ff5e52c8600c..2ce7f770c2f3f368cf1a2ccc49795c55bb7ed7c1 100755 GIT binary patch delta 1222 zcma)&4{TFa6vp3q?S-~X-e4EHL1sFji`yt8ZeUl&`UWgKvk5b3Y-Gk%$DipEQkbSj zSxLLuVV$#tag;EesfsRZV2%|^=d$J75 zu&IK1ECq^1nws0xYgcfjf)!bQnV;2bQurll$LBfi_>>YY(mix1oz@P1JUKm_PItvS z%vx{p=}ZYFC(*i+JqWGByONawoW_WQdEkBVqk|a?@ROKa&AyPKTZ~n)wE&mI&1x2x zp+Ss3z=~-{yzFF;?`~lhDPokhr;}PQ+e$)%WcoVgyN#3|Y)DQ$M51+8s3dmGimJO$ zO79j$B-&=n@<=>`uh+1p<$0iAmv%Q%qD30w`rWR*oV^>pN=nK-&m>ONuqWUvta7n2 zXv36?ZM5{H&DriQtXaou;1KRx$KHi}wAV5pTt=*A-Ecq@uV?uH$8c30TLy37!*#3z zBDh}<`|*?>_TYpbya;Xz+tA@=b#T9EceC-wU@?{>d&zY2xLMMA*Sr%RK}O6W<`O2t zOjw9{L>_UE7{U3a$LC1&+4o`cS)~#rw73)}y4#gv^tu$Q=yWLyje+;3Z>2vXjDP$) z$=unu|55YL-W&h3_P6OIDsk{>I!*5i+3tU)VPpVP)>iLVFKp(hi zf+2dP)Bl+hBwHVB-Jzl-2#115z{XxAp2In$T^EXhm#c<#V@4t8E z@1Co>B*``dt@LN=+1s&)6gCg?&GbP1gWO%>lJ)V1=;46B0l3ZFPSMX-NLqlxPfao*E^QQDg#EMp>Z>2xHuN8*loHJ zbu}xqo1>EklY_NHM55zM88hZtAtb}ZxDCGV zQwr>m|nq$(uV!cov09;^jqA zb^nvn`$ZuM?_$|965V*%&Wp~+gCDWV z!6(6sGY;NpJe)QxbRR-{Ew@7idusU^C_rl+_riCGb$k$pWXa<^58zYWSkKqO$GE$m zSHKZ`OACW|N(+6M(n1%4lR_)noV*?!(&Oak_kbD8k-wQccEZ41p)Ijs3^}oc$RTnG z17ReV63d8(WDJ*=o>*dJj9Ggy`I2cX<~dA{(zov0*WO#!W9jSO|4OG3cR5UDV8qR} zrqwd&FoE75o10CaBknzb>8zi6!1Mp=yZ%3GZ_Ulh9H;351e2WpUHwg4QErDS{rdqEp@ zoMtypknst;OJ7YFo+Ly4l8loWj)-Pj>*t6V-57^fIFB^-ucAOw64W`ApG+vR-ImyY9f*+}7O2 nV9mBw#A?DUYqk}P4Z$@2k|;34rQqE}0Z~X4$-9Z-tB3vp%+%mT From 2e5222055ffa1e721221753c26faba342b920712 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sun, 6 Jan 2013 22:56:16 -0800 Subject: [PATCH 02/19] BulletSim: comments and removing small compile errors introduced in last commit. --- OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | 2 -- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 8 ++------ 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs index befb076d61..794ee17f2a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs @@ -6,7 +6,7 @@ * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyrightD + * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index c215e3a2db..fe48166206 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -58,8 +58,6 @@ public sealed class BSCharacter : BSPhysObject private bool _flying; private bool _setAlwaysRun; private bool _throttleUpdates; - private bool _isColliding; - private bool _collidingObj; private bool _floatOnWater; private OMV.Vector3 _rotationalVelocity; private bool _kinematic; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index cc725e8bbb..ee18379a2e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -95,9 +95,9 @@ public abstract class BSShape // protected abstract static BSShape GetReference(); // Returns a string for debugging that uniquily identifies the memory used by this instance - public string AddrString + public virtual string AddrString { - get { return ptr.ToString("X"); } + get { return "unknown"; } } public override string ToString() @@ -105,10 +105,6 @@ public abstract class BSShape StringBuilder buff = new StringBuilder(); buff.Append(""); From 599dbc3d9556b7e2693ba61d7e234ef943ebad6d Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 7 Jan 2013 16:04:21 -0800 Subject: [PATCH 03/19] BulletSim: fix exception when re-creating the terrain when loading an OAR file --- OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index 1d55ce3f1a..8244f02e8f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -142,6 +142,8 @@ public sealed class BSTerrainMesh : BSTerrainPhys PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_terrainBody); // Frees both the body and the shape. PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_terrainBody); + m_terrainBody.Clear(); + m_terrainShape.Clear(); } } From 8452c0a8702ccf7ea045740dd829c69a6f509845 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 7 Jan 2013 16:05:02 -0800 Subject: [PATCH 04/19] BulletSim: add function to push avatar up when hitting stairs. It looks like BulletSim and ODE rely on penetration correction to cause the avatar to move up and thus allowing walking up stairs. Object penetration was minimized for walking and flying (so one doesn't go through walls) and this stopped stairs from working. This commit introduces avatar movement code to check for collisions at the feet while walking and attempts to raise the avatar for the steps. Not yet perfect but movement is better. --- .../Physics/BulletSPlugin/BSCharacter.cs | 97 ++++++++++++++----- .../Region/Physics/BulletSPlugin/BSParam.cs | 18 ++++ .../Physics/BulletSPlugin/BSPhysObject.cs | 27 ++++++ .../Region/Physics/BulletSPlugin/BSPrim.cs | 2 +- .../Physics/BulletSPlugin/BulletSimTODO.txt | 2 + 5 files changed, 119 insertions(+), 27 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index fe48166206..939d38a5d3 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -184,10 +184,6 @@ public sealed class BSCharacter : BSPhysObject // standing as well as moving. Destruction of the avatar will destroy the pre-step action. private void SetupMovementMotor() { - - // Someday, use a PID motor for asymmetric speed up and slow down - // _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f); - // Infinite decay and timescale values so motor only changes current to target values. _velocityMotor = new BSVMotor("BSCharacter.Velocity", 0.2f, // time scale @@ -214,25 +210,68 @@ public sealed class BSCharacter : BSPhysObject // 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force. OMV.Vector3 moveForce = (stepVelocity - _velocity) * Mass; - /* - // If moveForce is very small, zero things so we don't keep sending microscopic updates to the user - float moveForceMagnitudeSquared = moveForce.LengthSquared(); - if (moveForceMagnitudeSquared < 0.0001) - { - DetailLog("{0},BSCharacter.MoveMotor,zeroMovement,stepVel={1},vel={2},mass={3},magSq={4},moveForce={5}", - LocalID, stepVelocity, _velocity, Mass, moveForceMagnitudeSquared, moveForce); - ForceVelocity = OMV.Vector3.Zero; - } - else - { - AddForce(moveForce, false, true); - } - */ - // DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce); + // Should we check for move force being small and forcing velocity to zero? + + // Add special movement force to allow avatars to walk up stepped surfaces. + moveForce += WalkUpStairs(); + + DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce); PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce); }); } + // Decide of the character is colliding with a low object and compute a force to pop the + // avatar up so it has a chance of walking up and over the low object. + private OMV.Vector3 WalkUpStairs() + { + OMV.Vector3 ret = OMV.Vector3.Zero; + + // This test is done if moving forward, not flying and is colliding with something. + // DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4}", + // LocalID, IsColliding, Flying, TargetSpeed, CollisionsLastTick.Count); + if (IsColliding && !Flying && TargetSpeed > 0.1f /* && ForwardSpeed < 0.1f */) + { + // The range near the character's feet where we will consider stairs + float nearFeetHeightMin = RawPosition.Z - (Size.Z / 2f) + 0.05f; + float nearFeetHeightMax = nearFeetHeightMin + BSParam.AvatarStepHeight; + + // Look for a collision point that is near the character's feet and is oriented the same as the charactor is + foreach (KeyValuePair kvp in CollisionsLastTick.m_objCollisionList) + { + // Don't care about collisions with the terrain + if (kvp.Key > PhysicsScene.TerrainManager.HighestTerrainID) + { + OMV.Vector3 touchPosition = kvp.Value.Position; + // DetailLog("{0},BSCharacter.WalkUpStairs,min={1},max={2},touch={3}", + // LocalID, nearFeetHeightMin, nearFeetHeightMax, touchPosition); + if (touchPosition.Z >= nearFeetHeightMin && touchPosition.Z <= nearFeetHeightMax) + { + // This contact is within the 'near the feet' range. + // The normal should be our contact point to the object so it is pointing away + // thus the difference between our facing orientation and the normal should be small. + OMV.Vector3 directionFacing = OMV.Vector3.UnitX * RawOrientation; + OMV.Vector3 touchNormal = OMV.Vector3.Normalize(kvp.Value.SurfaceNormal); + float diff = Math.Abs(OMV.Vector3.Distance(directionFacing, touchNormal)); + if (diff < BSParam.AvatarStepApproachFactor) + { + // Found the stairs contact point. Push up a little to raise the character. + float upForce = (touchPosition.Z - nearFeetHeightMin) * Mass * BSParam.AvatarStepForceFactor; + ret = new OMV.Vector3(0f, 0f, upForce); + + // Also move the avatar up for the new height + OMV.Vector3 displacement = new OMV.Vector3(0f, 0f, BSParam.AvatarStepHeight / 2f); + ForcePosition = RawPosition + displacement; + } + DetailLog("{0},BSCharacter.WalkUpStairs,touchPos={1},nearFeetMin={2},faceDir={3},norm={4},diff={5},ret={6}", + LocalID, touchPosition, nearFeetHeightMin, directionFacing, touchNormal, diff, ret); + } + } + } + } + + return ret; + } + public override void RequestPhysicsterseUpdate() { base.RequestPhysicsterseUpdate(); @@ -342,13 +381,11 @@ public sealed class BSCharacter : BSPhysObject } set { _position = value; - PositionSanityCheck(); PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); - if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + ForcePosition = _position; }); } } @@ -359,8 +396,11 @@ public sealed class BSCharacter : BSPhysObject } set { _position = value; - PositionSanityCheck(); - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + if (PhysBody.HasPhysicalBody) + { + PositionSanityCheck(); + PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + } } } @@ -373,7 +413,7 @@ public sealed class BSCharacter : BSPhysObject bool ret = false; // TODO: check for out of bounds - if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position)) + if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) { // The character is out of the known/simulated area. // Upper levels of code will handle the transition to other areas so, for @@ -382,7 +422,7 @@ public sealed class BSCharacter : BSPhysObject } // If below the ground, move the avatar up - float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); + float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); if (Position.Z < terrainHeight) { DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); @@ -485,6 +525,11 @@ public sealed class BSCharacter : BSPhysObject }); } } + public override OMV.Vector3 RawVelocity + { + get { return _velocity; } + set { _velocity = value; } + } // Directly setting velocity means this is what the user really wants now. public override OMV.Vector3 Velocity { get { return _velocity; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index b9bd0bf2a4..23d573fd65 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -75,6 +75,9 @@ public static class BSParam public static float AvatarCapsuleDepth { get; private set; } public static float AvatarCapsuleHeight { get; private set; } public static float AvatarContactProcessingThreshold { get; private set; } + public static float AvatarStepHeight { get; private set; } + public static float AvatarStepApproachFactor { get; private set; } + public static float AvatarStepForceFactor { get; private set; } public static float VehicleAngularDamping { get; private set; } @@ -403,6 +406,21 @@ public static class BSParam (s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); }, (s) => { return AvatarContactProcessingThreshold; }, (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ), + new ParameterDefn("AvatarStepHeight", "Height of a step obstacle to consider step correction", + 0.3f, + (s,cf,p,v) => { AvatarStepHeight = cf.GetFloat(p, v); }, + (s) => { return AvatarStepHeight; }, + (s,p,l,v) => { AvatarStepHeight = v; } ), + new ParameterDefn("AvatarStepApproachFactor", "Factor to control angle of approach to step (0=straight on)", + 0.6f, + (s,cf,p,v) => { AvatarStepApproachFactor = cf.GetFloat(p, v); }, + (s) => { return AvatarStepApproachFactor; }, + (s,p,l,v) => { AvatarStepApproachFactor = v; } ), + new ParameterDefn("AvatarStepForceFactor", "Controls the amount of force up applied to step up onto a step", + 2.0f, + (s,cf,p,v) => { AvatarStepForceFactor = cf.GetFloat(p, v); }, + (s) => { return AvatarStepForceFactor; }, + (s,p,l,v) => { AvatarStepForceFactor = v; } ), new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", 0.95f, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 534f929055..e8575f60fe 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -79,6 +79,7 @@ public abstract class BSPhysObject : PhysicsActor Material = MaterialAttributes.Material.Wood; CollisionCollection = new CollisionEventUpdate(); + CollisionsLastTick = CollisionCollection; SubscribedEventsMs = 0; CollidingStep = 0; CollidingGroundStep = 0; @@ -159,6 +160,7 @@ public abstract class BSPhysObject : PhysicsActor public abstract OMV.Quaternion ForceOrientation { get; set; } // The system is telling us the velocity it wants to move at. + // Velocity in world coordinates. // protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor public override OMV.Vector3 TargetVelocity { @@ -169,6 +171,15 @@ public abstract class BSPhysObject : PhysicsActor Velocity = value; } } + public virtual float TargetSpeed + { + get + { + OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation)); + return characterOrientedVelocity.X; + } + } + public abstract OMV.Vector3 RawVelocity { get; set; } public abstract OMV.Vector3 ForceVelocity { get; set; } public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } @@ -177,6 +188,15 @@ public abstract class BSPhysObject : PhysicsActor public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; } + public virtual float ForwardSpeed + { + get + { + OMV.Vector3 characterOrientedVelocity = RawVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation)); + return characterOrientedVelocity.X; + } + } + #region Collisions // Requested number of milliseconds between collision events. Zero means disabled. @@ -223,9 +243,13 @@ public abstract class BSPhysObject : PhysicsActor // The collisions that have been collected this tick protected CollisionEventUpdate CollisionCollection; + // Remember collisions from last tick for fancy collision based actions + // (like a BSCharacter walking up stairs). + protected CollisionEventUpdate CollisionsLastTick; // The simulation step is telling this object about a collision. // Return 'true' if a collision was processed and should be sent up. + // Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision. // Called at taint time from within the Step() function public virtual bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) @@ -286,6 +310,9 @@ public abstract class BSPhysObject : PhysicsActor // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); base.SendCollisionUpdate(CollisionCollection); + // Remember the collisions from this tick for some collision specific processing. + CollisionsLastTick = CollisionCollection; + // The CollisionCollection instance is passed around in the simulator. // Make sure we don't have a handle to that one and that a new one is used for next time. // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 94b63e59ca..400d5d6b2e 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -562,7 +562,7 @@ public sealed class BSPrim : BSPhysObject } return; } - public OMV.Vector3 RawVelocity + public override OMV.Vector3 RawVelocity { get { return _velocity; } set { _velocity = value; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index 7b59e60e6e..794a6af5f6 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -88,6 +88,8 @@ setForce should set a constant force. Different than AddImpulse. Implement raycast. Implement ShapeCollection.Dispose() Implement water as a plain so raycasting and collisions can happen with same. +Add collision penetration return + Add field passed back by BulletSim.dll and fill with info in ManifoldConstact.GetDistance() Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE Also osGetPhysicsEngineVerion() maybe. Linkset.Position and Linkset.Orientation requre rewrite to properly return From 1603606f1d8bd62574cf98e051877d836cbeb012 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 7 Jan 2013 22:00:50 -0800 Subject: [PATCH 05/19] BulletSim: improve vehicle angular banking and deflection computation. Rotate angular correction forces to be world relative rather than vehicle relative. --- .../Physics/BulletSPlugin/BSDynamics.cs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index a9fbc8bb6f..eb695d956a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -1111,6 +1111,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin + deflectionContribution + bankingContribution; + // Add of the above computation are made relative to vehicle coordinates. + // Convert to world coordinates. + m_lastAngularVelocity *= VehicleOrientation; + // ================================================================== // Apply the correction velocity. // TODO: Should this be applied as an angular force (torque)? @@ -1222,14 +1226,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin public Vector3 ComputeAngularDeflection() { Vector3 ret = Vector3.Zero; - return ret; // DEBUG DEBUG DEBUG - // Disable angular deflection for the moment. + // Since angularMotorUp and angularDeflection are computed independently, they will calculate // approximately the same X or Y correction. When added together (when contributions are combined) // this creates an over-correction and then wabbling as the target is overshot. // TODO: rethink how the different correction computations inter-relate. - if (m_angularDeflectionEfficiency != 0) + if (m_angularDeflectionEfficiency != 0 && VehicleVelocity != Vector3.Zero) { // The direction the vehicle is moving Vector3 movingDirection = VehicleVelocity; @@ -1298,33 +1301,29 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) { - // This works by rotating a unit vector to the orientation of the vehicle. The - // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt - // up to one for full over). + // Rotate a UnitZ vector (pointing up) to how the vehicle is oriented. + // As the vehicle rolls to the right or left, the Y value will increase from + // zero (straight up) to 1 or -1 (full tilt right or left) Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation; - + // Figure out the yaw value for this much roll. - float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency; - // Keep the sign - if (rollComponents.Y < 0f) - turnComponent = -turnComponent; - - // TODO: there must be a better computation of the banking force. - float bankingTurnForce = turnComponent; + // Squared because that seems to give a good value + float yawAngle = (float)Math.Asin(rollComponents.Y * rollComponents.Y) * m_bankingEfficiency; // actual error = static turn error + dynamic turn error - float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed; + float mixedYawAngle = yawAngle * (1f - m_bankingMix) + yawAngle * m_bankingMix * VehicleForwardSpeed; + // TODO: the banking effect should not go to infinity but what to limit it to? - mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f); + mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f); // Build the force vector to change rotation from what it is to what it should be - ret.Z = -mixedBankingError; + ret.Z = -mixedYawAngle; // Don't do it all at once. ret /= m_bankingTimescale; - VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}", - Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret); + VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", + Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, ret); } return ret; } From 2ac96dd1813fa90907ef589b98a9bf68f0ab2e9f Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 8 Jan 2013 22:52:14 +0000 Subject: [PATCH 06/19] Add the new UpdateAgentInformation cap to make maturity on more recent viewers work. --- .../Linden/Caps/BunchOfCaps/BunchOfCaps.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index cc69645f4b..a534522b6b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -96,8 +96,8 @@ namespace OpenSim.Region.ClientStack.Linden // private static readonly string m_fetchInventoryPath = "0006/"; private static readonly string m_copyFromNotecardPath = "0007/"; // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. - - + private static readonly string m_UpdateAgentInformationPath = "0500/"; + // These are callbacks which will be setup by the scene so that we can update scene data when we // receive capability calls public NewInventoryItem AddNewInventoryItem = null; @@ -204,6 +204,8 @@ namespace OpenSim.Region.ClientStack.Linden m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); + IRequestHandler UpdateAgentInformationHandler = new RestStreamHandler("POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation); + m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler); m_HostCapsObj.RegisterHandler( "CopyInventoryFromNotecard", @@ -855,6 +857,22 @@ namespace OpenSim.Region.ClientStack.Linden response["int_response_code"] = 200; return LLSDHelpers.SerialiseLLSDReply(response); } + + public string UpdateAgentInformation(string request, string path, + string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); + OSDMap resp = new OSDMap(); + + OSDMap accessPrefs = new OSDMap(); + accessPrefs["max"] = "A"; + + resp["access_prefs"] = accessPrefs; + + string response = OSDParser.SerializeLLSDXmlString(resp); + return response; + } } public class AssetUploader From 5561333668f61f043cdfc0733a4eb50a1bcfb14e Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 8 Jan 2013 23:01:09 +0100 Subject: [PATCH 07/19] Prevent empty Anim Packs --- .../Framework/Scenes/Animation/AnimationSet.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index 65ae445950..d2fc7f1273 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -164,7 +164,13 @@ namespace OpenSim.Region.Framework.Scenes.Animation { int defaultSize = 0; if (m_defaultAnimation.AnimID != UUID.Zero) + { defaultSize++; + } + else if (m_animations.Count == 0) + { + defaultSize++; + } animIDs = new UUID[m_animations.Count + defaultSize]; sequenceNums = new int[m_animations.Count + defaultSize]; @@ -176,6 +182,12 @@ namespace OpenSim.Region.Framework.Scenes.Animation sequenceNums[0] = m_defaultAnimation.SequenceNum; objectIDs[0] = m_defaultAnimation.ObjectID; } + else if (m_animations.Count == 0) + { + animIDs[0] = m_implicitDefaultAnimation.AnimID; + sequenceNums[0] = m_defaultAnimation.SequenceNum; + objectIDs[0] = m_implicitDefaultAnimation.ObjectID; + } for (int i = 0; i < m_animations.Count; ++i) { From a775931a0cec9f65748c6e20dd2695edcbe21b7f Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 8 Jan 2013 23:24:34 +0100 Subject: [PATCH 08/19] Fix sequence id fr default anim --- OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index d2fc7f1273..64c31f88b2 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -185,7 +185,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation else if (m_animations.Count == 0) { animIDs[0] = m_implicitDefaultAnimation.AnimID; - sequenceNums[0] = m_defaultAnimation.SequenceNum; + sequenceNums[0] = m_implicitDefaultAnimation.SequenceNum; objectIDs[0] = m_implicitDefaultAnimation.ObjectID; } From 5fa4b8b1445829af66a63869672b76624d51a525 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 9 Jan 2013 00:01:48 +0000 Subject: [PATCH 09/19] minor: Allow "script *" console commands to take multiple script item ids --- .../Region/ScriptEngine/XEngine/XEngine.cs | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 79cec040a2..ba63bb6946 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -324,40 +324,40 @@ namespace OpenSim.Region.ScriptEngine.XEngine HandleShowStatus); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "scripts show", "scripts show []", "Show script information", + "Scripts", false, "scripts show", "scripts show [+]", "Show script information", "Show information on all scripts known to the script engine.\n" - + "If a is given then only information on that script will be shown.", + + "If one or more s are given then only information on that script will be shown.", HandleShowScripts); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "show scripts", "show scripts []", "Show script information", + "Scripts", false, "show scripts", "show scripts [+]", "Show script information", "Synonym for scripts show command", HandleShowScripts); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "scripts suspend", "scripts suspend []", "Suspends all running scripts", + "Scripts", false, "scripts suspend", "scripts suspend [+]", "Suspends all running scripts", "Suspends all currently running scripts. This only suspends event delivery, it will not suspend a" + " script that is currently processing an event.\n" + "Suspended scripts will continue to accumulate events but won't process them.\n" - + "If a is given then only that script will be suspended. Otherwise, all suitable scripts are suspended.", + + "If one or more s are given then only that script will be suspended. Otherwise, all suitable scripts are suspended.", (module, cmdparams) => HandleScriptsAction(cmdparams, HandleSuspendScript)); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "scripts resume", "scripts resume []", "Resumes all suspended scripts", + "Scripts", false, "scripts resume", "scripts resume [+]", "Resumes all suspended scripts", "Resumes all currently suspended scripts.\n" + "Resumed scripts will process all events accumulated whilst suspended.\n" - + "If a is given then only that script will be resumed. Otherwise, all suitable scripts are resumed.", + + "If one or more s are given then only that script will be resumed. Otherwise, all suitable scripts are resumed.", (module, cmdparams) => HandleScriptsAction(cmdparams, HandleResumeScript)); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "scripts stop", "scripts stop []", "Stops all running scripts", + "Scripts", false, "scripts stop", "scripts stop [+]", "Stops all running scripts", "Stops all running scripts.\n" - + "If a is given then only that script will be stopped. Otherwise, all suitable scripts are stopped.", + + "If one or more s are given then only that script will be stopped. Otherwise, all suitable scripts are stopped.", (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStopScript)); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "scripts start", "scripts start []", "Starts all stopped scripts", + "Scripts", false, "scripts start", "scripts start [+]", "Starts all stopped scripts", "Starts all stopped scripts.\n" - + "If a is given then only that script will be started. Otherwise, all suitable scripts are started.", + + "If one or more s are given then only that script will be started. Otherwise, all suitable scripts are started.", (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript)); MainConsole.Instance.Commands.AddCommand( @@ -478,29 +478,31 @@ namespace OpenSim.Region.ScriptEngine.XEngine return; } - rawItemId = cmdparams[2]; - - if (!UUID.TryParse(rawItemId, out itemId)) + for (int i = 2; i < cmdparams.Length; i++) { - MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid UUID", rawItemId); - return; - } - - if (itemId != UUID.Zero) - { - IScriptInstance instance = GetInstance(itemId); - if (instance == null) + rawItemId = cmdparams[i]; + + if (!UUID.TryParse(rawItemId, out itemId)) { - // Commented out for now since this will cause false reports on simulators with more than - // one scene where the current command line set region is 'root' (which causes commands to - // go to both regions... (sigh) -// MainConsole.Instance.OutputFormat("Error - No item found with id {0}", itemId); - return; + MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid UUID", rawItemId); + continue; } - else + + if (itemId != UUID.Zero) { - action(instance); - return; + IScriptInstance instance = GetInstance(itemId); + if (instance == null) + { + // Commented out for now since this will cause false reports on simulators with more than + // one scene where the current command line set region is 'root' (which causes commands to + // go to both regions... (sigh) + // MainConsole.Instance.OutputFormat("Error - No item found with id {0}", itemId); + continue; + } + else + { + action(instance); + } } } } From 3d5e3e35b799fe777066ac0cb27cf1bd3e104651 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 9 Jan 2013 00:08:08 +0000 Subject: [PATCH 10/19] minor: Fix command match of "debug script" command to "debug scripts" to match other scripts commands (and it's own short help text) --- OpenSim/Region/ScriptEngine/XEngine/XEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index ba63bb6946..4bbcb7c42d 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -361,7 +361,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript)); MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "debug script log", "debug scripts log ", "Extra debug logging for a script", + "Scripts", false, "debug scripts log", "debug scripts log ", "Extra debug logging for a script", "Activates or deactivates extra debug logging for the given script.\n" + "Level == 0, deactivate extra debug logging.\n" + "Level >= 1, log state changes.\n" From c1c540f454b0c9fd189ed768764fb8a51ee773c6 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 9 Jan 2013 00:20:14 +0000 Subject: [PATCH 11/19] Revert "Fix sequence id fr default anim" This reverts commit a775931a0cec9f65748c6e20dd2695edcbe21b7f. --- OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index 64c31f88b2..d2fc7f1273 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -185,7 +185,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation else if (m_animations.Count == 0) { animIDs[0] = m_implicitDefaultAnimation.AnimID; - sequenceNums[0] = m_implicitDefaultAnimation.SequenceNum; + sequenceNums[0] = m_defaultAnimation.SequenceNum; objectIDs[0] = m_implicitDefaultAnimation.ObjectID; } From f16c4a254c263a7fd75799fe79da6b3d0a1c80a2 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 9 Jan 2013 00:20:24 +0000 Subject: [PATCH 12/19] Revert "Prevent empty Anim Packs" This reverts commit 5561333668f61f043cdfc0733a4eb50a1bcfb14e. --- .../Framework/Scenes/Animation/AnimationSet.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index d2fc7f1273..65ae445950 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -164,13 +164,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation { int defaultSize = 0; if (m_defaultAnimation.AnimID != UUID.Zero) - { defaultSize++; - } - else if (m_animations.Count == 0) - { - defaultSize++; - } animIDs = new UUID[m_animations.Count + defaultSize]; sequenceNums = new int[m_animations.Count + defaultSize]; @@ -182,12 +176,6 @@ namespace OpenSim.Region.Framework.Scenes.Animation sequenceNums[0] = m_defaultAnimation.SequenceNum; objectIDs[0] = m_defaultAnimation.ObjectID; } - else if (m_animations.Count == 0) - { - animIDs[0] = m_implicitDefaultAnimation.AnimID; - sequenceNums[0] = m_defaultAnimation.SequenceNum; - objectIDs[0] = m_implicitDefaultAnimation.ObjectID; - } for (int i = 0; i < m_animations.Count; ++i) { From df1d7414adbf329e139201cdb7578c7b64bb50c8 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Tue, 8 Jan 2013 16:09:15 -0800 Subject: [PATCH 13/19] BulletSim: Fix hover height (boats float at the correct level). Fix problem of vehicles going crazy when backing up. --- .../Region/Physics/BulletSPlugin/BSDynamics.cs | 16 ++++++++++------ .../Physics/BulletSPlugin/BulletSimTODO.txt | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index eb695d956a..c34c05a90a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -856,6 +856,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // The movement computed in the linear motor is relative to the vehicle // coordinates. Rotate the movement to world coordinates. linearMotorContribution *= VehicleOrientation; + // All the contributions after this are world relative (mostly Z modifications) // ================================================================== // Buoyancy: force to overcome gravity. @@ -982,14 +983,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin float verticalCorrectionVelocity = verticalError / m_VhoverTimescale; // TODO: implement m_VhoverEfficiency correctly - if (Math.Abs(verticalError) > m_VhoverEfficiency) - { - ret = new Vector3(0f, 0f, verticalCorrectionVelocity); - } + ret = new Vector3(0f, 0f, verticalCorrectionVelocity); } - VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}", - Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight); + VDetailLog("{0}, MoveLinear,hover,pos={1},eff={2},hoverTS={3},height={4},target={5},ret={6}", + Prim.LocalID, VehiclePosition, m_VhoverEfficiency, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight, ret); } return ret; @@ -1238,6 +1236,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 movingDirection = VehicleVelocity; movingDirection.Normalize(); + // If the vehicle is going backward, it is still pointing forward + movingDirection *= Math.Sign(VehicleForwardSpeed); + // The direction the vehicle is pointing Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation; pointingDirection.Normalize(); @@ -1246,6 +1247,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 deflectionError = movingDirection - pointingDirection; // Don't try to correct very large errors (not our job) + // if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = PIOverTwo * Math.Sign(deflectionError.X); + // if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = PIOverTwo * Math.Sign(deflectionError.Y); + // if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = PIOverTwo * Math.Sign(deflectionError.Z); if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f; if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f; if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index 794a6af5f6..29bd4e4b80 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -1,9 +1,9 @@ CURRENT PRIORITIES ================================================= -Avatars walking up stairs +Avatars walking up stairs (HALF DONE) Vehicle movement on terrain smoothness limitMotorUp calibration (more down?) -Preferred orientatino angular correction fix +Preferred orientation angular correction fix Surfboard go wonky when turning Angular motor direction is global coordinates rather than local coordinates? Boats float low in the water From c28a2f05caae50590e64e61d8b7358ee92a7fca3 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 9 Jan 2013 00:54:28 +0000 Subject: [PATCH 14/19] minor: make spacing consistent in console help output --- OpenSim/Framework/Console/CommandConsole.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs index de30414cb3..f9c9307ed3 100644 --- a/OpenSim/Framework/Console/CommandConsole.cs +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -110,10 +110,11 @@ namespace OpenSim.Framework.Console // Remove initial help keyword helpParts.RemoveAt(0); + help.Add(""); // Will become a newline. + // General help if (helpParts.Count == 0) { - help.Add(""); // Will become a newline. help.Add(GeneralHelpText); help.Add(ItemHelpText); help.AddRange(CollectModulesHelp(tree)); @@ -127,6 +128,8 @@ namespace OpenSim.Framework.Console help.AddRange(CollectHelp(helpParts)); } + help.Add(""); // Will become a newline. + return help; } @@ -200,8 +203,8 @@ namespace OpenSim.Framework.Console help.Add(commandInfo.descriptive_help); - if (descriptiveHelp != string.Empty) - help.Add(string.Empty); +// if (descriptiveHelp != string.Empty) +// help.Add(string.Empty); } else { From 290dc274ec0860e96f0c19479c8da512779cdab3 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 9 Jan 2013 01:04:43 +0000 Subject: [PATCH 15/19] minor: Remove unnecessary commented out code from last commit c28a2f05 and fix up code comment --- OpenSim/Framework/Console/CommandConsole.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs index f9c9307ed3..b9f402ad4d 100644 --- a/OpenSim/Framework/Console/CommandConsole.cs +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -197,14 +197,11 @@ namespace OpenSim.Framework.Console string descriptiveHelp = commandInfo.descriptive_help; - // If we do have some descriptive help then insert a spacing line before and after for readability. + // If we do have some descriptive help then insert a spacing line before for readability. if (descriptiveHelp != string.Empty) help.Add(string.Empty); help.Add(commandInfo.descriptive_help); - -// if (descriptiveHelp != string.Empty) -// help.Add(string.Empty); } else { From a0000a034f3d193662d56a1c8147771b0d994b23 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 10 Jan 2013 00:32:22 +0000 Subject: [PATCH 16/19] Add "show sensors" command to show script sensor information for debug purposes. --- .../Implementation/Plugins/SensorRepeat.cs | 94 ++++++++++++------- .../XEngine/ScriptEngineConsoleCommands.cs | 86 +++++++++++++++++ .../Region/ScriptEngine/XEngine/XEngine.cs | 5 + 3 files changed, 153 insertions(+), 32 deletions(-) create mode 100644 OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index 24cceeaaac..37422d73fe 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -42,6 +42,41 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + ///

+ /// Used by one-off and repeated sensors + /// + public class SensorInfo + { + public uint localID; + public UUID itemID; + public double interval; + public DateTime next; + + public string name; + public UUID keyID; + public int type; + public double range; + public double arc; + public SceneObjectPart host; + + public SensorInfo Clone() + { + SensorInfo s = new SensorInfo(); + s.localID = localID; + s.itemID = itemID; + s.interval = interval; + s.next = next; + s.name = name; + s.keyID = keyID; + s.type = type; + s.range = range; + s.arc = arc; + s.host = host; + + return s; + } + } + public AsyncCommandManager m_CmdManager; /// @@ -78,24 +113,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins private double maximumRange = 96.0; private int maximumToReturn = 16; - // - // SenseRepeater and Sensors - // - private class SenseRepeatClass - { - public uint localID; - public UUID itemID; - public double interval; - public DateTime next; - - public string name; - public UUID keyID; - public int type; - public double range; - public double arc; - public SceneObjectPart host; - } - // // Sensed entity // @@ -128,7 +145,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins /// /// Always lock SenseRepeatListLock when updating this list. /// - private List SenseRepeaters = new List(); + private List SenseRepeaters = new List(); private object SenseRepeatListLock = new object(); public void SetSenseRepeatEvent(uint m_localID, UUID m_itemID, @@ -142,7 +159,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins return; // Add to timer - SenseRepeatClass ts = new SenseRepeatClass(); + SensorInfo ts = new SensorInfo(); ts.localID = m_localID; ts.itemID = m_itemID; ts.interval = sec; @@ -161,11 +178,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins AddSenseRepeater(ts); } - private void AddSenseRepeater(SenseRepeatClass senseRepeater) + private void AddSenseRepeater(SensorInfo senseRepeater) { lock (SenseRepeatListLock) { - List newSenseRepeaters = new List(SenseRepeaters); + List newSenseRepeaters = new List(SenseRepeaters); newSenseRepeaters.Add(senseRepeater); SenseRepeaters = newSenseRepeaters; } @@ -176,8 +193,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins // Remove from timer lock (SenseRepeatListLock) { - List newSenseRepeaters = new List(); - foreach (SenseRepeatClass ts in SenseRepeaters) + List newSenseRepeaters = new List(); + foreach (SensorInfo ts in SenseRepeaters) { if (ts.localID != m_localID || ts.itemID != m_itemID) { @@ -192,7 +209,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins public void CheckSenseRepeaterEvents() { // Go through all timers - foreach (SenseRepeatClass ts in SenseRepeaters) + foreach (SensorInfo ts in SenseRepeaters) { // Time has passed? if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) @@ -209,7 +226,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins double range, double arc, SceneObjectPart host) { // Add to timer - SenseRepeatClass ts = new SenseRepeatClass(); + SensorInfo ts = new SensorInfo(); ts.localID = m_localID; ts.itemID = m_itemID; ts.interval = 0; @@ -225,7 +242,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins SensorSweep(ts); } - private void SensorSweep(SenseRepeatClass ts) + private void SensorSweep(SensorInfo ts) { if (ts.host == null) { @@ -301,7 +318,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins } } - private List doObjectSensor(SenseRepeatClass ts) + private List doObjectSensor(SensorInfo ts) { List Entities; List sensedEntities = new List(); @@ -450,7 +467,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins return sensedEntities; } - private List doAgentSensor(SenseRepeatClass ts) + private List doAgentSensor(SensorInfo ts) { List sensedEntities = new List(); @@ -626,7 +643,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { List data = new List(); - foreach (SenseRepeatClass ts in SenseRepeaters) + foreach (SensorInfo ts in SenseRepeaters) { if (ts.itemID == itemID) { @@ -656,7 +673,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins while (idx < data.Length) { - SenseRepeatClass ts = new SenseRepeatClass(); + SensorInfo ts = new SensorInfo(); ts.localID = localID; ts.itemID = itemID; @@ -677,5 +694,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins idx += 6; } } + + public List GetSensorInfo() + { + List retList = new List(); + + lock (SenseRepeatListLock) + { + foreach (SensorInfo si in SenseRepeaters) + retList.Add(si.Clone()); + } + + return retList; + } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs b/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs new file mode 100644 index 0000000000..e47917da96 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs @@ -0,0 +1,86 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Region.ScriptEngine.Interfaces; +using OpenSim.Region.ScriptEngine.Shared.Api; +using OpenSim.Region.ScriptEngine.Shared.Api.Plugins; + +namespace OpenSim.Region.ScriptEngine.XEngine +{ + public class ScriptEngineConsoleCommands + { + IScriptEngine m_engine; + + public ScriptEngineConsoleCommands(IScriptEngine engine) + { + m_engine = engine; + } + + public void RegisterCommands() + { + MainConsole.Instance.Commands.AddCommand( + "Scripts", false, "show sensors", "show sensors", "Show script sensors information", + HandleShowSensors); + } + + private void HandleShowSensors(string module, string[] cmdparams) + { + if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_engine.World)) + return; + + SensorRepeat sr = AsyncCommandManager.GetSensorRepeatPlugin(m_engine); + + if (sr == null) + { + MainConsole.Instance.Output("Sensor plugin not yet initialized"); + return; + } + + List sensorInfo = sr.GetSensorInfo(); + + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.AddColumn("Part name", 40); + cdt.AddColumn("Script item ID", 36); + cdt.AddColumn("Type", 4); + cdt.AddColumn("Interval", 8); + cdt.AddColumn("Range", 8); + cdt.AddColumn("Arc", 8); + + foreach (SensorRepeat.SensorInfo s in sensorInfo) + { + cdt.AddRow(s.host.Name, s.itemID, s.type, s.interval, s.range, s.arc); + } + + MainConsole.Instance.Output(cdt.ToString()); + MainConsole.Instance.OutputFormat("Total: {0}", sensorInfo.Count); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 4bbcb7c42d..8c3bb5b06b 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -169,6 +169,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine IWorkItemResult m_CurrentCompile = null; private Dictionary m_CompileDict = new Dictionary(); + private ScriptEngineConsoleCommands m_consoleCommands; + public string ScriptEngineName { get { return "XEngine"; } @@ -318,6 +320,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine OnObjectRemoved += m_XmlRpcRouter.ObjectRemoved; } + m_consoleCommands = new ScriptEngineConsoleCommands(this); + m_consoleCommands.RegisterCommands(); + MainConsole.Instance.Commands.AddCommand( "Scripts", false, "xengine status", "xengine status", "Show status information", "Show status information on the script engine.", From b1b46872500476cf97b5de8c16012b8545fed0c7 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 10 Jan 2013 00:57:49 +0000 Subject: [PATCH 17/19] Add "show script timers" command to show script timers. For debug purposes. Also, "show sensors" changes to "show script sensors". --- .../Framework/Monitoring/MemoryWatchdog.cs | 2 +- .../Implementation/Plugins/SensorRepeat.cs | 18 ++----- .../Api/Implementation/Plugins/Timer.cs | 52 +++++++++++++------ .../XEngine/ScriptEngineConsoleCommands.cs | 46 ++++++++++++++-- 4 files changed, 82 insertions(+), 36 deletions(-) diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs index c6010cd092..bc5ed973e7 100644 --- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -72,7 +72,7 @@ namespace OpenSim.Framework.Monitoring /// public static double LastMemoryChurn { - get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } + get { if (m_samples.Count > 0) return m_samples.First(); else return 0; } } /// diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index 37422d73fe..dd45406a92 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -61,19 +61,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins public SensorInfo Clone() { - SensorInfo s = new SensorInfo(); - s.localID = localID; - s.itemID = itemID; - s.interval = interval; - s.next = next; - s.name = name; - s.keyID = keyID; - s.type = type; - s.range = range; - s.arc = arc; - s.host = host; - - return s; + return (SensorInfo)this.MemberwiseClone(); } } @@ -701,8 +689,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins lock (SenseRepeatListLock) { - foreach (SensorInfo si in SenseRepeaters) - retList.Add(si.Clone()); + foreach (SensorInfo i in SenseRepeaters) + retList.Add(i.Clone()); } return retList; diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/Timer.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/Timer.cs index bc63030ec2..0b14565d04 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/Timer.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/Timer.cs @@ -35,6 +35,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { public class Timer { + public class TimerInfo + { + public uint localID; + public UUID itemID; + //public double interval; + public long interval; + //public DateTime next; + public long next; + + public TimerInfo Clone() + { + return (TimerInfo)this.MemberwiseClone(); + } + } + public AsyncCommandManager m_CmdManager; public int TimersCount @@ -59,17 +74,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins return localID.ToString() + itemID.ToString(); } - private class TimerClass - { - public uint localID; - public UUID itemID; - //public double interval; - public long interval; - //public DateTime next; - public long next; - } - - private Dictionary Timers = new Dictionary(); + private Dictionary Timers = new Dictionary(); private object TimerListLock = new object(); public void SetTimerEvent(uint m_localID, UUID m_itemID, double sec) @@ -81,7 +86,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins } // Add to timer - TimerClass ts = new TimerClass(); + TimerInfo ts = new TimerInfo(); ts.localID = m_localID; ts.itemID = m_itemID; ts.interval = Convert.ToInt64(sec * 10000000); // How many 100 nanoseconds (ticks) should we wait @@ -121,8 +126,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins lock (TimerListLock) { // Go through all timers - Dictionary.ValueCollection tvals = Timers.Values; - foreach (TimerClass ts in tvals) + Dictionary.ValueCollection tvals = Timers.Values; + foreach (TimerInfo ts in tvals) { // Time has passed? if (ts.next < DateTime.Now.Ticks) @@ -147,8 +152,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins lock (TimerListLock) { - Dictionary.ValueCollection tvals = Timers.Values; - foreach (TimerClass ts in tvals) + Dictionary.ValueCollection tvals = Timers.Values; + foreach (TimerInfo ts in tvals) { if (ts.itemID == itemID) { @@ -167,7 +172,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins while (idx < data.Length) { - TimerClass ts = new TimerClass(); + TimerInfo ts = new TimerInfo(); ts.localID = localID; ts.itemID = itemID; @@ -181,5 +186,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins } } } + + public List GetTimersInfo() + { + List retList = new List(); + + lock (TimerListLock) + { + foreach (TimerInfo i in Timers.Values) + retList.Add(i.Clone()); + } + + return retList; + } } } diff --git a/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs b/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs index e47917da96..efb854d5cb 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/ScriptEngineConsoleCommands.cs @@ -47,20 +47,29 @@ namespace OpenSim.Region.ScriptEngine.XEngine public void RegisterCommands() { MainConsole.Instance.Commands.AddCommand( - "Scripts", false, "show sensors", "show sensors", "Show script sensors information", + "Scripts", false, "show script sensors", "show script sensors", "Show script sensors information", HandleShowSensors); + + MainConsole.Instance.Commands.AddCommand( + "Scripts", false, "show script timers", "show script timers", "Show script sensors information", + HandleShowTimers); + } + + private bool IsSceneSelected() + { + return MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_engine.World; } private void HandleShowSensors(string module, string[] cmdparams) { - if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_engine.World)) + if (!IsSceneSelected()) return; SensorRepeat sr = AsyncCommandManager.GetSensorRepeatPlugin(m_engine); if (sr == null) { - MainConsole.Instance.Output("Sensor plugin not yet initialized"); + MainConsole.Instance.Output("Plugin not yet initialized"); return; } @@ -82,5 +91,36 @@ namespace OpenSim.Region.ScriptEngine.XEngine MainConsole.Instance.Output(cdt.ToString()); MainConsole.Instance.OutputFormat("Total: {0}", sensorInfo.Count); } + + private void HandleShowTimers(string module, string[] cmdparams) + { + if (!IsSceneSelected()) + return; + + Timer timerPlugin = AsyncCommandManager.GetTimerPlugin(m_engine); + + if (timerPlugin == null) + { + MainConsole.Instance.Output("Plugin not yet initialized"); + return; + } + + List timersInfo = timerPlugin.GetTimersInfo(); + + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.AddColumn("Part local ID", 13); + cdt.AddColumn("Script item ID", 36); + cdt.AddColumn("Interval", 10); + cdt.AddColumn("Next", 8); + + foreach (Timer.TimerInfo t in timersInfo) + { + // Convert from 100 ns ticks back to seconds + cdt.AddRow(t.localID, t.itemID, (double)t.interval / 10000000, t.next); + } + + MainConsole.Instance.Output(cdt.ToString()); + MainConsole.Instance.OutputFormat("Total: {0}", timersInfo.Count); + } } } \ No newline at end of file From 84407e322f6469a3001b390f7c516f4eabaad1e6 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 10 Jan 2013 01:30:00 +0000 Subject: [PATCH 18/19] revert accidental change to MemoryWatchdog stat calculation in previous b1b4687 --- OpenSim/Framework/Monitoring/MemoryWatchdog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs index bc5ed973e7..c6010cd092 100644 --- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -72,7 +72,7 @@ namespace OpenSim.Framework.Monitoring /// public static double LastMemoryChurn { - get { if (m_samples.Count > 0) return m_samples.First(); else return 0; } + get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } } /// From 170d37696795d70799fb9b402400133088f389e9 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 10 Jan 2013 01:45:56 +0000 Subject: [PATCH 19/19] On baked texture save, replace any HG ID with an ordinary asset ID so the HGAssetBroker doesn't try to save back to the avatar's originating region --- .../CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 0a69979d34..ce79f07172 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -287,6 +287,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory if (asset != null) { + // Replace an HG ID with the simple asset ID so that we can persist textures for foreign HG avatars + asset.ID = asset.FullID.ToString(); + asset.Temporary = false; asset.Local = false; m_scene.AssetService.Store(asset);