From 181a0b8afcf8d9a445054f33976877f433fa594c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 21 Oct 2012 21:11:52 -0700 Subject: [PATCH] New Hardware RET6 code for more channels, added particle elements, physics. --- .DS_Store | Bin 6148 -> 6148 bytes balance_maple/balance_maple.pde | 96 -------- field.cpp | 93 +++++-- field.h | 9 + hand.cpp | 10 +- hand.h | 11 +- hardware/.DS_Store | Bin 0 -> 6148 bytes hardware/head_hand/head_hand.pde | 44 ++++ head.cpp | 1 + head.h | 4 + interface.xcodeproj/project.pbxproj | 10 +- .../UserInterfaceState.xcuserstate | Bin 97315 -> 97488 bytes .../xcdebugger/Breakpoints.xcbkptlist | 19 +- main.cpp | 97 ++++---- particle.cpp | 232 +++++++++++++++--- particle.h | 44 +++- util.cpp | 61 +++-- util.h | 4 + world.h | 2 - 19 files changed, 483 insertions(+), 254 deletions(-) delete mode 100644 balance_maple/balance_maple.pde create mode 100644 hardware/.DS_Store create mode 100644 hardware/head_hand/head_hand.pde diff --git a/.DS_Store b/.DS_Store index 70c620c8f2a7d05ffd84484e998eb30841822429..4be1b06fbb26033d1cf4bc63c6f2429a4765bf0e 100644 GIT binary patch delta 81 zcmZoMXfc=&$(fQ=UR;orlb^KtBJ)Z{M)t|ZOkV{#7%~_V8HyNE7|OwP>STXr 0) - { - pingRcvd = 1; - readBuffer[0] = SerialUSB.read(); - } - if (pingRcvd == 1) - { - SerialUSB.println("pong"); - toggleLED(); - } -} - - - diff --git a/field.cpp b/field.cpp index 9abd2b9feb..a1b9627ef2 100644 --- a/field.cpp +++ b/field.cpp @@ -6,21 +6,13 @@ // Copyright (c) 2012 __MyCompanyName__. All rights reserved. // -#ifdef __APPLE__ -#include -#else -#include -#endif -#include #include "field.h" -#include "world.h" +#define FIELD_SCALE 0.00050 // A vector-valued field over an array of elements arranged as a 3D lattice struct { - float x; - float y; - float z; + glm::vec3 val; } field[FIELD_ELEMENTS]; @@ -33,24 +25,24 @@ int field_value(float *value, float *pos) (int)(pos[2]/WORLD_SIZE*10.0)*100; if ((index >= 0) && (index < FIELD_ELEMENTS)) { - value[0] = field[index].x; - value[1] = field[index].y; - value[2] = field[index].z; + value[0] = field[index].val.x; + value[1] = field[index].val.y; + value[2] = field[index].val.z; return 1; } else return 0; } + void field_init() // Initializes the field to some random values { int i; - const float FIELD_SCALE = 0.00050; for (i = 0; i < FIELD_ELEMENTS; i++) { - field[i].x = (randFloat() - 0.5)*FIELD_SCALE; - field[i].y = (randFloat() - 0.5)*FIELD_SCALE; - field[i].z = (randFloat() - 0.5)*FIELD_SCALE; + field[i].val.x = (randFloat() - 0.5)*FIELD_SCALE; + field[i].val.y = (randFloat() - 0.5)*FIELD_SCALE; + field[i].val.z = (randFloat() - 0.5)*FIELD_SCALE; } } @@ -62,12 +54,66 @@ void field_add(float* add, float *pos) (int)(pos[2]/WORLD_SIZE*10.0)*100; if ((index >= 0) && (index < FIELD_ELEMENTS)) { - field[index].x += add[0]; - field[index].y += add[0]; - field[index].z += add[0]; + field[index].val.x += add[0]; + field[index].val.y += add[0]; + field[index].val.z += add[0]; } } +void field_avg_neighbors(int index, glm::vec3 * result) { + // Given index to field element i, return neighbor field values + glm::vec3 neighbors(0,0,0); + + int x,y,z; + x = (int)(index % 10); + y = (int)(index%100 / 10); + z = (int)(index / 100); + + neighbors += field[(x+1)%10 + y*10 + z*100].val; + neighbors += field[(x-1)%10 + y*10 + z*100].val; + + neighbors += field[x + ((y+1)%10)*10 + z*100].val; + neighbors += field[x + ((y-1)%10)*10 + z*100].val; + + neighbors += field[x + y*10 + ((z+1)%10)*100].val; + neighbors += field[x%10 + y*10 + ((z-1)%10)*100].val; + + neighbors /= 6; + result->x = neighbors.x; + result->y = neighbors.y; + result->z = neighbors.z; + +} + +void field_simulate(float dt) { + glm::vec3 neighbors, add; + float size; + for (int i = 0; i < FIELD_ELEMENTS; i++) + { + if (0) { //(randFloat() > 0.01) { + field_avg_neighbors(i, &neighbors); + size = powf(field[i].val.x*field[i].val.x + + field[i].val.y*field[i].val.y + + field[i].val.z*field[i].val.z, 0.5); + + neighbors *= 0.0001; + glm::vec3 test = glm::normalize(glm::vec3(0,0,0)); + + field[i].val = glm::normalize(field[i].val); + field[i].val *= size * 0.99; + add = glm::normalize(neighbors); + add *= size * 0.01; + field[i].val += add; + } + else { + field[i].val.x += (randFloat() - 0.5)*0.01*FIELD_SCALE; + field[i].val.y += (randFloat() - 0.5)*0.01*FIELD_SCALE; + field[i].val.z += (randFloat() - 0.5)*0.01*FIELD_SCALE; + } + + } +} + void field_render() // Render the field lines { @@ -75,6 +121,7 @@ void field_render() float fx, fy, fz; float scale_view = 1000.0; + glDisable(GL_LIGHTING); glColor3f(0, 1, 0); glBegin(GL_LINES); for (i = 0; i < FIELD_ELEMENTS; i++) @@ -84,9 +131,9 @@ void field_render() fz = (int)(i / 100); glVertex3f(fx, fy, fz); - glVertex3f(fx + field[i].x*scale_view, - fy + field[i].y*scale_view, - fz + field[i].z*scale_view); + glVertex3f(fx + field[i].val.x*scale_view, + fy + field[i].val.y*scale_view, + fz + field[i].val.z*scale_view); } glEnd(); diff --git a/field.h b/field.h index 60ec6e747a..ca62f91cec 100644 --- a/field.h +++ b/field.h @@ -9,6 +9,14 @@ #ifndef interface_field_h #define interface_field_h +#ifdef __APPLE__ +#include +#else +#include +#endif +#include +#include "world.h" +#include "util.h" #include "glm/glm.hpp" // Field is a lattice of vectors uniformly distributed FIELD_ELEMENTS^(1/3) on side @@ -19,6 +27,7 @@ void field_init(); int field_value(float *ret, float *pos); void field_render(); void field_add(float* add, float *loc); +void field_simulate(float dt); class Field { public: diff --git a/hand.cpp b/hand.cpp index 7e9a929b7e..4d7502c5b3 100644 --- a/hand.cpp +++ b/hand.cpp @@ -12,8 +12,10 @@ const float DEFAULT_X = 0.0; const float DEFAULT_Y = 0.0; const float DEFAULT_Z = -7.0; -Hand::Hand() +Hand::Hand(float initradius, glm::vec3 initcolor) { + color = initcolor; + radius = initradius; reset(); noise = 0; } @@ -23,7 +25,8 @@ void Hand::render() glEnable(GL_DEPTH_TEST); glPushMatrix(); glLoadIdentity(); - glColor3f(0.5, 0.5, 0.5); + if (isColliding) glColor3f(1,0,0); + else glColor3f(color.x, color.y, color.z); glBegin(GL_LINES); glVertex3f(-0.05, -0.5, 0.0); glVertex3f(position.x, position.y, position.z); @@ -31,7 +34,7 @@ void Hand::render() glVertex3f(position.x, position.y, position.z); glEnd(); glTranslatef(position.x, position.y, position.z); - glutSolidSphere(0.2, 15, 15); + glutSolidSphere(radius, 15, 15); glPopMatrix(); } @@ -41,6 +44,7 @@ void Hand::reset() position.y = DEFAULT_Y; position.z = DEFAULT_Z; velocity.x = velocity.y = velocity.z = 0; + isColliding = false; } void Hand::simulate(float deltaTime) diff --git a/hand.h b/hand.h index c90877bfeb..b1edf7b39c 100644 --- a/hand.h +++ b/hand.h @@ -20,15 +20,22 @@ const float RADIUS_RANGE = 10.0; class Hand { public: - Hand(void); + Hand(float initradius, glm::vec3 color); void simulate (float deltaTime); void render (); void reset (); void setNoise (float mag) { noise = mag; }; void addVel (glm::vec3 add) { velocity += add; }; + glm::vec3 getPos() { return position; }; + void setPos(glm::vec3 newpos) { position = newpos; }; + float getRadius() { return radius; }; + void setRadius(float newradius) { radius = newradius; }; + void setColliding(bool newcollide) { isColliding = newcollide; }; private: - glm::vec3 position, velocity; + glm::vec3 position, velocity, color; float noise; + float radius; + bool isColliding; }; diff --git a/hardware/.DS_Store b/hardware/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..83544c91383fcf8bf40f68c6a546ef0c03ef8580 GIT binary patch literal 6148 zcmeI0F>b;@5Jmq77eq8JQ*wpeAVP2g4gds4a$$ucN}q~zaWwRI#uCD&qd@3CX*Krj z{9U=(*>3<`{xu&!4Pb_dIAIjcYYdoVhJZ#5Y|&zmA2iMHpn4o>-_YV4do$>utnKX6TB%xj?YVG(zGd}jQQSdI=NEC7M3|Kjig#SciTcS#i`1@%;7 zxCf?`X)#zViRy|R3^^EbFb^%J`C#tMbSRXgkv`;L+1(g(%QO literal 0 HcmV?d00001 diff --git a/hardware/head_hand/head_hand.pde b/hardware/head_hand/head_hand.pde new file mode 100644 index 0000000000..3719d5ba45 --- /dev/null +++ b/hardware/head_hand/head_hand.pde @@ -0,0 +1,44 @@ +/* +Read a set of analog input lines and echo their readings over the serial port with averaging +*/ + +#define NUM_CHANNELS 8 +#define AVERAGE_COUNT 100 + +int inputPins[NUM_CHANNELS] = {0,1,2,3,4,10,11,12}; + +int measured[NUM_CHANNELS]; +float accumulate[NUM_CHANNELS]; + +int sampleCount = 0; + +void setup() +{ + int i; + for (i = 0; i < NUM_CHANNELS; i++) { + pinMode(inputPins[i], INPUT_ANALOG); + measured[i] = analogRead(inputPins[i]); + accumulate[i] = measured[i]; + } + pinMode(BOARD_LED_PIN, OUTPUT); +} + +void loop() +{ + int i; + sampleCount++; + for (i = 0; i < NUM_CHANNELS; i++) { + if (sampleCount % NUM_SAMPLES == 0) { + measured[i] = accumulate[i] / AVERAGE_COUNT; + SerialUSB.print(measured[i]); + SerialUSB.print(" "); + accumulate[i] = 0; + } else { + accumulate[i] += analogRead(inputPins[i]); + } + } + if (sampleCount % NUM_SAMPLES == 0) SerialUSB.println(""); +} + + + diff --git a/head.cpp b/head.cpp index 9d429ddd3a..bf1478db02 100644 --- a/head.cpp +++ b/head.cpp @@ -48,6 +48,7 @@ Head::Head() void Head::reset() { + position = glm::vec3(0,0,0); Pitch = 0; Yaw = 0; } diff --git a/head.h b/head.h index f1318105d7..eef04f2eb3 100644 --- a/head.h +++ b/head.h @@ -42,6 +42,8 @@ class Head { float PupilConverge; + glm::vec3 position; + public: Head(void); void reset(); @@ -56,6 +58,8 @@ public: int transmit(char*); void receive(float); void SetNewHeadTarget(float, float); + glm::vec3 getPos() { return position; }; + void setPos(glm::vec3 newpos) { position = newpos; }; }; #endif \ No newline at end of file diff --git a/interface.xcodeproj/project.pbxproj b/interface.xcodeproj/project.pbxproj index c61e0141b8..5b93949fa3 100644 --- a/interface.xcodeproj/project.pbxproj +++ b/interface.xcodeproj/project.pbxproj @@ -108,17 +108,17 @@ isa = PBXGroup; children = ( 08FB7796FE84155DC02AAC07 /* main.cpp */, - B6BDAE4115F6BE4D002A07DF /* particle.h */, - B6BDAE4315F6BE53002A07DF /* particle.cpp */, D4EE3BC015E746E900EE4C89 /* world.h */, - D4EE3BC415EBD90C00EE4C89 /* network.h */, - D4EE3BC515EBD93400EE4C89 /* network.cpp */, + B6BDAE4315F6BE53002A07DF /* particle.cpp */, + B6BDAE4115F6BE4D002A07DF /* particle.h */, D4EE3BBD15E7465700EE4C89 /* field.cpp */, + D4EE3BBF15E7467600EE4C89 /* field.h */, D4B96D4715FF966200CE6E8B /* head.h */, D4B96D4815FF967C00CE6E8B /* head.cpp */, D4EFE3CE162A2D7300DC5C59 /* hand.h */, D4EFE3CF162A2DA000DC5C59 /* hand.cpp */, - D4EE3BBF15E7467600EE4C89 /* field.h */, + D4EE3BC515EBD93400EE4C89 /* network.cpp */, + D4EE3BC415EBD90C00EE4C89 /* network.h */, D4EE3BBA15E45FFE00EE4C89 /* SerialInterface.h */, D4EE3BBB15E45FFE00EE4C89 /* SerialInterface.cpp */, D4EE3BC115E761B000EE4C89 /* util.cpp */, diff --git a/interface.xcodeproj/project.xcworkspace/xcuserdata/philip.xcuserdatad/UserInterfaceState.xcuserstate b/interface.xcodeproj/project.xcworkspace/xcuserdata/philip.xcuserdatad/UserInterfaceState.xcuserstate index 629b5a34d2792b80efecfbb525358cdaba69c600..8cfc080bbe9d158bbf36ff22d920cac4afd027ae 100644 GIT binary patch delta 35054 zcmZ^p30PF+_s2g2_f9S_cOOtdcGLl67ZBXVea9707;!{q5M0YLn|c53yEPTiMaUJoldSJ>U0x&b!_h*Z(Z<{6${lj9%lsDwzL! zmUORXPrqvT`h+A~sx8BoX)Cmq*vf1bwn4U`wh^{bwz0MeL7Q#0P21|X#YWlG;N{Un zZ8vXyGkSBh|4&D>!|v$jh;?WV(-H4Ta3nj@99fROj$B8+qu5dEsBjE&40BXD#yG}1 zCOfJfGaWUKxem9(<5=iuakM#>IZk(+A2c)o#O__EsnL04UUbD z&5nB<4>%rjJmz@PvCZ+E<0Z!q#~Y4aj=hfkjst$j`;LztpEa7OF++V0DN(R2`-cS4XH->PU5} zTCGk~r>is6nK9}tb-wCR7pP6@vfyuBru$>Gc&&%lQ%lf#Ykjo7T8Y+QE7i)h5n7ct zQX8dJYtyvpnp>NvHEN5rCE9XrrM61*YyYQRq+P6Ct_8Jgv}?5+wY6G@cDr_$wpqJd zdqCT&J)%9SJ*7RZy`k;a_GtUG{n`QTp!T8mk@lH(So=!*TKi5ruKncK{?sMCiyo^R zx~a$Oz4TPQx9-yO^FUo^Nfp(i;c^T zD~zj+ps~@|WZY@oWo$O?Hn#YUdyIRH=Zxo#7mOE;myDN?zGr@Heqw%VeqkOpzc+t3 zPndsLcIz~&i{-EkE5S;%lB`TC%Nk%+Se4d5YmhbA8e$E#hFR0B>DCNurZvl&ZPoa# zT5GQ5ww77Ttrgbk)*04H>rCq`Yn645^?%lR*2UH()>^B>T4$}dHdwb?cUT*(P1c>( zUDg)se(OQ&5$kd5DeEQcW$P7dhxN9#&)RRjW4&vAV0~zPYJFyXZGG#Eaz;BPr|h&l zPjhy0c6D}hs!q)r=Ztsua+YQ|Go9Ja0_Q;IAm?Cbm2-@<+BwZR-8sWq=WKAgolVX~ z&K1tnoo6`x&VW-mFLqwyywrJF@XVN@wxPi%Vy^2^Eu@cx^r?`(6mnM~cN22Hh(02s zk8JhEPP0k33F)z|&pICJ5~~I#2c`t71J!|Pfyu$7-ftzSw%v54JKJr^fr<`WN?>Z} zeF06j^kBid#K9^j{k;PfciH;b`r2H!Y+H^k*VfOLXUn%01f~aO1ZDjIyGfQE^f6Q9Yu1MkNGp4cr!38|VnE3#<=p2;Bac%&1;r zVNt1JVNn@jVNt!q!lGQkZIx#5NLq?5H@NGeF;P+Z!J71JTT$@Vxk+2^O`j1RPgc~m zVM*89)IfFMm9V58fyu#Ly;FiCo1WPEbMG^XN7__-mk`R{JtSP%)`wuiwjnV2FU5AH zQ@vdeRNQ4Z?55qaJMD4yczX|fPkVwrF?e@fLX5E8^Otgaa&Tu|nk_9{UwdZoLwA;~ zPdE#Ec5um@-nM?>Be`EgyXK9vmHw-^_5tBcUYffkYLQK~UlZ2Y9?|%GMB@vc z8gKfC##=)gZ?mtpci7k2*V{MPZ@1rJ-)P??Y%dDiOTzZDu)QK|JB003VS7#3UKh4E zgze3f$~T9V-xE=Oe^~j05#^7Bl|LR){!~Qywy^T&{;mAQu<|vHXGiz6si*Y`DJSc+ zoKSU-hdZHK*uD=Y^{#{6tkh%z| zyO0zi8A6H^Qi70@g_JI&Y#|j1sX|DDg*049gmj6Jt`O2yLb_H+ zHwfwGJ#?i3H}f1e<_CI;=$ixKNw|O}ha5b6*C?Y)#C~h1Jtny8t-CG7!hoJYqBvaV!yA`J}$WHt*P-7BlZV6 z?Nd8-Htp4n=@Gz#oq*ZF5BCoa?%1mvbrIjgoxXn1%Ka{U|bOKKilcQB-r{^ad7VbMC0;^@A*#ORl#G23W87XSB+~Tz85=v z*9Sk`KPCRAi2dbG`)!>`Ul#%F=mgx+S>e~-(TqDIfY&+!TSBe2d+j!*hvUA8 z_l-{PgPr^<-qnpqB7mKpfG657x7$Yaa6BFH?C$hD8w#Bn6uWv|d!H>a*zaK1;DGmf z881YDZ*>A+3D1C=-!rV&BEGjfeLDj^f?7&w?sN~Td%Fa?9*B+K6G83oMC}VTRoZ@) zWGilOk!&%hG*V23t1_{TxD*TT*r{$T`mpcD3K@aTcL#^(|Hp-%hP!G?q5 zjiV9!2c7og!DkNE89zqsA9dP)4fZ=U(Kr#YfAY6ov4sXmxF9L-$p4U{ND=5~ozO19 zV~2+3byw6ts;IcQ@ZV`nQ)Zu%x<;ubSD7OuSxELU^=ZM)A7kwiV#Dn?(uY2rf7YtpDG$wBm7kMR zURtzz)~cf7vVUt2DCeJ&^8)2Ua+QmPqzXwB(Q}RnNlzd8aPFaf+E;4bs=|`uf}E6s z($a#}vsV=t_AmW6^OedqA?E+txz{S!k*i!UBvVLMSgkX7%SV}cw<_yT8M8sTom^$3 zkm7~ZBOKE+ctcu#ggkloXEX6|DL=bMTYO_ETp4tnwVW$_qkD z5mIV6CM}fo8}ChY<%BC)R2Z~Q5-Q+57 z38{~e`i41N;lVrilRkNel#fGk?<*fDA1WUSDMv`TLh2`^yba1H%BSQihlP|cq=Il< zVK^@D)82WXaihlu~zlu~=BJ;_xQg*0!7kcNhthlR@cR?6!u2i2}B?H?lUUr@Aq_Nsz{{{Nm( zYMRrYvL2Gvcj+9;%2A=QQ3n-j`^SLQXV zKaEVt^3v6_R+SXz|GTKoYHNt|e@z>;OQK@LTV7v(h(uKgU^4FV@iMr{uj{y@FhI zjgXpzUs@O@ZVqMmNX(0`HOyXB8X7qHg{x<+D(GMQ?-Z|7Z#pIG&FU@Ws<#QrC#04z z>7wxL`=DX}jM<@etRTOzcy-OHf}(=roRpHHuvmY1J5g^}?>Zz4z)q8~0CZxq- z-X-Dgc>O*5qzG?8Ny+M2tIEpD|1I`G^|4cuKCV7NuKJY7l$Hr;d6;!YsJ0jEoqon| zq1uM^mKUs^wW_>-Q852&tIz-J*H_dXFjXK>hz%x=j>Q28Q0gXDlRK7$w?{9FADW*|Dv+OoRmnZB9;13{VYWMKgAqY zKPOlHQb_);LJEYLMR3vAef{66Kb%s{kLpk4s=o;7Tp^tojyXS6-WLLkqppaQx2UKv zw1^bs|N9J}{;5f)lA_6)om{PpkS-L`MUma};$XrzS^1hnGfo+0Y8JU#oRBUR(q&;S zmj{=8@bwX+n$6X)l(U-^Heqwf{hJ_{N zp&FKl7SDg2qqH&F#8ax7q)m<_cB7DP@}JD?p~FqzJ+$k`NM?m4ol{<$snwlQ;T&yl zgzFX|-FlMh;oRMKwSRG4n5ut4!9S=xT63Usy|zGW(iRG7t&r9g3TeZ7&8zve79rgs zq%A_)`uAyAYYQJirH;RkpxUxPkKj4S1}1f4PLE*LcVf;8?mW6Ces#otd#7E54&@hz z&i}z%kNXn-K6Ps6X%_@4JGAqKw6R0GP)M7C`9EgFUmB?I&@L0wop13aUHMwOLc3C1 zqg@r8bu6jtW+B}zq`Lyu!E=s{kG@W8559gZHTrt(hTySdL$#Z=TSJ7m2!2de8;@t4AkVBEKZgX(cZyGOe(M0c-{9_-NW z7t%vvx`#q^4-4txPP#|6HQM7Lx<|uwkA&z>CVbBHZQ4tL$~&~}+Oyho+Vk2A+KWPZ zTu4s}>1iQt6VkIndj1aWW$hJhhxV%Wn)bSoUKG;nLfS2)w}kYrkPda0eP^V+PXw2I zKeWr<$jei~CEpvt*T3)ACGxc+Jrg!Kf9Tic{Rm=v*iipNzb>Cd49|rPn||om<%@{n zg|Ok+5B;xWR*KMLuUy?m+tto;(|8Rjbq+V9#4ZD(-l zk7?!(A-yJ~C-%@)N4M!wwnRNTxar5flV4T4?Z%5wmvwvX#Og7v^F1pkyO%Fqr=LdK zKMfI7SG`-k>KrqoYJ6MMh_=QRi#)B%$G0`Qmp8YyuGhQkF~QWI2I#t?Yk|rRT@}(B z9l9>0H-q^2o{z>xK0GN&a~u{zf5v@L&EW?S%G4nE%5t{|o=*Zwc|+JNeu6B_aC7 zLi)HvUn-F4Vg24=a|6LOc}&|llet_f6Z z(67?3)`R*rLOLp>ABFU*kpAct)UMwY;<{eHLBCN*-wNrNkiJ{5->l!F-zuczLi%1v zKh&#P!70D>3daAoI&-7G`F{o6t#8rq5z;ldy*#C4Kw){xZ5fuqiLqR9+T%8W%w1`;`ng6~t)MH~JW;0|S7~Vo67mk|4 z&>}8p$TjTm`>!2)V^TwhzZk=5^azx#GvbVRA;$~3$2y~@ks#!rLQZI38)fU8kY;55 z2asj-7IKo1d$n(gvL&YG7aHJ7%Al5Lhci3XPhxH1Q;J`#}#g8k}+Ax*+S0wM?3x*#_SO6 z9|_eMwL&k!!(%Kknv8`;v*8ud zkBR7;gj^uxA|aOuxm3vILaq?-X(OIoqKxusv{ZKJU!;vVvM)S`v%*7^M!BbV;sF9YLReFmJ+MnuTOY@Ht@^~Rn5b{JJ zPZIKEAx{zV)J?_{WEf8wPaDq|+sGhG$khQ=$kT*;pO7CBc4t5hRM(CfQ8mpyZ@hb+ zr=?z1$Bd||Uf6cVMEB{l< z&k*uVAY$4YOxmL(^n~eR&J0Zo!0pp-?C{&g?LS7t_E99$$yiv%TMD&rG7WbKL zE0#}N>hVNQ9ucAKH^JJ~idNv;=ii;!Cb>@|Kfem73g z)%e5s)5NryQSCWBZPR0#h3pn`gOGiJ>h{Zf+RBHRrv>WQn_bMVW;Y?v6S7ChO}pu8 z#+b3Dqh`!}PwVpL1}UX@MF)Z0@NY{}8huh-hT~QXZw&;o1^HCI-z-ak&w?lIdD$a^sLaxS!JGWt`_onLOx%}7o6;CM`XC1 zeEn-coP50??7i@Rjs$<_P&k=pjCq9_43z!v3r_PI^ICGvb|GIP&U$sqJa!D<&~w(TSde&{u2Vq5bv zPfC8jqVQXF^I4vhWsUBZkSRapAGOqddh_x#Q~Cyfcl14ZN;hu{b=6uSuc=qfRiR6* z{M9)rt4hm?b5i=3m9Osngf=&rcLgeMH*YuZFgKc;%sYjAwUC2CzDCH`3i-O*&CTZB z<`$c3-YevGAzv@#8$*Yvo9k6&e0Xsg+19vX*@U+Fp4l_p?F~t`O3i%4d@MBP9u@Kp z9idguLM-JjUE*z7;52XYMxl2zjlLJJy+d&9{ZT zPRQ$n?#$HoiM?zoiRMA`ga4pDG(QsZ?Lxkz{gPg`UVihi`FWsnZRe+u`IY%ir;Z~+ zzVomB-8>dj@|}5H$ae{OOQ(__&0qf0#;@jYLf$OoyJ!EsSELVF^+EWw@OPYr6&>*Z zD+NokWFg-pAU-NCdHK+4GuUHL%iq7F< zHCn!q!ugiRT3|I<3$13$E96&&{F;zo7xEiIepARhg}iGcS>*Dn)n+Xw%UT+!4jpj% z3wgJY_k>pAy^&pDn0tBS!ja9b^Fs%aM_!AyMcJlUXVcY|VXdy4SUuI<+Tw@-V#b!y?h1NywWr}UWJnJ&+ zxniJND`;I~T`S~wg#50M-xKlyAs-a-AtApnGLvF@!4fBc8O z{i{98*IV~k_t7?eVs*7=d3bDFo2{)8eE2__Sr4D=25YnR*xwFov-M=?<>bJ3TF+R| z1uEBD+pO)@vqJt-$X^Nh>-A>r=1?C9xjkI`_6eG;Hs5;9+7+nWV7+d=VZCYX6!H-v z9~JVqLO!;^+D)$YmbF*N--WaILC8P0r|P!2_AfPCd3I>ZR(o1JjmtgrLr1Wa?Z;RL ztV4ma4(p(hk9Sz_3;FwY&9o&**2mT-?TXWu?vJrPx4!Izd?nEm78Y z)~}&4cHH{j`oa3q`pNp)`bEgU2>Dkb|0d+$g?vKDe+c={jn;4ES|_YOLUX}s6Lvzn z?KWZW5_$+d$IcBMHB7_ymF|pjIs#=K&RAiO>ToK;9vznHcN$JBP}$)$gS z=T7?9uil+Kor$5<$SLFn9nK_S@4W1Ara03=E{BjCJDlmle%j=d$;CK(JNtIF=@Ry? zCtXfwt}`#x?C?mt`NH0<(^upy3si0}oz8S;e`l$X7YO@l!roojV>UR`BY(q|(;`3H zZI&%%s&lAwcxPiHgx%3eILbNtUqyC~b506Wu6K@iPH;{Xc2(FlVb|A_>zv}88m^on z>{k0S%eFe%Im=n|cjq~4h28Aro$IU*b#ca2XQR{e_j2uAAnb8}E!WQGK*f5e*XeV% z2z$J+CkT75lkqXmHs{iRuffh`&gH`1L)d$^pYOC~COXe_p8X%hYUeq^o+#`|?JqiQ zDf67?IxqMS;zH*|!k#SbDUoGx`ts(M=H<CL(FD~c^G0I zg_x%x<{60D4lyr6%*zn-D#UyQv0Wk739<1In+LH45L*PXB@kN*vE>k30kH!ib}+;a zh1lT`TLrOo5IYxQ8^9ks4`MwKyAWc15Zel|OCWYR#GV1MXF=>$5PJ{Az6`N@A@*x< zB!i;>98zCoA`0;1|TdqvI>-oK)D2zn?Sh*l-oe*0A)QWw}Y|~lsiG$49b(BJPpb=P@VpN~tzbL^#x^jX1><=zUIgQ1Fm`~k7mR&iyaUF2U>pSFeJ~X; z2Y}fO=JjB{4CW_b9s~0@m_LH~Gnl`Dc>*jAte#*cf|UwZI#`+bu?oQ|2CE#b3a|!( zH4Ll~V2uQ88dyzWEd*-`SZ9HCAy}7zbpu#8fwdN_bzp4(>rSvXgS7>$hrxOVtT({g z3)VrfJ_GA_a7KeO*$>V>;BigY#F2ONO}K5H}p+#zWjhi1SZ@xN3-NfVg=O z*9LJ*AZ{7Nt$?^QAnr_vTLp0!LEI$}cNxT80dZ>}?rMm;2I3xrxV;efJH)3!{Ah?@ z2=QKsUk33jAbuspp9S&fLj3sVu^|2o9K z3Gurieh`RNYDfq@-wO%v zLSk1)EQQ1=kT@3-8z8X}5aT0}}6m#7&TR z7bM;biT6X|R!H0qiElyTK}hs}35j1r;xR}(4v9ZN;;)eSJ0$)ANvA=Q3Q0O7S&$S5 zNqr#61xY!O)DM#KA?aL5x(t$TfTWusX)PqJgQN|RbSETjhNLZ!^bjOH1xfoL=@alL zeFjN~A?b5S`T>%Df?iS3OM+f@=+y;!b%S0p&?^pl^?+Up&?^agB}1=N=rsj;oejM< zL$7xsxf>*BL2?l!mq2nEBoBb(fsi~HlB*%P4wC2MC%F-lJ&@c4$;%=6bVyzW$*UnW zJ(JIaru$s23ZFn>r==&3|U`7*4L2r3uOHUy}Ll~ZqPdh zdOM)E3cYpcZ9?x<=$#I|Gog2H=-n53XG8B?=sf{?H$(3=(EDNNeE|B{pieh&^ofB! zvCzi}ed62wIkqSKzd_#<(DzSp*}xSIE*V^>fvYRHV!)+}*GzELfNKu88o<>E zt_9#~23HHX+Q79GTr0q}5?rgmbq=@$xXuIDh2XjbT$h7u4Y-2fx(-}7fa_*(-3G38 z;JO`Lo4~ahT=#(Me*Cx|1lJ?rdK_F&fomJMo&(p5;CcmIuYv1LaP0=yUU2OP*L&bP z1g;Ok^$EBRgX>FheFLsXm+X2~{AF@rzj)Ux; zkevkCDUh8G*;$a?7qW98I}fr8A-e>!%OJY~vIjx-P{<-A@0NEQM`!2}d0@?RL_G6I!1Y{qC?9U+kbI1X5xs$Zde!OCk3f$h{77w?XbJkozk1b3ng1=+^`KRpF=KMCdme`kfE`u7G}Ppx;*L z_ayXt8v1<({f6Uj_L=$bT5}pMm`CkpCs*ABX%Opg@O$ z1Sm*?f&ox4915zSU;z}gLBSFzxCjcaf`T9v+zSPdLc!xu@DUVz1qI(gp#_EhUQn0< zg_TfP1%;!aun7tmL*Y^=ya5W=LE#1{d(Elj({~7xK3Z+gcO@`7`D6N9hiBLKjN?ZI;x&lhifYR%s zv;#`lL+Ptf`WBSF4W++8Srn8>P?iB@xlon|Wpkix0hBF-vdf_CS}1FWvPYn78g4JDq+akFywp~av==a3quaUkPl#J z4h$`Up`|eNd>DEK3|#|5--Dr_z|hZNSRxF|gkim5*a8^V2E&%Xu&prcNf`Dt4EqI! zN5OCjhWkgu@F_678iwBl!#BY2J7D-HF#H=BeiTOJ!H6;#F#txK0V4u1;{Ra8HW={= zjCd8QVxh_jRq;?Y8LDPORV`Enq3UL+x)rMSLDdIP^%0CrgOSD^;)b}vz1dRF0%IPAF^|HS-(ai+W9=|@5R4rK zW5>YQ%V6xaFt#1W?t`%(z}SyqTpEnahH<$tt`Wwyz_?Zz_Xv#J2IHQE@!ep&0pl$g zzZAx=g7K^UF#dTM|2m9+6DGvLgcO*N1{1t6VHr$V0TVXEga={5!!YqQn5e=;9VX6( ziSuCMe3*C-Ond|;J_eJbU{ZIO6bqB4z@!?OR0oqb!=wjc(!((6Cz$L9CP%^KDwsSG zCQpXRYhm)8FnKdfJ`R(Ahsl4yl;JRCJWQDgQ#QbqEimO?nDPTm`2(f`Q-{FRF)(!; zObx))i(%@eFm(@1JpfY=L3JKfmqGOa@K-N|>Qzv^8meD_>NlWzCrpchX%&b#VA>-vZ5vE`7N#e|^gb}%1=G)k>6gLuD`5IAnEozIKL9f_VMafgkqXo zc@SzIfttsm<~gW&0cv(Y&FfIJ6Kebiq2_(4`2=b{gPPBw<{PLv3N_zB&2LcK1!}uN ztpc?g)EZD554Am^HVJC8pmrG4j)U3>P&*lFtD$y2)HXqFGt{;~Z5z}+0kzLV?JlU@ z1OD1~q4pruz7Mq@LG2e%`!&=ah1&0+_E)G&hPu8`R|Rzwp>8tNRYTnjsB45e57aG% zy5&%JI@GO%y0f6}Y^Xa2>Mn)4%b{)!)LjjA*Far6)ZKuex^+;u0qSmtx_hARKB#*E z>RyJrSE23=sM`f~Z$aHYsCyUc4no}rQ1>y^eGPR-V2&N;C@@EZIoU9$80PecIa6Uy zEzFq%bIyY~m&2SZVb0w!=OI7Lc?9O{hB@!SoP#ho0p@1F+$@;e1alX|+@&!0ewh0> z%zYB(?t{6Xz}(MZ?qQhwIn4b5=KcirQBW^Iy&dYiKz%o;kA?bps84|UUQnM3^%+p_ z?+x`cp?(?E2cZ7{p#FTQzYyv#f%?m#{yM0?9_nv_`n6EM9_sId`Ujx?5vYF*>Ysr6 zZBYL#)V~1rZ$SNfP=65WKZ5#Cp#C$c{|f5Af%1LjYH`RBm=EinHe%>M;EcJOooPj~P*z@vf31WzCE6o97)JZ0di z0M8)sd&Yrh0(ho^XF7OhfoBeQ>cKM)Jd43|E_f~j&kf+Y2|R1TvmQKmfM+v!wt(k8 z@H_^dz2G?no)5tDF?c=$&rjg_6+FL#=TBG=1q-TSK^=Y;G{XWPELaK)mcxQGV8L0i zK){0YV8Mm3;1XDH6)bod7VL%v2VlXcu;4H(_!1U;0}FnI1;0a6S7?fXCIy;wXtJOw z9-0!MsSh-{peYxc@}Q}}4^2hT)E}B^q3H~0x)qw9ho(clEKG&w zJD~YaXucbo?}g?Ep!p$aeiWLYfaa&6c{?;e56v$@^A2c!9h!GS^B!n^8=BvNX8!?b zejl1Yg62=5`EzLg3Yw2V^D$`t9-4oG=3k-t1b6{&G{2hc#FW>AH3z@_f~>;FnEW7w+g(Y!8;DT6Tv$Lywkut6TCIx zodezm@HT>X0eG9i+XCJ;@Gb@K3h=H3?<(+~16~2%^T2x{crO9(<=|Zd-XM6d18->k z_udTN+rYaHytjjQ6L>d+_a5-x58emC`v`a+2k%qh-3H$0!22S2UjgrH;C&OkyTQ8` zy!*lX9(WId_e1c00^Y;m{Sv(2fcIPQ9tZD_;Qhr9-rvFdC-|bkCxfpG__~A70X_|U zCivpO*Ask6;7b8tI{31{*B5*_;L8JFA^1wbR|dWc@C^drQ1Fca-ze~n1>XelO$J{z z_-265KO1~?;HwAUJn(tIw-9_j@U?<(3HX+S?+oyr1-{kb3xMxj@Ld4Di@|pp_^t%s z)!@4peAk2TCh*+~z7Fth0N+ON-37ia;JXieTfz4*_V<{IqO`mgk`5eQ5a{TE2uuc37mqA`KRm!=hoZXap?sz@k=Iv=|m$ z0gKvU(G9TZIau@>EP4Z4HE8V#t%=Y&8d|47Yc;f91g%#=YYRq}@T<9j4t+v~NoLOxm}m{aPZlTj%bh?{Po#=FC?NvIBq0>}4O{dehbozl#Yw1)@r!!zxbk3sld33&j z&NtHePCEaC&acq9E1kR3c`Ti$(Rl`)3+TL+&S&C(=Tmg4MVH!ixtuQ7(B-dmd5SJC z(&c5k45G_0x{RR9Cv^FOE=%chgf3_H|4i3k(e<}<{T*HZL)ZV(^#!`VN7oT_9ZADG^K{pmK3Zj0%*gl>oEc7$$6 z>7Gva)^yLI`)zc;hwk^$y(iuK()}&EPpA8Qx-X#nPP!M<{h)*%_2`jCk7o3^j~B^w>dS>L zkDl}Cxrv^;>A9C)b?MbKL9cXrT}iL&=ye0Vo}t&v^lD45_vw{KuTk{+l3w4@YZZyc zBwCVaMdBF}FLUN9KQV{IXCxNWyAHjZ&^wLZ_tN`OdjFf=)9F2*-V1m&k;1DDc(oC) zKFX`l@al8CI+s@$@#^Qi`V+67=G9+#?HXRYh1YK7wf?;JA+HVLwE|w-%4^$sJ%`tS z&FdHPdV5~)!Rx(veG#uOtqXXo6K^GW>s8)b%v;~^)^hp< z{p!-MKK-tx-{0tW8~yswZy^2NrQbUGZKvN(-fqU**}R>b;O(b)`$gVv_ofec#1pv?^0%DY*-dmiszz`I>|_f_6~op)FA?t0$cNaDR#y!R{K zJCFBz@LnI@dz1IR=e>=*x0&}_@P01upTqkP^8Vwz|0M5E<^6fQKc5e3^Fd=iXu=1# z@xeWOa33Fx;)4l%Fo_R}_@ImrD){g+KD=7Ohu8AqAU+(%ha>p#ID;{`27@1A@KX$a zhQX5=Jcq&a7<`%`XEEe#hTP7Odl_;+LxwYC3`52-q>v${3@K;mZyEYWhF;0g4h-$d z&;&!5oDs_z`W?g0Vpt=Fr84X;hCRfvM;JDWVG|fOiD8Esc9LPI8Gb9n?`HVD3?Iz! zj~Je0_;!ZxV|Xzm&Spe9Bbqa!1tVHA;ygxNz=%s2k+_TzS1{twjJTQ+H#6c^M%=-O ze=y>1M)YLFG)8PAuO4|V$jcxvi@Y52&LQtS^8QTT4dmTK-fiUFN#0%LJxboc$$N^t zXUTh>yqC#46R`3+koOvSgUS1dys6|(CvOgUACtF$yv5`#A#W*pE6FP+uadmO!AIxt(YcJC#OSGvp26tZjGo8nPZ+(B(Tf@V z1*5-W^f!!N!RS?tUc>0MjNZWL&5Yj0=$(w-!{{PL7c=@0qstgw$>_upMjvBz6{Alv z`WKQlNY)~GHpzM<8VnMSfX$qbTNBy&ifL-IV5zb1JR$={K@l;q_kuOxXD$!kbn zNAgCJw~)Mz>>`6=YrCciHE z4ajetAipX3&B$*_ekS?ZnfzPH zzk~dLkbgJ%_mTe~`HztQ82OKr{}lPplK(vUFOr{lh5Yv9cOt(l`901|lK&d{eaL@{ z{Ql$*B>z3~KO}!B`6I|5MSha}vE)x6e=_;g$e&669P&RVe*yWQk^edQUy}bd`QMWN z9r@pr$p3--b>wd(e+&8B$=^l(Uh?;me}McS$v?uFbjGx1Ob%nNWXyGpxq&f{GUge^ zJja-Uj2X(9;f(o&F<&rdDPwjrrkF7Y8C#374HD z-<PJkS$kfS9{feopn7W#&$C&yH z(}HOkOgo2Z=Q8bPru~Cy|72Q6ruAZ4?*!BGnKp%K)0p-R)7CJpfN5n+JI1sVOwVNc zxlBKw={GR_4yON|=`SA=5S`_ zG4pd~e#^`i%skA@lgvELtn-=mduCnAtUH-?KeHYrF)P8Wx0v-dv*t2u5wkvL)5g4|H<5YnA?rHuQRs~ zb0;u&CUa*qcMEg(GPj6%wVBtLc}^mabIlTZ8e>4$tegikl}=`KFq!@|}qJfDTXX5q6ee1(PWSU8V`i&?mYg~$Jo ztop15pZ%H7Zs4<<_^cP7y~$_&_-qBAt>v@zENaN2<}7N-qJOjKITk(7qNyyJ$D;Wx z`jJJ|EIP&F+gN-Li|=D`9*f7Zcmj({S$vem$0dAz6QBQ`&+p>%0en7~&xi8)4}89b z&$qFpAxoOGq$Nx4Wyzx~`8P|(uw*Jrrn6)pOUhYN$rpd%i@)&2HGI*FFW%&fetfZl zFV^zKdcLg7muL9@m+5@@7+*fim;dF<*?hT>FBh@2B}?0|^j9o>fu$W-+KHu~v-De* zuHdUwzRKXMOuo9CuO8v6NBL?7Uwy(?pYk=nuEW>$`1(G+evGgG!`F$)d_9M+=kfIk zmIcddvg~e_J;Jg_S@sdjCbDcY%Su^xlx4^H<`%xWi*N4cn-P37mT$)M%^|)y!Z%0x z_IAF#mv8Uq+mU=bo^L1eZ86_glKA#8%g<-|?^%8+%e%4sb(Z&G`9_xSV)-6cv}8pa zR{V+;|6@f*R&-{?TvjY%#pirilke*DT|>TmjPIW1yZ`dtH+;8-?+RG?8&>{-m6x-! zi-eW0vhsCSu43f|R&HWd2CL3t)w!&Cj#X`0)t*&zSoImJ7PIOU-`C>%+I)Wl-`~Oa zf9Lywd_R=$hx7dgzTe6ByIGyh>I+!?8&?01)g4*g`3_cp$?ES|y^1w0Sd+_|b6E2f zYhGl{%dDBont805&zeoFDPqlj))cem0Ber2=J?Qw560_>3H&gbAExocOn#Wd4^m=6jV}hn6+23_9oWe!rI$ddxwOz_p$Z?)=pvVC#?OH zwToE$IcvXV?F!Zf>l(7IG3%PLE}eC)S(n4Qf3fbrtb2iVFR|_w)^%lFch=2i-50D| z%DQE&`<8V-ux>5u&t`pkg7wW=pTYV})}PDz^I88m>tAC1E39wN`i`vc#roc?|C;p$ ztY63ajjZ3y`aP^KWW)JvxQq>dWW%4>a1|SFWW&vD7{G?%Y{+B7M{G#4;mnENFqI8^ z*ig=fN;Vu}!%;T;%tmayiH&!&@m@AQz{ZEz_yilDV&hacE@0zAHZEr45;iVp<9BST z%ckaRYRRTdHnnEc`E2?%oBqS57fEb-nN983)PYSs*_2?@d^UZ>re$ne&ZaY9>)5n` z&8^vdA)9~8=HIjVQZ`@3=BwGwO-Y%XMTC7Tbk`6!!@vxS5$ zHP~_mTW(~_&1|`qEw{7fUbfuNmOQphV#^e^OlQkXwk%-FLbm+KmY>;*tu@(Ni>(dW z+L*03v-KXf-pAGl+4?YBpJeOPY#qVYiEN#mVCyut&S2{&Z2gq2m25rD)?e6GgKa5n zYrwWfZ2KGA?qk~nYbI5@dP`bV#j=Te8rAs>{!l@@7S@99UIu0&dxUM{1u6v=d<(I?7Wnnm$CB=cD~2X z57;?`ox|9f&(5*z+{ezt?EHzH$JtrMt{Uu0Vb?9}x|dz|v+E&tJ;JW1*!2v%^4K+r zT~pXKmtFJO^(ng+N!Yc7UCY_El3lCWRlu(G?5bdQGj{)q-FLA2K6XFB?kCy(BD=e> zy9c}bu=_1`_hu%zhw6Tb{}I;YxbPao--N!Jy){lCidLI zp1as{4}0!s&qM6_7ki#&&vWc~fjuv=r!9NBvFA>bYDk?bAM-Wlwj&)%=tyNtc7*;~Ng_3Yim-mUB{WbYZn2iaT7-U{}fAW;|; zHl(mIg)J$}q%fPpHWZ#q;l&hQLg8f;UP0lXDZGKgJ1D%5!Y3$viozEue1*dH6n3Jp zD}}F7*oVTmDC|$+KnjOaIF7;@6n-Y5a5077P`HA^RTQqFa4m(~DBMZm9tw*nET*uM z!YYbtP}GQ`REjbv%AzQTqH`!ZkD}jEbSXubQ*z6;rRG5aoI-(~E(f_-L2rpT_*EEB3Eq{~GqMW&Z~DZ)X1yiZdv_fa1F-eu&~HDSnaS zZWQ;RxDUl|QQV*6ffT<-@d%1XQJkcBtc2nT6wjo15yjt7ypG}x6z`;X55+|k7gKzQ z;xk5%QCvmwDT;sLz}Xx~<3MW;T*!gna^Q~~_!9^I!hvf!a6JcZ=fK}N@J|lh%Yg?t z&@RD&;T)LAfi)c1%z^zJ_>luYa}WpXaIiiH8*#7+2h%y&nuEC<{1pe!=iqNRcoPTz z$H8tK9K*q>99+P`r5s$t!2%9$<=_qu?&e@22NU}_Si!-=96ZXw6C6Crp=KPqnnSm9 zs6B^zaHt=LhHz*Yhw?czjzbeUG=)RcIW(U`pK@pshn8??DTh{aXg!B^aOe<+N;!0t zLnkU*@8z{Yr(!W#sPfG8l^Z`mA zru0clpSg?D|5ExtN?)e53#G47+Mm*4l#ZZuETt1DolNO8N@r5KfYQ$>{hZP-DgBz# z?CIf{e`kyDZ8DrdntQ>vWF>ql(PR&_Fu~WN7>7iwWF*fWxXitPuXC~ z#!xnnvgwq~qHHc@^C|n3vZa(Qqii{4D=Aw|*(S;gDJvsUR!!L{%4<=6Hs$pwZ%BD6 zekvZK;$I0W{!PUbRJ=gNOH{O_q5~D3 zspw6`+f;l=MLrc{shCE^Oe*G3@i7$(sQ8kKuc`Q!itniSo{EiB?55%n700QlqOt~+ zwWvIs%6e2L8dBMu$_y&AsLY}A94arO@^UJ#r}9QB@1*iBD(|84ekvcL@(C)Rrt&!| zU!d|ODmzozo62{n97N?1Du+`!lFHFkPM~r!mD8x4NuqKQm7i1jC6!-Mxr)lw{8*14 zTkvBBKW6b`Ha}j#kH6u^8T|MeKQ89SFZgjOKd$7*?>YPsho9l_a~ytw!!L2TEr&aB z_+1VU=Wrf}KjLt{gu`<})0D@Xfsv>!(YaC8ty z-{v5KX`DEp z6TjiaMVz>t6W4L#W=`DAiAOlmffL<1@u7qhQ#kPnCqCuGB2Fyf#8OTyX8Ihk5TnFRZmg%ELE>i)sCu;RCS^19jXRV^)6K(P&I_A;Z#kaY5`TBQS~`hUr@Dz zssgIkQ?-ez-BguRb(E@8RM(-p9@P!0PNh1LMs;(lbEs}Z^|@4EK=svBUrY60slI{g zo2kB)>iemFi0VhEevazrseX~_SEz1Jb*D3+RKH5~8&tnZ^+>8eqIxvdW2hca^(3l4 zCQ-eB>V;H)OZ5t>S5du&>a|pFpt_LiGODYoKE=u4WC|y1bFw}s8*#D;C(q;LHJp5s zlh1RqGbg)pvNtDR=VV___T%Kcocw^3LpXV+PWR+0P8M?tr|NL39;ebc)tplqoXX-< z4yS(0so!zxkDU4wr~bmJYdLi@r*7rc9h~|Hr@C-z0;kq;Y8$6^aH^0~m7F@vsVYvL z;?ysku94t$9ZuKhbR$kT;dCobx90Q(oW7jXS8)0oPG86A8##Ror~l6Be{%X>PCvlu z|8V+QPQSwG_WZJrUv~4$UVf?Im&5#WlwVHp%SnFuS^U4AXegu#O@(GcOQDsJC0s8& zE4(LsCVVY?FKiHY3VVb@!jHl+5p_h=6Ok^WwTK)M=ZH90#HAuG6LEuxn?>9z;tr9B ze~7qS#C;+j6!ENx=S93I;uR6?MRXF;RYVUF{Y3N^F;K+2A~uRB6tPdl0TG8pjo*tJ zSBo0giW=988aIj>y+w_8M2$hB#`~hihoZ(xiKwwz)YvL&><~3}iJIA>=0&3B#iHgV zqUIk&%`T#5A5rs7QFDN(IY`ueU(_5bYK{;!M~RwaMa?Oq=3G&8k*K*s)LbcQ{vc|u z6E!!An%hLpoucL*QS*?fc_blHh?E*4rM5_^D^ePWlqMo2U8J-SDXm4y`6A`_BIRn4 za;-?YQKZ}=Qf?C|{}3s6i#3 ztX|@*{^G26#98l(v)&hHtrBN#5oc`^XYCYc?H0A0OGNEoiQ4Ch+P@aHFBG-^BWk}W zYQHRMw-dEHh}u&{?FFLtLQ#9AsJ&X$E)ccXi`tt+?X9Br4pI9IXrZY6lc;@M)UFn_ zPm8mOvulX6Yl*XKi?i#Bvu_t?Kb#O}_Y!B15NEFtXP1aNwMCsaqR#I`ol8WW>qVWL zM4i8hI=72De;0NBDeBxS>O3IoJS^(GAnLp%>bxTAv=?UI`&yNS9zMcv+_?(3p%Us1Q8 zs5?N^9VO}}McuKY?s!pmqNqDX)SWKs&JuO!in^bRx|>AZeWGrqsCSmA_iIt_YEkbl zk*N2$sMkZ(8zSmW74>F_db32mxuV{DQE!c?cUaW>S=6s3>ZgkO8KQocsGlS1pCjs@ zE9(DQ)W1{I|Cgx$vZ&u*)E_A7zbEQ{DC!Rt^@of4D@FbFqW(rve}_cW-!1AFiu(IS z{R&aPTGT%+8W0WYhz3nWgG)q%KZ*wTi3X2}29JvdPl*OEhz1=*gU+HsH__k?(cnGN z-~-WMh-ffeG#DcqED#MAiw5gNgYBYWZPBp4Xm~|JG`veRyjwK9S2TP;G<;Yzd{Q)g zS~Pr4G<-oc>>wI`BpQwt4Zjx+H;RU*M57Rm&KHd?6OH~T8vRK$`ip3Et!Q+;X!N{j z)LAs@DjM|=jS`~KYobvf(dg|n$Xeo!=iRy2NI zG=5Puenm8HFA|MAiN;+;;~t`MKhb!cXgo_a{#rENEgJtUQtOJ;^F-=ZBJ~=PdYwqU zQKa4?Qg0QhcZ<|}Md~9W^)Zq9xJZ3Uq`oLpUlFP8MQRU`nh>e4iPZigb)ZOnPa;zD zMCv4wIz^;T7pb#E>SB?)M5HbksVhb5YLQwXQrC;r9U^tNNG%kp`$g(Ok$PC9{vw*x z5ltQyP5O!^gG7_}M3WCilcA!?4AJB>(PXh`vRpJ-DVnTKh$aQ1$$HUblW4M6G}$Mb z91u-PM3XYnq*64kA)3|~P18ivY|->e(ex(K^cK(ey6S^ls7gWzn>UXxd9O zeN{AlLo|I;H0>vvjuK6$h^Es-)0v`aVvcC~v1qzLG+iZ{ZV*j3iKg2{(_NzJUeUBd zH2qODJtmq~iKeGST8Ok1kycxzrHix+MA{!k+O;C>4w3eEk#>(ryI-U|B+?!eX^)Gv zr$pK-BCV@P>njpzZ;7;iBJFLFHdLey7il9!+GvqBMx>1wX){FHERpuHNLwJ%J`-un zMcQ{FZH-73C;8}9FhL9NM9h*zf6eq)gt`|k-kTy7m4&@k$y;| zmx=UBk$%RDVVnqMfI|5h~ri)fy>Ry4m}G`~qS|C?xjyJ-G*(fpsH`F}+7Cq?sTMDzcO z<}ZlmFN)@`i017?^FE^aMA3Y;Xnt6Cw0J_acuKT*TC{jcw0K3dXe(Ou z5-oa*7K22K_eG+`V9{cjXptvcd?Z@rix%TVi@Bo3$D+jo(PE)!u~@YDLbO;aS}YSS zN<_;>qUB|x<^7^%Z_zR-T7D~9ZV@f_ik20k)nE|iFj zYemM*BI99^@wCWzOJocd8ACSR^u*h>WEoW0}aaAh-lSNw8|2#E)lJ660Pn{h*tj-t$K@ABSov(qSbQI zsz9__FIsI7t#*o5C8E_~(dvZA43U{4GHZ#Cq>pdBI{C-b)(4or^vcTWZf^a9uisq5?TKiS#3pD zFOk(-WW6fx&w5Q{4HsDxMAjscHC1HI5LvTD);y8*rO5h9WPL5NmWiyjB5S?KDim4! zMb<%)RVuP7MAl)EbyQ@X82U@sXr5>$TDKIfGezqQMeDmn>nB9(uA=oY(Rz_+T_Un; zi0nopJ5^+-iR|VgJ40k=iR|Br?7xWYYee>SBKt;>eT&GxO=RCIvL6@OPm1hkMD~A0 z_Wwlo%ObmnNM!dC*{_Q1H$?WEBKvKT-Ctx66WJp~_9&5^6xm}%_5_hVS7d)IvKNT# z&qVg;BKu2`y;@}dAhNfJ?Cm0Zm&o2LviFJXa*_R`$o@%W9~arxBB!RvNtKA4)*>fI zm@{PeUW>P$o;*@y;S60E^@CF zxmStYYeeoXBKKC2dzZ+)N95iwavu`8&xqXTMDB|s_Z5-bUgUNWx!px>FOmD6$Q>ba zCyLz3B6o(!oh@?biQG>`?jn)fc8SRSUgT~RxrHKkpU6EVa?3<+rN})ha!-idlcG&S y(Wbd*^Lx?ePom9LqRlm;&2^&9J)+HnqRnIf@0(^RHU3XG`|$tt|KFm`fBrvX5;?yB delta 35049 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 + + diff --git a/main.cpp b/main.cpp index ba6bf38a16..abdb8e5bc8 100644 --- a/main.cpp +++ b/main.cpp @@ -43,6 +43,7 @@ #include "audio.h" #include "head.h" #include "hand.h" +#include "particle.h" //TGAImg Img; @@ -54,7 +55,7 @@ int serial_on = 0; // Is serial connection on/off? System wil // Network Socket Stuff // For testing, add milliseconds of delay for received UDP packets int UDP_socket; -int delay = 300; +int delay = 0; char* incoming_packet; timeval ping_start; int ping_count = 0; @@ -82,8 +83,21 @@ int HEIGHT = 800; #define BOTTOM_MARGIN 0 #define RIGHT_MARGIN 0 +#define HAND_RADIUS 0.25 // Radius of in-world 'hand' of you Head myHead; // The rendered head of oneself or others -Hand myHand; // My hand (used to manipulate things in world) +Hand myHand(HAND_RADIUS, + glm::vec3(0,1,1)); // My hand (used to manipulate things in world) + +glm::vec3 box(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE); +ParticleSystem balls(500, + box, + false, // Wrap? + 0.0, // Noise + 0.3, // Size scale + 0.0 // Gravity + ); + + // FIELD INFORMATION // If the simulation 'world' is a box with 10M boundaries, the offset to a field cell is given by: @@ -97,7 +111,8 @@ Hand myHand; // My hand (used to manipulate things in wo #define RENDER_FRAME_MSECS 10 #define SLEEP 0 -#define NUM_TRIS 20000 //000 + +#define NUM_TRIS 100 // 20000 //000 struct { float vertices[NUM_TRIS * 9]; float normals [NUM_TRIS * 3]; @@ -176,11 +191,6 @@ timeval last_frame; double elapsedTime; -float randFloat () { - return (rand()%10000)/10000.f; -} - - // Every second, check the frame rates and other stuff void Timer(int extra) { @@ -354,7 +364,6 @@ const float SCALE_Y = 1.f; void update_tris() { int i; - float dist_sqrd; float field_val[3]; for (i = 0; i < NUM_TRIS; i++) @@ -374,32 +383,15 @@ void update_tris() tris.vertices[i*9+5] += tris.vel[i*3+2]; tris.vertices[i*9+8] += tris.vel[i*3+2]; - if (0) - { - dist_sqrd = tris.vertices[i*9+0]*tris.vertices[i*9+0] + - tris.vertices[i*9+1]*tris.vertices[i*9+1] + - tris.vertices[i*9+2]*tris.vertices[i*9+2]; - - if (dist_sqrd > 1.0) - { - glm::vec3 pos (tris.vertices[i*9+0],tris.vertices[i*9+1], tris.vertices[i*9+2]); - glm::normalize(pos); - pos*=-1/dist_sqrd*0.0001; - - tris.vel[i*3] += pos.x; - tris.vel[i*3+1] += pos.y; - tris.vel[i*3+2] += pos.z; - } - } // Add a little gravity - const float GRAVITY = 0.0001; - tris.vel[i*3+1] -= GRAVITY; + //tris.vel[i*3+1] -= 0.0001; + const float DRAG = 0.99; // Drag: Decay velocity - tris.vel[i*3] *= 0.99; - tris.vel[i*3+1] *= 0.99; - tris.vel[i*3+2] *= 0.99; + tris.vel[i*3] *= DRAG; + tris.vel[i*3+1] *= DRAG; + tris.vel[i*3+2] *= DRAG; } if (tris.element[i] == 1) @@ -418,12 +410,7 @@ void update_tris() // Y-direction if ((tris.vertices[i*9+1] > WORLD_SIZE) || (tris.vertices[i*9+1] < 0.0)) { - //tris.vel[i*3+1]*= -1.0; - if (tris.vertices[i*9+1] < 0.0) - { - tris.vertices[i*9+1] = tris.vertices[i*9+4] = tris.vertices[i*9+7] = WORLD_SIZE; - //tris.vel[i*3+1]*= -1.0; - } + tris.vel[i*3+1]*= -1.0; } // Z-Direction if ((tris.vertices[i*9+2] > WORLD_SIZE) || (tris.vertices[i*9+2] < 0.0)) @@ -563,6 +550,10 @@ void update_pos(float frametime) // Slide location sideways location[0] += fwd_vec[2]*-lateral_vel; location[2] += fwd_vec[0]*lateral_vel; + + // Update head and manipulator objects with object with current location + myHead.setPos(glm::vec3(location[0], location[1], location[2])); + balls.updateHand(myHead.getPos() + myHand.getPos(), glm::vec3(0,0,0), myHand.getRadius()); } void display(void) @@ -597,21 +588,15 @@ void display(void) glRotatef(render_yaw, 0, 1, 0); glTranslatef(location[0], location[1], location[2]); - glEnable(GL_DEPTH_TEST); - - // Draw a few 'planets' to find and explore + glEnable(GL_DEPTH_TEST); + // TEST: Draw a reference object in world space coordinates! glPushMatrix(); - glTranslatef(1.f, 1.f, 1.f); - glColor3f(1, 0, 0); - glutSolidSphere(0.6336, 20, 20); - glTranslatef(5, 5, 5); - glColor3f(1, 1, 0); - glutSolidSphere(0.4, 20, 20); - glTranslatef(-2.5, -2.5, 2.5); - glColor3f(1, 0, 1); - glutSolidSphere(0.3, 20, 20); + glTranslatef(1,0,0); + //glTranslatef(myHead.getPos().x, myHead.getPos().y, myHead.getPos().z); + glColor3f(1,0,0); + glutSolidCube(0.4); glPopMatrix(); - + // Draw Triangles glBegin(GL_TRIANGLES); @@ -643,9 +628,11 @@ void display(void) myHead.render(); } myHand.render(); - - // Render the world box - render_world_box(); + + balls.render(); + + // Render the world box + render_world_box(); glPopMatrix(); @@ -657,6 +644,8 @@ void display(void) glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); + drawvec3(100, 100, 0.15, 0, 1.0, 0, myHead.getPos(), 0, 1, 0); + if (mouse_pressed == 1) { glPointSize(20.f); @@ -799,8 +788,10 @@ void idle(void) // Simulation update_pos(1.f/FPS); update_tris(); + field_simulate(1.f/FPS); myHead.simulate(1.f/FPS); myHand.simulate(1.f/FPS); + balls.simulate(1.f/FPS); if (!step_on) glutPostRedisplay(); last_frame = check; diff --git a/particle.cpp b/particle.cpp index 8311099597..88ea0c96c7 100644 --- a/particle.cpp +++ b/particle.cpp @@ -8,51 +8,203 @@ #include "particle.h" +#define NUM_ELEMENTS 4 + +glm::vec3 color0(1,0,0); // Motionless particle +glm::vec3 color1(0,1,0); // Spring force +glm::vec3 color2(0,0,1); +glm::vec3 color3(0,1,1); + +float radii[NUM_ELEMENTS] = {0.3, 0.5, 0.2, 0.4}; + +ParticleSystem::ParticleSystem(int num, + glm::vec3 box, + int wrap, + float noiselevel, + float setscale, + float setgravity) { + // Create and initialize particles + int i, element; + bounds = box; + count = num; + wrapBounds = wrap; + noise = noiselevel; + gravity = setgravity; + scale = setscale; + particles = new Particle[count]; + + for (i = 0; i < count; i++) { + particles[i].position.x = randFloat()*box.x; + particles[i].position.y = randFloat()*box.y; + particles[i].position.z = randFloat()*box.z; + + particles[i].velocity.x = 0; + particles[i].velocity.y = 0; + particles[i].velocity.z = 0; + + particles[i].parent = 0; + particles[i].link *= 0; + + element = rand()%NUM_ELEMENTS; + particles[i].element = element; + + if (element == 0) particles[i].color = color0; + else if (element == 1) particles[i].color = color1; + else if (element == 2) particles[i].color = color2; + else if (element == 3) particles[i].color = color3; + + particles[i].radius = radii[element]*scale; + particles[i].isColliding = false; + + + } +} + +bool ParticleSystem::updateHand(glm::vec3 pos, glm::vec3 vel, float radius) { + handPos = pos; + handVel = vel; + handRadius = radius; + return handIsColliding; +} + +void ParticleSystem::resetHand() { + handActive = false; + handIsColliding = false; + handPos = glm::vec3(0,0,0); + handVel = glm::vec3(0,0,0); + handRadius = 0; +} + +void ParticleSystem::render() { + for (unsigned int i = 0; i < count; ++i) { + glPushMatrix(); + glTranslatef(particles[i].position.x, particles[i].position.y, particles[i].position.z); + if (particles[i].isColliding) glColor3f(particles[i].color.x * 0.7, + particles[i].color.y * 0.7, + particles[i].color.z * 0.7); + else glColor3f(particles[i].color.x, particles[i].color.y, particles[i].color.z); + glutSolidSphere(particles[i].radius, 15, 15); + glPopMatrix(); + } +} + +void ParticleSystem::link(int child, int parent) { + particles[child].parent = parent; + particles[child].velocity *= 0.5; + particles[parent].velocity += particles[child].velocity; + particles[child].velocity *= 0.0; + particles[child].color = glm::vec3(1,1,0); + particles[child].link = particles[parent].position - particles[child].position; +} + void ParticleSystem::simulate (float deltaTime) { - for (unsigned int i = 0; i < particleCount; ++i) { - - // Move particles - particles[i].position += particles[i].velocity * deltaTime; - - // Add gravity - particles[i].velocity.y -= GRAVITY; - - // Drag: decay velocity - particles[i].velocity *= 0.99; - - // Add velocity from field - //Field::addTo(particles[i].velocity); - //particles[i].velocity += Field::valueAt(particles[i].position); - - if (wrapBounds) { - // wrap around bounds - if (particles[i].position.x > bounds.x) - particles[i].position.x -= bounds.x; - else if (particles[i].position.x < 0.0f) - particles[i].position.x += bounds.x; + int i, j; + for (i = 0; i < count; ++i) { + if (particles[i].element != 0) { + + if (particles[i].parent == 0) { + // Move particles + particles[i].position += particles[i].velocity * deltaTime; - if (particles[i].position.y > bounds.y) - particles[i].position.y -= bounds.y; - else if (particles[i].position.y < 0.0f) - particles[i].position.y += bounds.y; + // Add gravity + particles[i].velocity.y -= gravity*deltaTime; - if (particles[i].position.z > bounds.z) - particles[i].position.z -= bounds.z; - else if (particles[i].position.z < 0.0f) - particles[i].position.z += bounds.z; - } else { - // Bounce at bounds - if (particles[i].position.x > bounds.x - || particles[i].position.x < 0.f) { - particles[i].velocity.x *= -1; + // Drag: decay velocity + particles[i].velocity *= 0.99; + + // Add velocity from field + //Field::addTo(particles[i].velocity); + //particles[i].velocity += Field::valueAt(particles[i].position); + + // Add noise + const float RAND_VEL = 3.0; + if (noise) { + if (randFloat() < noise*deltaTime) { + particles[i].velocity += glm::vec3((randFloat() - 0.5)*RAND_VEL, + (randFloat() - 0.5)*RAND_VEL, + (randFloat() - 0.5)*RAND_VEL); + } + } + } else { + particles[i].position = particles[particles[i].parent].position + particles[i].link; } - if (particles[i].position.y > bounds.y - || particles[i].position.y < 0.f) { - particles[i].velocity.y *= -1; + + + // Check for collision with manipulator hand + particles[i].isColliding = (glm::vec3(particles[i].position - handPos).length() < + (radius + handRadius)); + + // Check for collision with other balls + float separation; + const float HARD_SPHERE_FORCE = 100.0; + const float SPRING_FORCE = 0.1; + float spring_length = 3*radii[1]; + float contact; + + particles[i].isColliding = false; + + for (j = 0; j < count; j++) { + if ((j != i) && + (!particles[i].parent)) { + separation = glm::distance(particles[i].position, particles[j].position); + contact = particles[i].radius + particles[j].radius; + + // Hard Sphere Scattering + if (separation < contact) { + particles[i].velocity += glm::normalize(particles[i].position - particles[j].position)*deltaTime*HARD_SPHERE_FORCE*(contact - separation); + particles[i].isColliding = true; + } + + // Spring Action + if ((particles[i].element == 1) && (separation < spring_length*2)) { + particles[i].velocity += glm::normalize(particles[i].position - particles[j].position)*deltaTime*SPRING_FORCE*(spring_length - separation); + } + + // Link! + if ((particles[i].parent == 0) && + (particles[j].parent != i) && + (separation > 0.9*(particles[j].radius + particles[i].radius)) && + (separation < 1.0*(particles[j].radius + particles[i].radius)) ) { + // Link i to j!! + //link(i, j); + } + + + } } - if (particles[i].position.z > bounds.z - || particles[i].position.z < 0.f) { - particles[i].velocity.z *= -1; + + if (!particles[i].parent) { + if (wrapBounds) { + // wrap around bounds + if (particles[i].position.x > bounds.x) + particles[i].position.x -= bounds.x; + else if (particles[i].position.x < 0.0f) + particles[i].position.x += bounds.x; + + if (particles[i].position.y > bounds.y) + particles[i].position.y -= bounds.y; + else if (particles[i].position.y < 0.0f) + particles[i].position.y += bounds.y; + + if (particles[i].position.z > bounds.z) + particles[i].position.z -= bounds.z; + else if (particles[i].position.z < 0.0f) + particles[i].position.z += bounds.z; + } else { + // Bounce at bounds + if (particles[i].position.x > bounds.x + || particles[i].position.x < 0.f) { + particles[i].velocity.x *= -1; + } + if (particles[i].position.y > bounds.y + || particles[i].position.y < 0.f) { + particles[i].velocity.y *= -1; + } + if (particles[i].position.z > bounds.z + || particles[i].position.z < 0.f) { + particles[i].velocity.z *= -1; + } + } } } } diff --git a/particle.h b/particle.h index bbd52129b6..6e55feebbf 100644 --- a/particle.h +++ b/particle.h @@ -10,22 +10,52 @@ #define interface_particle_h #include "glm/glm.hpp" - -#define GRAVITY 0.0001 +#include "util.h" +#include "world.h" +#include class ParticleSystem { public: - void simulate (float deltaTime); - void draw (); + ParticleSystem(int num, + glm::vec3 box, + int wrap, + float noiselevel, + float setscale, + float setgravity); + + void simulate(float deltaTime); + void render(); + bool updateHand(glm::vec3 pos, glm::vec3 vel, float radius); private: struct Particle { - glm::vec3 position, velocity; + glm::vec3 position, velocity, color, link; + int element; + int parent; + float radius; + bool isColliding; } *particles; - unsigned int particleCount; + unsigned int count; glm::vec3 bounds; - const static bool wrapBounds = false; + + float radius; + bool wrapBounds; + float noise; + float gravity; + float scale; + glm::vec3 color; + + void link(int child, int parent); + + // Manipulator from outside + void resetHand(); + bool handActive; + bool handIsColliding; + glm::vec3 handPos; + glm::vec3 handVel; + float handRadius; + }; #endif diff --git a/util.cpp b/util.cpp index 9d172aed14..c136e20e8e 100644 --- a/util.cpp +++ b/util.cpp @@ -13,6 +13,12 @@ #endif #include #include "world.h" +#include "glm/glm.hpp" + + +float randFloat () { + return (rand()%10000)/10000.f; +} void render_world_box() { @@ -20,28 +26,16 @@ void render_world_box() glDisable(GL_LIGHTING); glColor4f(1.0, 1.0, 1.0, 1.0); glLineWidth(1.0); - glBegin(GL_LINE_STRIP); - glVertex3f(0,0,0); - glVertex3f(WORLD_SIZE,0,0); - glVertex3f(WORLD_SIZE,WORLD_SIZE,0); - glVertex3f(0,WORLD_SIZE,0); - glVertex3f(0,0,0); - glVertex3f(0,0,WORLD_SIZE); - glVertex3f(WORLD_SIZE,0,WORLD_SIZE); - glVertex3f(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE); - glVertex3f(0,WORLD_SIZE,WORLD_SIZE); - glVertex3f(0,0,WORLD_SIZE); - glEnd(); - glBegin(GL_LINES); - glVertex3f(0,WORLD_SIZE,0); - glVertex3f(0,WORLD_SIZE,WORLD_SIZE); - - glVertex3f(WORLD_SIZE,WORLD_SIZE,0); - glVertex3f(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE); - + glColor3f(1,0,0); + glVertex3f(0,0,0); glVertex3f(WORLD_SIZE,0,0); - glVertex3f(WORLD_SIZE,0,WORLD_SIZE); + glColor3f(0,1,0); + glVertex3f(0,0,0); + glVertex3f(0, WORLD_SIZE, 0); + glColor3f(0,0,1); + glVertex3f(0,0,0); + glVertex3f(0, 0, WORLD_SIZE); glEnd(); } @@ -77,3 +71,30 @@ void drawtext(int x, int y, float scale, float rotate, float thick, int mono, ch glPopMatrix(); } + + +void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, + float r=1.0, float g=1.0, float b=1.0) +{ + // + // Draws text on screen as stroked so it can be resized + // + char vectext[20]; + sprintf(vectext,"%3.1f,%3.1f,%3.1f", vec.x, vec.y, vec.z); + int len, i; + glPushMatrix(); + glTranslatef(x, y, 0); + glColor3f(r,g,b); + glRotated(180+rotate,0,0,1); + glRotated(180,0,1,0); + glLineWidth(thick); + glScalef(scale, scale, 1.0); + len = (int) strlen(vectext); + for (i = 0; i < len; i++) + { + if (!mono) glutStrokeCharacter(GLUT_STROKE_ROMAN, int(vectext[i])); + else glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int(vectext[i])); + } + glPopMatrix(); + +} diff --git a/util.h b/util.h index 466e4a990f..46e1447581 100644 --- a/util.h +++ b/util.h @@ -8,10 +8,14 @@ #ifndef interface_util_h #define interface_util_h +#include "glm/glm.hpp" +float randFloat(); void render_world_box(); void drawtext(int x, int y, float scale, float rotate, float thick, int mono, char *string, float r=1.0, float g=1.0, float b=1.0); +void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, + float r=1.0, float g=1.0, float b=1.0); double diffclock(timeval clock1,timeval clock2); #endif diff --git a/world.h b/world.h index 2801003e48..e11976c1c8 100644 --- a/world.h +++ b/world.h @@ -15,6 +15,4 @@ const float WORLD_SIZE = 10.0; #define PI 3.14159265 -float randFloat(); - #endif