From 97e40ced57489f4d66d4b9bc9fe213c388e1a827 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 11 Dec 2018 18:54:48 +0100 Subject: [PATCH] WARNING: BIG rewrite of rlgl module This commit implements a big update of rlgl module, intended to optimize some parts. This change could break some code bases... hopefully not, but it could. The BIG changes to the module are: - Replaced LINES-TRIANGLES-QUADS buffers by a single one, now all vertex data is accumulated on a single buffer and managed with registered draw calls. LINES-TRIANGLES-QUADS could be used the same way as before, rlgl will manage them carefully. That's a big improvement of the system. - Support multi-buffering if required. Just define MAX_BATCH_BUFFERING desired size (currently set to 1 batch). Should be enough for most of the situations. - Removed temporal accumulative buffers for matrix transformations, now transformations are directly applied to vertex when on rlVertex3f() - Reviewed rlPushMatrix()/rlPopMatrix() to be consistent with OpenGL 1.1, probably I should remove that ancient behaviour but... well, it was not consistent and now it is. - Minor tweaks: LoadText(), I broke it in last update... also multiple comments reviewed. - TODO: MAX_BATCH_ELEMENTS checking should probably be reviewed... done some tests and it works but... --- examples/models/models_billboard.c | 7 +- examples/models/models_rlgl_solar_system.c | 156 +++ examples/models/models_rlgl_solar_system.png | Bin 0 -> 30443 bytes examples/shapes/shapes_basic_shapes.c | 17 +- src/config.h | 2 +- src/rlgl.h | 941 ++++++------------- 6 files changed, 466 insertions(+), 657 deletions(-) create mode 100644 examples/models/models_rlgl_solar_system.c create mode 100644 examples/models/models_rlgl_solar_system.png diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 8ce6a44f..59655714 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -28,7 +28,6 @@ int main() camera.fovy = 45.0f; camera.type = CAMERA_PERSPECTIVE; - Texture2D bill = LoadTexture("resources/billboard.png"); // Our texture billboard Vector3 billPosition = { 0.0f, 2.0f, 0.0f }; // Position where draw billboard @@ -52,11 +51,11 @@ int main() ClearBackground(RAYWHITE); BeginMode3D(camera); - - DrawBillboard(camera, bill, billPosition, 2.0f, WHITE); DrawGrid(10, 1.0f); // Draw a grid - + + DrawBillboard(camera, bill, billPosition, 2.0f, WHITE); + EndMode3D(); DrawFPS(10, 10); diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c new file mode 100644 index 00000000..21fd30d5 --- /dev/null +++ b/examples/models/models_rlgl_solar_system.c @@ -0,0 +1,156 @@ +/******************************************************************************************* +* +* raylib [models] example - rlgl module usage with push/pop matrix transformations +* +* This example has been created using raylib 2.2 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2018 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "rlgl.h" + + +void DrawSphereBasic(Color color); // Draw sphere without any matrix transformation + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + const float sunRadius = 4.0f; + const float earthRadius = 0.6f; + const float earthOrbitRadius = 8.0f; + const float moonRadius = 0.16f; + const float moonOrbitRadius = 1.5f; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 16.0f, 16.0f, 16.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.type = CAMERA_PERSPECTIVE; + + SetCameraMode(camera, CAMERA_FREE); + + float rotationSpeed = 0.2f; // General system rotation speed + + float earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees + float earthOrbitRotation = 0.0f; // Rotation of earth around the Sun (years) in degrees + float moonRotation = 0.0f; // Rotation of moon around itself + float moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + + earthRotation += (5.0f*rotationSpeed); + earthOrbitRotation += (365/360.0f*(5.0f*rotationSpeed)*rotationSpeed); + moonRotation += (2.0f*rotationSpeed); + moonOrbitRotation += (8.0f*rotationSpeed); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + rlPushMatrix(); + rlScalef(sunRadius, sunRadius, sunRadius); // Scale Sun + DrawSphereBasic(GOLD); // Draw the Sun + rlPopMatrix(); + + rlPushMatrix(); + rlRotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Earth orbit around Sun + rlTranslatef(earthOrbitRadius, 0.0f, 0.0f); // Translation for Earth orbit + rlRotatef(-earthOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Earth orbit around Sun inverted + + rlPushMatrix(); + rlRotatef(earthRotation, 0.25, 1.0, 0.0); // Rotation for Earth itself + rlScalef(earthRadius, earthRadius, earthRadius);// Scale Earth + + DrawSphereBasic(BLUE); // Draw the Earth + rlPopMatrix(); + + rlRotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Moon orbit around Earth + rlTranslatef(moonOrbitRadius, 0.0f, 0.0f); // Translation for Moon orbit + rlRotatef(-moonOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Moon orbit around Earth inverted + rlRotatef(moonRotation, 0.0f, 1.0f, 0.0f); // Rotation for Moon itself + rlScalef(moonRadius, moonRadius, moonRadius); // Scale Moon + + DrawSphereBasic(LIGHTGRAY); // Draw the Moon + rlPopMatrix(); + + // Some reference elements (not affected by previous matrix transformations) + DrawCircle3D((Vector3){ 0.0f, 0.0f, 0.0f }, earthOrbitRadius, (Vector3){ 1, 0, 0 }, 90.0f, LIME); + DrawGrid(20, 1.0f); + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +// Draw sphere without any matrix transformation +// NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f +void DrawSphereBasic(Color color) +{ + int rings = 16; + int slices = 16; + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < (rings + 2); i++) + { + for (int j = 0; j < slices; j++) + { + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*i)), + cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices))); + + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*i)), + cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices))); + } + } + rlEnd(); +} \ No newline at end of file diff --git a/examples/models/models_rlgl_solar_system.png b/examples/models/models_rlgl_solar_system.png new file mode 100644 index 0000000000000000000000000000000000000000..576510c45927c8953e87e3df2451216e9b05a0b3 GIT binary patch literal 30443 zcmeFZdpOhoA3r?CHit2hn9~+Q$oU**(}pBT(vqCbNXVh2+KizbMo3Ccb4*H3krWZj zp^veI=z~-gl2mlO-<$e=et&#F_aFClU)O!z*Y*9!b(QV?e!iZ^$K&~YzNERj5a6On zQ4k0OcXY6K1A)LO5J=!53A*H7=x(D&#g1zG5S`4J5h zDC4*)!>JsE+<*Nc5TD#ef(95@XVBY7u=M}(Lq9zhgO_gq@4p1TP$Nd=$TK+h*8e63 z<_Z*`n7hp4xZI4J=~E+eVTp! z3S+9X5Ned-9_PCAZ_*a||Ehn$Fl|bLterLZz1{z&V$cMZlI(uT$4IR6ZAa-9t~|Wp zRLR)x8#Z2x=x6si#@qi*^AveOmJy0o>1Qm2$NBu1ACz#k{+NLLuCRZ1AV9SeY?7bC zze&$L2xHuXCJnrRuUJdZEPiU+C|EKkX_Vp>=lad~7N`DSY@%u*gbxOc`s@Gh(6jOg zY`I3OVy%jsqN#ehilWgpywN{{OkN4d@Ettx&AA zBmeykcpRWL%!)F??Te-Or&cM}Nh|!ZssDZqS6lum_dfmazfPy~O#9*8vs=Fi9Vh?o zb-<}+rTB)N3;Vav{J&sGUW}IgYo4G^ZCdxG`2UFjMAZMZz{@ND)58C>@V~skLstLO z!vD1J2Vnot7yf_b3vGtT;r|o-Vw_-?En`G?G+(?pN&;p6AAAVJS;xvc|9o5s2WlRo zzL;v$Fhkb=^WQ8qMOiTY@PpGoY?1%Rr-A?074j((^9#0c*k_A;`_C6t_4tUh^7MbO z6-ZsE!hmI9scl$T@eg#5Sm#Mc{-J3U9L1w<6RU1BOXMGH2m-M67XZwq{K3HOXv&eY zL=T`?0Utc@53U8tti#IxPy-(r!b;n&%9{Vd(eOXwgu{3QZ0~=tIs?HBfFyd9`(!*h zn;!Jw&udPsE9ldogZ1GkV|DOyQnVy{_6D2eq4=9aWCLjSB=EeizfrMx2Lkh6&9(R3 zNS|=po{xqPIR~;iIu^fKO@|dJ`nuQnZndI}JML*%^qs6& zdwku`KgV;1rH5lxcAfbBc}zE=?dcrTBUtEJ7}O-lj(&HPNy0Bi57dIq3oHLoQ$$MJ ztT>zW_IDN)uuKAJDS_Oq#7c}y=6p@#aVm$GHoQ}gvJ|Tq|HyGR`VZ&DoL=|NzkUla zpedxOL)D>P9|##W^8FEiv^2?Otu$91TSJQEKGAgN2}WbTj}LH4we^7U{e8Gp zn1B=EzK98ZF3_g=q*ZFnFCXKp1vTpBb%Hxhc^P2``}cPlvVtrn21n+Pa*tyvIdm;k ze~v;G?rDN2f#$JDc;PM~N+5=xEzetH$%p=a1s(@Fxo(B7-;ZJ@w^gF=6)Vo5ONbqHih)xQ42x}HsL>3n)KL`Z6cMxUYDk|iiG9gPT3aj`4iVo zNqp}huEz-nO}Vcodf10CBqjg$HSHnI^UV3@dsLq$FfKXFrHjXWB%G`GW3S&Gjv>Bc zRksN_%*ptODf%JnbNdR#w|sOwGOrrv37b>e=4=0?PrNiFqJHF$^@R7Sj`N_ia1nA+)4GMW9<2!fR`W$;tk}D2Mp~6_4%^|;aL0{Lz&?^75H4*~x>Bf6oIGM_5 z8b{LWSyTIy?`5CzDprg(bfj|AcVrz`)?NG)2Kwufti-Q7;=KsD%sMDD_1Y_O*p0n* zRYC%9Z-_S>*c|3-BlAO_*!L&ieMI>^n)vnnYKq2$bue=9+YRC?eQh+|mKJnT&m@R( zhMJDfolnXT&!t%PEX=K@EnBXLl%{a}{?rY+8{dLM@Az^vBn6@rUUt4!y@TF)wuSU6 zrG0#=?ZQ$-44$O)3}u$>57i1uQa&xGJNzHnQ}|iv)Vl1C5vV(RDgAX#$fOxl8O}mm zu!Qcs-1Q;}FA=E|bF$Tog6KOh4H$DG0cH*1sVcgV-jZ)BP+W z>G7VH$T-mjdJNeFm2xnJ=9;J$SSh=$jOQ9WKz zB(GN|A0U2#?xwLN-v=~t6j$0=r0N6k5vWB*SN8aawhOL~))I0Dp$uZi@r00Lnxh6p z2rbKJT0r_A4@ki48>-la)wWm2 zD9eZXb*Z~9<*p_p=g!nu{4Tlz6qVNcSp(1K#E^_?- zfDZ?e^*H9&DU_8Eslf}}x@X7Hlc^zyYjNfgCeEB6hGQS(F3V?*_@7M6hzmG8?erJ- z-fhK$%EkC#CF@2!09HKY_(tHu_z$z>Co`0V5GI2GhDzw?=H+pWS4bSn<~w#fTsovj zZ*nrb2iT;Na5oM0LjzSGT(T0tXZvs7+y;R{p%8Lx3I5zmzVGo+8=$>hzA7>9Ks;@y zB2lP*?tJPb@^NXbD)gW#Tn$o}GAIGnakUGwTr$rrt3}HQLhsLOwCu^yoMv!>>FPGojQFO)63?Hx>&U#Z^NTwjEj5rtS(MTyga--`7p9FIX=W-s{PP!`Fx$ z8uKJ|i$xuJ2CxqpddWH|$Xv4_IC|Lscz}+phR|zi@@4bnqx7W0&y%93qLvleI&L4$ znm?z@cJsfls=8iRPw^5xNKueW)<+Dv7I$fC83duK&~c6%D?d{Nk3~P|Ug~l040naD z6>{q{bPwg6f6W$&^qVj4GVJ9%VUyI?52?IUNjSoNcZ3 zYW{r&a$)45$fXg|8~@`$nlt@#on^vNSiw_Ts6O$Lc`@B(?VI-;GfMMjeKA$bLG_#0 zTlB>BBi{x@76iyak-!QwsVE1$q5PAhg}&{cx*@7=$fPJxkGk1xhY`!o^-h0}SgNk{ z5bAASWqJn=kc7?nv0{$n=PpAaFAF7uZjK5Pi}Zy<3pmsDI-d2#aoa0yQxy+)gP%LB z0P+QFRAsurT9-Fy2H3^6TMjc<;Jy2~5-~FgPQ4O>)*a#XiR;q+(_4 zNeH61U3Dg`cvOPC?IS8+nD@Y-&;+xJy;U@d?gGcpBgyrCN$lx|`-TTKZtcg4t16t zBr7WIa`G-oxvJUU!rHiwn+DdS#YqWH^_3Ci%Gn8BwCf9lm9nmNKA|(l=GtV7I`8Jm z@Sk-~!WD)%cFa|nRIQw8d$@`*(fVVs?55Aaqh ztR0X>1gcRwaa@Xe5!ag_{4SkqD-Jri-@^l62P@fs!Y-@!sBs=Y1P4sl-5bCIbXV9w z9S(D}1ydw3AY!|+{in3drxMejyqvxR0_gol5yJJ&{XM6_Bct_}>JqYBIvUcEyVi*x z7r?&2#ON%Lc+d#J72;h1QPKG4!dBigC%R~|-Jjh$4zr4~IMafMVXn0*Nn^cvn8nWg zEFYuI+{IM!XNF|sN9*!zS(gXEm;ei6)b>#ZyTn=-Hh-?gkfZHW;R@Kd{1XZrpfPhF z?r~>R5$BIqL?+5=Zg=QEjIy5DX2B{y#H!?nE2VK=bH0G)*Z``9z#R0n@~m1H9-2P4 zb6z?>CWS7=C5PKK>dP-Aq{~W|B!L>A4>}%+K7XG6c%kMzK)uK(z+$+l9;Ns(lfp4R zS|O?ML)hIE;E^aH%@BQ-)m7J5F1uSW;own1csF?BXwsq%vjmr{r$_))_eiJ`9WmK^ zx<)qMYe}Rwb-X2p@nc6AAqnv4g4)z8u^DB{{DSv&y;e5>wHibRnJDzlzQCv$lr$Vd zee=!GEjYw|dmQx*{#;W}_lN1&nw;)twupe6X^C{GO=GmnxgTz9C8@BrLfP_I-;WCM z^7!gphu&pB<2(GYK>&1=m&Of|g3%HJe)u01hd7CStZsGh`uXF`6cP5efwTim|C1rF z?f`X*^a0}t8@MJd7$s>vXv0?aQj#K;ot^Ts$$VYL7u6nClMdm7e$WCA)ath5_0YrE z6)79ySW1{_SQjmyaBA~6IQIi612n`sW>UBt7@u(wpctPJj)AC8uR=d=5@zM zlMbQdhylaDivKDZv^+!WD-Z4tHrC+76@7jFOfQjxVEO{&Uy(9XxvzP{bxh@itDUK& zsoU9v+-LkrOueEZ)d%P|$KqcfB}Pju<+XA@ zKPQ>;D`T5D-&?N(Fk@>9s4`EzG*7AWYh{Q^BS42ErmJQ}gsm%m9YY90L4N~zsos+r z1arNMF3OGGmC)p|FiwW)>3k=AIh`&m#+48la0BX$K!YW>Ghe&puH-?2kW6^V2w`y^Vev}E z1cJj~IGLFb2Od|n4vI`XqeMuh7!NKj+8TX_f{X|Z z3^XrF?Z3}6Vm9U+0`(~zfene%e`U2|NWHRlxxg^H@Revo?<8j&@LyFEez5VTs%)45 z*({e+1Ts`<=Iq}6En;6y$&>g=F^_;>oV!?p%8ssJz9!6>fiUJ#hwgmYkjNX``gV`Z z$0!qq8W8TpX8(m<|>B>Vt zS1m1a`{E}>y@_$xYmzmV=j=wtO@O(yt^`S5aMh(#FlIaHM2Ydu<>awIv75DtSJqB7 zU3kiB27nVC2TND(uqwC%ZVe<&3eJDW^tU z8qs$I)w|E+x{s7}p~F_1+sh-3RRGORV41!k5dvvO*W7@q{dbj14jzEJrC8WR#|PcEknX4^(7NO!uQUL8Jqeo#cdLSMa{ zWo-G)fC!VYhcS-fEG^c~a+gIcbSgqr48F11S_)cPp={lAb4q0_3!cXP*Tw#nDlXt; zB`{0HJF7c{uoN#9ts=((zpdWmME;uX?Q6+1pb&Jc5PL3(GVb(#nNIwiU0a^+X;s;9 zFNFCpw}4vAQ3SAbhXLRB=Hl&>K@C;{NtVLg#aYDAhvL|7|B7!?hXqx5DVYM67o3U3 z_=I=mKMOg~HMR9G%ydb#kmf?FxUg+b&P~!Gy^Pnq^qIaMe-}Dzgh&%27)eb0b7<{V zMXR&}8>rfm;u~!<0X!hq)UGTDz)N=juUxvANe{@ahdJf?8X@|Ty7ZDH0)R5`MnPwm zoh=}qi4Z2)VwtU2y?Zlt6e_wqz9>F%k>EGI!F_Ho>!U%z5A(t*pdP5vL7KZaU%=H% zfIa0Y;}KgPxr}7D*2qiwtw@xpJpQC{+gDI*cl@`=`g)+pMFju{z$>H8ZeT%w!!|f) zj=_TyUT==0RBRkb!arUbbqw$J*QaZ%E46Y;_p^lm_7N2z&I=dEogh?rZI*el+Ox$g z5Lp^KeM}l{HMcKoU@(hS3IJ*GfOYHq4G?%YF;W6N+7C>Vv8n{v&LhPz^5eM#23f;o zYasEt1+Q|fK>Pt#DuQlQWO=NCMU9Bh)4cI}h_ADy|LPobB)8&Rf}w{bO%@b&g#nhs zT*rivlrWg9+wGj^BJ$T`bH>(C&#vauB{dCl#?sJFpYV!*v01Jas>9-F;J$}L& z&Eg0i)lUJDRM-xGvDhDQJ*Uu}o!aqh653xvE^dL)idfA+XCKfse~sxCQ;bTLdsH0U z4VIm(ua*9rJ%w47p82~SQO-+D55qN+Kv5b=$}~N;uc2qJH~9%2b%C%V{&%+f=gfR# zpbh{XR^C3;VEZL}98|SrAu$u6wX6+m?cKUin=yBAvOmif4dnhETcXqjI z{jpySb>gE%2NswF|BGD1G-45Ar8eJt>YTT7`{3s4pfAwPE`*&090W>zJt70!u#^#} zOk&2d0zz741SwGCKE$?kBf3#E9`)nwEx*#()#flW@$rX9E zgE+GDCQu}?ur=Dq*Hmt_aY4tX!>5dien1MVUU#FeS!KZ)cT%pY=i^GR3XVhBg~J@P zOC6i@MmQ#9V?zfMT4C&l>kd5RBs}5zp3*kH+vNbg|2_EPVMpBy41k(_kw)(R-^T8moEx!gy^0>aCJfM*;HFFi8twZYAq2 zy4C?>fV64iZzav1xGLNi?zh!|xVkaG*5+5@R=)uyl_YNVhQ}qVG0b`O((}f=P@AFq z!5C3SmbU{XON&4&wQ^k=WzJ>8-q3=>A`=@85w8q1x6}8^4Uy0EkkX8ig3R@555ArP zswTKa?9<`ij2Hkz_CGR2e3p2l4@tI)e*_#M3vdOo=BXC~)U~6iUOf5DL2M-bmUu~! z2I!zQrT-S{HnrwL{wK-mcNaN1Apz@6I;g8IkQ@=+`=r`B=M0E6FO+0%D>K`cF_HU1 z#@V^EV=I@MdeAk&jr^VmdMFA#XiSd|tkyRBBV1SE(JJ!IO6{iD{jjhtax-PwZkq;r zZ=p`k9)hOldv*Mts9e5=kgJf&$tLR+2ST|nPjJ?k`Ia1al@N%sLb2iacvCm%pf^#6 zpuS}xeVED$>;w7$n*?=O0ssRop@VJ;UKm#O1_$azm7d*Ri_em>N#9fcOv@=Lftlvu<{_6DLmxk>b}lftVRnaTRkdiNEc zq_ECJmTq#J4g$J>#3pzO0#ydahF=sGRUzl9loZ%iLkw>kZaRFxLh;AWjM@!{ZO{$j ztbOH2>x~souUE0Bo;V6JPsk!8iY()ed+hBxDJ>d`4SsQ6VSjGd2h6CLm=QMP3eSh9 zOgC7fW^&`$Q-+GYk_mxZ)L$v9`|fg_FqK(QFAG=DEyk}Wv|}J%yJ3|e>Eb8m;-t_) zd+g0P2s6n{gNG8mnoQgt#{ZGF%>;}+m*DI?Dje$R7P_XP^%GT!cA0O3YrXp9^Tnum z#igod%dofK&@<9w6T@}7J1AdqD7zS{b<>F^tE3#z@?kBM`NKJE2i^%Pbhra3lAnNV zT_2%vQwiaQoGziaWAZK~^=P{NaDF=p*SuVl*UR$VHQ{Cd?rvN?SHY66h2qM*VYpjD z5h8vQrbaZNV(lXL2~?*qyCaVI5IHRWa9`zKtLMevMgiPkuMD7iUuj;?DHvpuId}0Y zXCX0Q+mNx4F+HA;w?Q^4FhglBaiSrAb4%g#3|!S z`JAs=L$86;=Rr=3y=b2u_X?_S7LpY}~Jcq^Yz-$jTDgeh=D^2~BquNKs%=dTsIp1@{K z%m!-5^{QM;oDGG~+z%qs&Ej#26TbDCp~NIJ1M0b3-id1$J?6sdlN48Z`A59Y-GOYk zqn|$KdizpgvPEUG8oH&jA#%gPT4l#bYXIU^d#9X)3ca=^=Le&V9JMs)ITJOtc_RRO z`fB$+)wRmk*-0+5t6`MXZko<_5%JCE2eSp8kY%b9;ZYate8i6VS*hf@V{<)xaP5mc zx}s#Qh@%W7C#j;3`k&kH_hT18rAJ*nHIj%$wo&iAo!%XCvnpi!>T?&Ab(NsId3K{m zkW1>h&zIrf z(8iRqr<7keEl*;;0&qgni|=oKS~*CT@kHV;{+-f8zztb)`%+h71*wg)}Wo>mu7ndSnaaqFS+fElP3n933s6cNCv@2n0ylu+lwt?OnEfslas zy70RK3>CrE9?y8=)Z0(hRn>1vmi!I19Oo>Q2L}hJl{Xi=d1OAJi2FE8U6K!ZDupT& zWbRWp82lkIc0BX`d$6^Z#GB(9+}ov{?|JdE>l1L;+<5`?mIs_`s)nvH;;lip7+-p* z{4i$tR!zHZ!Uvb$Cv6U&zmev4Ch$BCf@)w4Rg`=9=Z<4j#y+1nti$%xSAAe78Gk!A zf20xXTxpNqcLrHRSv}z4*{u=v;D>FP@YCfUgd}d%rSb2Xx;N)W1=@#2ihvpw3ES%>G$j+f+OxSQn(K@wj17}r8QYG7v~mkm9lS3?%h*0 zt3{XR@#GP{#oB>Gl=M+t-Msfy^`YY?lLqgKmR!v*KkU2nAiECLQF(K-i3&U=n&5cI zg1DT+oAx4*<#J?W;2jQ5kd)C~{o*E!&*yGxC3G48L#MDiLL+(iw}T9~H**4*E`Yf_X`t^fjx!Kly$eezL9zhd`-o;USjAc@9%Suf-s$!=L$q|C=zbz^SDLofZBGjPkQ%CLK1Z#MGXmN=Hf zb9;p7UP)_()3Yw6apnHp(_93~ck{(N}h z=iJnncJD9ZD_f$8-R-1_-46r^VUZS$h5J+PjRqMW`*Kr2t3up(o1JEZVp82dy^?B9{`D=tGc>NFdX`%kp16I}PD@%kg zwyi^xm^Tlm+$^)a?Hzob6E|$u{4jJ(RAypZJn)rS<%VA}?Q?PD{?k4RCyL*Y`W)_@ zsXqXWcfi1fgg{WhlE^{T1Qe29V+zs%CPTWE{$DFd1n$I~z-+#$)#SgfX= z2;qXyo=Vf=eX5s7(s%W`fDGhMa!>a;Nm+#K@&9O1+!JUlflzAa1bVWRlGjTg_Qd7k zjGE#ATkCvZ-xtl)rHYI=KnPe;8259q+cvLFpsn^!GW*p5eQIt5CL zirX0y>?7azR;gk`T1|d{KPEF{{im@A9WzvVg&A+Ib#$4+~K3*8^MRxz-5 zu8v5+Ocqqad@*6BEDt#zy{ozlXew9v1}6OeH4j=SY9U4U(dJ6kYvk^>h;ImU5$cNX zyL*V|0(Lhqh&=spLGLg```Oc{ftX=Jzq}AOyef z#md+b0Bkj6<#+A>j4+D8g^X<2dXh8+S=lwWYAH8)AnTj((g8%kFSo(9HV?wFsHkbU z%giW(Ts}ulg@*Z3s#FcVhNek#;=F+>48B(|>UHscf9Zp-Atp9+qa*gQ;MxaJOL5HN zwQ>apQ23&WaylPCRV0#6 z&WywOyil$XJfc>b49L}?DYpfU$vS63@Uy0d;)Qalo+?Bx=DpOkexue@28}` zZfB_Au>v;nQs@kS`BR^%-f5!NV9rTnfm6odVX5NamkQ5Qdoao1zQ0l5pfrWq36X1Y z@j@nJcq=sEwZ=j)Iw%8wbB_>gL%O;Qpw-#jmO0LB(#C5|hC0?06zz|hcLj1LA(o1>PY2q9 zC95ZR7l@9Wl1xD`yO*_BB9J1lzmz0$Rydv!?j^O$e zT(a(-s1Km?h;&k>7S%)KQm!DG3$I@QwM{>iYNBQjaDR}z228i{=1ot>BVYo=XiTc^ zNz&-5($;W_k{Y%OVjgAgV@2Q8%(9m>o9s1W&H2_lmj>|)^X%m-6;A_E$YAaB4t^nx^sLON^!@zV9PzglA|ppcm28W)B#l)*^+1sTNguk2|km+ z&k~&gPi60{VfTOJhI?hd%xs^AxGD6m_AGN&QTJUspQc@V9rV6^g9=MsFOvUMW-iv* zSG?D`M7E&hc$!R{YaJ8SplW}v%d|NhwZ+B(DEZ@tNEHSHWqSWZr2;Up^`Ld2g=!WA zit#_0_z}jm1aVlUGxu?4ZuQ8+q=MSZxpxb_;tk4aF6aKkI83GR9QP;va`ytSp#h%6zFU z9q~$um?YBBXqR4(p` z@4RqeZpuD7Zh%}bdi5|vOg&z9csBf+v5w{fg}Esm2lTYFW3zwkcJk!EtQB}!=M~D1 zaOL9IRc4Dn^Q{!)flsXJGwqqL7cLu~YN88U_?j0y6wEiR$liwE>Dj|4Ffg|PA2tj; z%1+)P&n4H zB~6@VC)2b}NkkW!5-pANc}q$#HvvsmaeED+gS0cK3F>w~6FEy|u$1h`*7xl)8Y|oC zIodjI5B*k5S2$b4)sjI`FXSrd%8lPe&(f%xW&`ieXK$~{QiOspn$|r10Q|)7Wb+uQ zJi{(maPH}VnmY&%i5sZnWaw>YM?%#V1|s(jvqEV1_SAC@0MIm66Bs-jp^|#^;$uKd zd3#pER;Ht%>fxw35!uErlU6HGJxtQ=BiCoQ0;_bUjo$!clx`=Q8aryOLTFZ`iPTzuk6SM_PQ>#YmM?ZPLpE(z0OSsZH>+ATH339zB_ zyI^7#nKAYOW%g0^mV9XmsMvTreu9d8$rY9KRP|vFL4lMEr`pL1Hsv4QV~8=4`>0zA z-$hRD#x#SQ=UMpHUE#-FpDWD-mA6l4as;zKqxCJph7Nn>o0DO>__D_#ii6j{_AUhj z&y||+kMZQ=lfB+6PF1Wr6l>p{tGIyICWKq4+J&6&1V@(2K`+KT+4#HQzs1=O;*!`C zXA6A|3kKhkLY!Lp!86UZ4};JHT^Rc-_F15zVp_r?y2!DaraTmP0)8xPw-^&1x$#8oBAjat(zoPF}RKIatTABo*J)_3(yd?8@56lhBW zXT2o-0Hz}}OCcX6M7+s0*sI;~Zw_MvMOu4}!;~29GG;5{S?#iBBZkXB=WuD$5@0Q~ zs|YJtE#5mX6(_2F&_Z6qKV^Y#y{t%=CHL|TT81+!-(l6-Faf)DINy=kVyAyw+ zMt3+%-MAghX?ip~phSvm6SXRMK5Oumrd`=K4QY--Rs@(Zl(O2dxB#m}tfwzsVFI3n z090>%yZimkErHCA02K5h8V&7mNB~ zB{yy`P{iddX967-fP45=2CD6joJLNl==cg&<<1@g`H5ZN=vxRs8LepoEr8_@C7KXT zBwiyhhZB>>*-EK~!V@lDuk@-rWLk9;bQa|@R2=#wU$4sRet^XBdm%oerG%FoPN;$R zzareiZ!_5V#3xcLEH_;l4#7@gZ=H?T?37%_ukiMgpDz1|3CtL$y<0uRxHPx#=44%P zO|JIJd@5!B;hoO>7vb{aAHeS@Q=uwt%ktGjx~*@2adOVz3ie^!$e6O);1+{64i0$M)UPi#OBy zBwDTTOaIQm+bz*X@wn6PzSgIS9cu+~_hi79Kt`lP+MtfGdS+bTdV`Ni1~Ims8cbFYepJiWS?+8abR`-ZmXR_12Oijyr-MN+(?y6EvpOX?y8k9GvQ{j@oXXWcP z`70j0xXT496a)tPwhMSu^_azo*j|!|!!Le5pt%#miZSOzp$7vU`YO77qQy)s1DdvK zxqY`n?jA8xZ+IxYUqdM1-E7Vc4!OVsjnO=k4YUfp;daLpA3p%sBhh z>d=ab$?}qqmO0~wMA1PFo<2I%N~g3HXF1Frw_qYg>2~k-8Zg8X;#-6d?=V5`ZnE06 za#kZj3E`eDr_!1|LIAsC0(4sgOz zMvU#NpFrc~o&SoGJnZS)+q)9x^HOXRLlJ#v;2L0VY3p8V?A(cp?jcf|Nj={PeQr88 zKz28eLwe599_!XP(UqsL&VfAn_o`c4R&_>WFxwOtlF~iqX6PWTkQ7-ZL_rHQY6)sR zPbW7MdV&7N_Nl*oY1K*w#LaGcLIrs6vE#|0uwknA-uWi%(VBh6N%*GOi=vQF_3d&| zf>9#_gVvqKtH?&Kk)nA7MS#ml(q+l(>JflVVOZOs7=Szm2Yf^f;$P|d{jeHXY&XT( z4nzt^No#uHJ0Mchw9;Kh#N|Ewd*bwv>&Oa-*^Nniy>aL@TvER-bG9sIgi$MXO8=3x z^+U-1*p9q#%-fK>*ronN3NjOJlcD)UsR%A#otQS4zx z?+nCCvR%`({oclt^AW%(bL!BGOrS_xp#oBX#TrPp*!lKpWzWDJXHU-V5jel$r>go* zpoU-p;^=H>sbB~!0x#xnIO&Isw^T@d0*&g+$hT@5G5|Du9gHPkOh1Y`hqQ>&YzeNn zN3QR|y{^ocg*!TJt={uxa(4txD}lr@mk$dmuc{JLL^lO(UwSl5(g>*U(J+IzS#Aav zV%*{OQN3C1xz9qLY9XC{!DmMGKO;PtYWIr!+;xT7SJXra}jU5gVlIp5K9To|i z6MxzDcZpPd6bJ=vIJs3eKGNW>T4;dB95*#?U>+KUEKyT~`j2ieShiFT+-hMomNcD% z4BT$g`*3RZ3g=1JBp;o^97}U$^tn2-KJC`y6S%qDAEX3Mc(l0bJF3lB=Wq?7nL*Ad zBYW?}Ue%jGCw9sytCOzT0MTLe1|cquOLRsYldL@Xb(XV+X`~H)J<(Ktm6OSyTpv0( z6_v;u*UuhjZn|9J9)){s&Gj_ejy$|-q)|DAt0*QoLf4-@b*R8i`*h^OH&i*WD&D19 z3V%K|tL)}tQW6LB$?xM^HsOGKV^%0togW+a+6~+--c8mB(7c;Myd(?tPoPd8t+_mg zbsj^N`VvAkgodTiv3r+EpDc`lYY|3Xone+5TUrNOoE6C@lxMujF?3kotIjTAU8~^P zn61UD*Me3NJ?%8z1bpv!J8;KIi6K>hyuC502Af!rcy7#&Ugc93tbWd$;vedBep;D) zr7*Gg1yxkVEAN@lF-dmO%#GXSff@nPmBW)d5g+=)2kJgj6)l?vx3wZ`XAP2XDI&wZ zY~NF-tlFs4z5gfN{xaO-i_f`BRHq?;17rBZ6Rb2!r$aJ;CAFV*M!!j<8Kd^hnrm}6 zvZi|!V~mX`MLRuvy0;1_St=ycfanv$1Dd4lmuX{>_$S#dvvbGwjkyE8UG0qbz8OSv ziljxLdHgAZfk@MlaqwB~05dq<*m_J-zJM20>65tt{`u)MC*GbTRJvHa11jXfk;o6Q z8krX7`W%ah@L}RvsYqala(l*;c=1+jKJb>gQ!31a*vU2l2wCO%&hS3*#)PaF6{Q|$ zSu4gJ?e5-^!{!RJ5;_1ZszLb3!@mK&Q8t3I@oY^Z@}jNlB37jc{Y(k?%OeJ^ENMd- zd=12M6TBu*$ zRr1l!*~0PtD8jEtAwJF?)|}?`#dUBEWn85{@(`A*0A*dmUkduq_*uXM#EG)Zzbu$J z3=fNMPRs2{cQ@m(q_NkP>SKp6;}IfeKi?d5?bG5Q)&ZNaNz_evC*mNE!d4n^(p(R zv*~VToNGD70dpdyAtDDgk_Iz{bc1m8gKy{_Ka17oKQyOZDUzIdd(NY`mfdegYw+64 zZ7UP}Po438>kWYv-hbVFdiXWwJFAb6yJ|IZG`rSmX zLu7Q#hK|gx&&4{wnhBO#1Rr{IEZFSLePB zHJPx=Zy-{$@@drPol>H@Q9Xe~!O{5^uqNcwce3-f7SU{l(;J1jTCkTz8g*2Nk)E3?8jm{fD+BDStY3^e4}O7Y+lS_~ zWM_Fh`cyIKiI*xOn>XHels)X9cm)pk(>@;0TVk||8nn>0LZjlQ3r{eCB_Hd1Oa1hL z_$`qA<5Oai@l7JnYqb*sIha@032UMH`9;jtvS3g2g?^gBPy9ruz9{_wzZ9J}c2r_e zZ&XCYVCgxRW`RkO2UW7G2FcEZmWG^t0eAy>Mm0q`9_ScY+0QOwy_L;eizHW!g{M0p z&XDULsq_@kotv z*#{Hzn&cr&G}dR4`OFRCH`whncbtJdGkcmv&=3mJ(EW0iQCJS$2~1-r^v$T9BqfiM zFmsjkz^6V>&aB)FC7p1#tbW_m?)3}a*eD2CCznT?^ni^eO(CC*;r7Af!W-|=e7R8T zafTI{roIo}R&T4ORNAt517)4hRNM5}MpYW_m?d>~nt31#=Q;im#V`=s`^%X+Xw zLiF>bnD>Cy^)Ar6RPp?T)a?g5_1SGc+Ar;Vg{vf?IM?5=nPeQ5)Dqes$rb2*^Pwq;`AcOFyM5`tW#pv&4+ z(ib1T*+PuPz>*(fk~S7Yo?!O9O`2C%zfmzNg61++QX#g*PyWKhWX>vi00hG)dBJDb zd+<{!dA6b0;RES^9RkaztXc-po*rWDs^%bgS4aU|S_oX?nSDx8Z$K%fFJ24QNXlKh zw%;>*j46L3(qUdxr?d#zsQ9olpTzKc<`ccC!IID!ZR60N(pjeKRJGM@S=QQWbLZW2 zvhYw~*2a#s`wL>L6>07{VKO#@1UB6GPB$cY0Jz;t->feD26)7_U#_9DN#je`V*FJa z|Dth)@S$5S153onlOdsn8fKExm)<=7U}mB)+<7@+ z$JxWA(TgP|U22CNtxVoj<|LS83U>P8`#p+wN&9oY>K}pk-v1Up#^`*@6YA@E7QQ0MoP&J9I{mq z6=xyDRwNeP!vZtx8u}*98>D7;T8?PJSe{wsoY;PiBv~U_{X2X7J_D~hhx4;kS=p-L zpj8oaaZ&5>yZR?to%zq}7ZaElq-TaTXzKTXiwxo{3S>Q*dB^~GIa#e*VX(w>gYR4D z;Gv`-Ea`w$wt_e6)XLZmOV1&>+lz@Y_N3NeuRzgXQg&yr$%Fk(_c%QA00w9tVAgQF*>~d(n)s_0Y6c- zc+(}vBkq#+)U)9lCT8|-8!9`Kgspc!rmh9nlo|L9nK&p}>pfO&+-sKwv5AjY>vD;Y zIm?)U)Sh<*N1}QR+y>=z-}hm%2!6c+@fU72Jq%@9s^Cg(HkYXy0!wg5jNSO$n-!8q zMpLSMkQJFwgXEu>7Pv(pL%X|uX4_1JVk)pZvVHox8uYH8n(2?B=|OD5MY;X{XHdvh z_F`u+F#kR4Q>VPT{KD1If92-zQl1!CK4>;F&AV%`uFf>K=XN`>E7SBDeI3&8HQopO zm4;e*ae!xFRXnD|x7>jb>*J1^4}5iSaoaHM3NuOw2}4<;nM=w2ZQ}M|mHpp&Z6M1X zBl%v%-G5_DOFo!*L(c)1I_&jS@g3r4Dop_cu%FUKj{w-xbA25Uz1La@amBjoX#Ck0$X!)K&&|>LG1~_^0?bQ{;=7D0q^Bn* zL5)$&Ai04bV%mENFLPFM^$Xs83noyQeTZ2LZZr->^}<&KirVFZ*nwR>yMp;s zB;bZTZ%@MUY!F&s4w=@3KMJXO&X_Q9H9|KHACr8oPun*MzF2fSS+dd>e@)X*JNZ8U`Mw;;U#PSNdyMExyyA85^5^g-&L<1aY_MWULIoy*h< z>1`MjqR7Tc@Myu7M@Lo`lMrKX3_M>-=cYoB5mg&#_Pont!2Qh28;i@IJ1hWw&0Ewk z#SKm`0h6q}z~SfVAF1KU#5~Kw!o_rm4ukf9JbNkh^W9B1Bnt9A7P(I97xpXWAr=!9&cB`CQx*zyWi6HND8*EtrRIl&n>im&3xH<}yrVW}>E0|2j=(oVR zQpHbi2%GekmOj@!qRAnbTtLnRs@=^PTo&Q3jodP)0;m>4j8Pk3IF}B(GT{1UjtfZxp)WPmKRCVHb7$(Usi*%rk4?xNtCMyM z2V`cjqQFfr$15y?f2gr7V}AoZDSYPuGiEIazJ#jMk~(tf9ULRb?Y@ z<;qTW<_3cEG*Y#?A(js$hNI02UdGgqn*3*fm?i)h2m@wRX23BLtNaw$qFJsQAt)>5 zq;W`&N}sP;&XCgWB5ALkRSt)ub}W`fVv_+lSJyQ7%;$+TP5rOwtHQi8N< zL%X969Y|xEL327?Y3;V5oI=HH$k7^88GJ|h@$wdqVb;A>rT$HL^(@l3bj|3Xe}&NQ22l2d9lXELVI zoJNwQnnRjnh(wAtrZA^a3DL~qL6KGH@R&t%C}T;YCs`$FUum7|d4Biy>G%8o`eQG> zUZ2nXx$ob@*95b57UyZJ9?8VZjbAVIqbt-_3K;mc3P<&tH#8jsHJL=7k;7Wd7Tf>JhoI+>+rVYK8+y4Q&5!8K;k(qdKa#+=6oywEdQ&t$pz z-N6{3nC3Pin@5n*Nlr6?K>fi^D{@nP;6Fh3o~z#nyk2shf9r-TPslo>Cp(Pbe6W}= zXl;cg?}9-)Yg9H%0T)x{sNP?q>9x?TL1V#O2oA|ZuhlGOJ;zp4J7@-N2d-57#9#6P zTih7LswM*!$#V5(p-G`>Csp@-MJZ})!ABoKEwNTBGji^}>pUN-IFXlZ6Q^d8YKqUZFK zPW#F^o9*LE+o`|0z>}FEBd%y#^7$iNwD!F)&jO=8_XA`2<$nvk^Kb^23vDig-pn#+ zhL;UAB<}}G2nEMWCbJs{sv^S;ReRJVC#dl|r-*X63|qoZ`dcpg{^bp4>3>u54~H)t z>1e_cH^T17^fj3Vn+-;KW#ALhvw%8A#~Iaf zKMj&YHnzKG4{TdCYN1-dIx8lrmbJ1cLbT(9fP7j){;)C5N}5#BPioa3M>WYRYD=tB zIZ^N(YrX3FiV|sr%xL{?3k4lQ~gKW8P+WbJNgG zJEqIbr?C%6V~_RI*bW=SVQwn!D{*L&G1dx@e?{F6Y|vDKvy_%Ofm^HISoi1w>1toe zf{uFa!7+W?#&a12&ASti7awjZ&&4D!q;$ObUjK5C9eSXDc!2!|A2;G8d(`fLyP>icM+bzan!chad}X$Fd@fUNUAXJMaklfb4tK({|=>FY~<4+ zdb7W6+4*EzNGRj&Wytxc{}BV`$4B* zHn!pBE{Q!!=u~%612X+Cl=ceeM^ zNnNRVd;lt`8@Ty7ZmsYoUZSD1s+H+STO$2%z~nTRxicUNPK&vx6&IuK$TK2Jdn`Pu%>W?&I+#Fk#Kyxi{e+%(Qs@KrI4`k7=F#Z z>2b$<5BCOH2TX3fmmJp2Ka%|FQZ0a;pe>Nq4ucIU7P41wI@YZT(d6;|N+j>UNaSQ@ zMiCi?Zta6JL?y47NNmi2?T`g)<}n)i56?VC)+%$$-xY~`@ zI;9JT!&iTM!n{H(Mv%ZUwBd=JWKc3j^OPfY^)r1xx+VW&S?W~c!IL&e=Y;PLyMp{ogwXfRUcwZo@?h8)8g-J78O zlB$Oza>kNY1j1GvX>`UKpA~Y4a=s? ze6OBL@}>WX0Qd+qSkxm(H~--{NDrCzz3b^z*8IlrK6e1_lx%j2FSG8hxqtEpl!8Ee zG|6{NW_KFI@cR}@y?}MLfVJwGx>-Th*slJucs%_{!TDRp6D%JRN{?35O z=U9xpt4^_~nBh$POQpz&i{&fYo@r$Pw_pv99+%vK)xQSW)Eg?`z*j*?x$wD9b0ufi zKG^lvUn=t_>V}r*}2!NlXsA za{EV+d5xaPU|CC2s?XX(Y+Owf`&JRa&45H z*=9Ynq~fs;RD&)>CPqQwTB+_=-XlM$o`bp5LYKe^lOY$8VZu*~^Hu;pL2ey|9k`+v z6Q`(oYhtp77i@Mq)+P*XEyyFxs>N)E-~5G{Sg4)xLVX%-bsdA4-RQum$RtLq-0RyI?Dgzs89939R{L1Wy{`efluT`1w@)68_%^e*cO zPPt|UpS09bY(oVdxcZPS65xUT4cvh(x+^swl#HxkB^v+uS2^y%w<4NpJUD+BcxjoX z$J9A^^Sqnx^Vbmq*0v<*D25T<6O!syACQlVtk5pV3N4W3Byica8j?&6%-uOc%z|2F z9r=SVpSIrgmQ94$3eMEqRbXuE>p8Yuo-;zO?bn?trI>1^k-qP4Z~(KrDYO zelg#eYFv(-cOA1*+OuQF*f%}1=v3AOK=Ap08v|0((*WCwm$MiJ1)xl(W2ZI3WN(?< zky`;}-|>@XQc^cK*_H69fH@HUDs8%@N_fDCuJ{2)c?CuRRemBaieU1|zy%vFtE5Yp zbmismi{=tHrtaEea_7w6t5zimM%LSkdY=?-di@leYo6Rhoo4RF8NrFSH#h8Vk_ozc znjAA`#hqwpXaHn4*b_*n=fEbeDp?+cUzQU=ukx0g z$lSkBTaAB~=y{Dlnp`rWrPDG`7|AazOR-gsKE?{r1B?5!cY;28JGX3JJ{8G0RPHWg zD$q$akMdlIL6n_Sl!>{2l-EvWaB_5&Sj#DOKQHj^IG4cr^UFcRJFv*i3!;?) zpq0B^-JVm_c1q#$+Bl8Rq_~v+*+Ppr+1S_cWb+@3HkMt6QWXzrA=4Wk{3Q8xG2Ug{ zv}Bl69GqUFu&Wb1%thy?-q2{pV6;dO&7w+H+1q>9wo1z$^^mup&Wo8oJ+bCTvPEPT z{Qd+=$SB4gsB_nm|>S9Hu$Ipkc>nl6@4&>N2fdU;|8b@^Vs zRfZg@R7W9f9pNS~ajUa~F(Z0Sz|i=HHZDigem`vRN<7A5d`pnXff~CA=<5t!Gyot- zNAmZ-a&{SJy4sA# z?*G<&B6?pA{Km@KTp6SE>VW^s)KG>G?=mz5V&sD=rQIj)w z>qyKunrDIAQt@|Q_%r-9=lu+gRj}-GNJl?|kjfRH zIXVj3c>uwS7T$|!_D@g#^qY(*4!R{q zh3SAMkP9XNzN^FzVK_|YH zvLy9}0p-ZM`=o?lFO#1WQ$La7wHMYs=3;#2PJ=|rWD`kOp{AEh0M|j#{x3KTumD6k z*v>p2fuAFR zBcUmpsl6&?v0t&bFIEq}!rEryIaQ~$;xTO=9Hgkxdpo}MH=rY^z!eEVKlqcP zqa1@7l)a+bM61DZ=Rg;F<|Zbl4ZL?R41S&d0@Wufvwk9iITj_G#Fr6Cprsf08U##U z^-o*yyT_|e)q=Zu&JsT7)t-i{thSwkVILrzMz;wXXfB?tT0d^PF zM4G(YRdIl(ZW4l@qhGX(e%9A;==h{nJk{-N5d10E>1>sZ$0t6yLX4x0r9&%afR#o9 z#Ak)t1B_EVR$e7NPPnL>Y8xpJvp{|9KsOz`GqEjx*S1VB=HKHwe;s;BJ|u>_#EP3< z@w-L9iXyqnnSU)OZ)P&4CtJ_W%(QH4i?ISx_GWJF9Y+sGlpZH2< zLIc=|&tO_2!4?+{3{8?^B-3#z;m4+C?ust_NVppbQ&Sj}DmDpJQ}MI!lEAY`0H218 zII~wNj%~XnVE<#H3p3>KyTdxVQRqOTV04eS2&W{U=z-5Sc*#A`k?gs7~EU z`b@j0?7mz_@GdxgZ?g38x?J+AmW%PNxqukE#CY;TBzk>VDCq|WmRMPCO(vYio~m0jV z*!dva^lVN)i+iHWzGY`hckXwh+>tndh?+HVHBo2y&?_=ij4RyNC%%DcEqL4AO`@k? zXVkEN`n1~I2kKqdv+bWS>d13LVJ^rw!pGB$FyDXB9$RJeLUeW7GewX?(5!z0ygZPu zt)KdcJ)%tghGT-FbI?~Kkm*TpZamLT*^aJh0>2?T;$3yz93;xA(A6!Az>37);kX@3 zi7fj!I<7g--lUxa!ns50C*4RUdEbc^D38f6fI>V-bbpH0h$A&)wsg`Xm3H4!V)~~G z(Yl11()y)iLZKyZa$R$gfa$8fRP5;!0za{OF}~l;s>^%-81U4@a7_$P(Ln4lQ?D{1B z;xhw@SY_f2LhHy0P$l)-*Rx}cRqCXdA!8z6ZNRi?eF9NXKAzChkr#B4j zaTrpP*Vg^+Nw#~DLd@;UwU?q==Ezsl4C64%DyB)8<+EG$TlV{kZSh+$#d{Dl=D;Tl z0qBXwH>`~$?K&XFG2*KBU&e~?z?u3W+;ROmzv$}XH_y3$LH8J-AssjgWE&08k&zpy zVbW!xw!{;d%IZj~0FJJD5YGMsOy=vZvQr)a4kiSm%`)^g2KbsJh?`^{lE`-qJ=WO^ z^v>*+iraa%dhjKQjP3trwe&B=LZ6l~Bb%#hnx2Ga{^kxtBsvC-upf-z24Ir%Pj=Wy zfcfr}pJXHcWeD_;H6`3Tjv+OGZ@^|&K)iSY7V2t{eT^bnCbf2ebkQDLtm#H?_5V6) zW1u$KtuxAadcUC<{C5jnMd3fT_7_G4v^Rc+ROv6EN&rXz9cY#nD$phfWQ}ZvzAq9J zU2L-8gKfDMZD-$A!!-YfAGoK8P1pG$j``eiK{2<@Nq_eF%T1 zgzKEQ7OfG3|FlBB0nQD`(-Q-IqlyZI(98I%}51|AC2Kw|jz5dB-d z<`qso4CylfzJ`?K|H1wTz{e_}&R`^KQSbZWHd#gz8l5B&E1~q{V)Z-=mcUXKLpCD* zzk4S7aU>J#?9QIl=G%JZzwis$r!Fe*2`(OF_dQ^f4*?Vb;LM^Il>2;88rRLS$@dqF-z zmfE?^%hzFp^$YU;LRj7%bh*-9MEfSOnQZ>!d5JlZttv^aUjs{)8uI63f@Qp6JkYma8gDiC6~ z5aL~oc$gI$2%|!UNG-k3Qk?7o%iaM-lBM0NaJ%~)G8ejMFa?s_4Ds`z^Z5qKT!@5w zd_h?tiDzv-@7at6|(Q2Z>O08LSh`Yz!Qs!;~HSj_QK)Cw;| z(@Oi7B_N2Ei#6v6W($7ZD-o9|rQr4y;ARLRKgIP-DIe>*g1LPDDn2~0c%TuG>bB!QDXbf2dKc7+owJ79%U{v&o`@QTrr$F$%xM1zIH0{A&M~o zvNzzZmw$}8SfBOHrRA)N;FwC~O?0L}dzpTWf5wnsTz`Fgmfbfhu~$nmU8?*GE#C!S z@J&n~_MakV$N@bD0K>CkOuXYgU^C)E=ktr}Z`vQ{=~0`A{S}u*X;KaG1(*^>5N%w< zv{47muDD1OTLS*KWw%v7IkRx^0htS3c$o?Dx>w9U6GckB-FwQ|B;#L1CjCH30qw`f ztBiLstP-(H2=-Cn!Z@#u#q1DMgQzXZng5j)Y?y?x7a`KQk?a~exwp6DKS7hQb`xK} zp(8fl04$BRq`EmJP3cfp_6D^nPi}5MIIItw=PBw#w-|Jv2FtkMQcT1KyxBs zo+v;Edn!}_Y6i?VAS{4R9c>m67jt0iv$9p^$<>9Lc<~3pj}@3U7?U7=vTmmwZ0Zn7 zKaJw#J)H4{y}Ul)x9ea3yeoCaFXT|d(1L4-{TbFFVEsTm#?7D-DJW6_MPAcLbb0q- z-c}g(D#HP33^UyHKSi3*EgBq9g>#-nZxjXKB6R*fQb@mhbVMp|E6;BeBk8o<__uS1ulrRu(&_&LMs>@^ literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_basic_shapes.c b/examples/shapes/shapes_basic_shapes.c index 4b7cc261..67eea9d5 100644 --- a/examples/shapes/shapes_basic_shapes.c +++ b/examples/shapes/shapes_basic_shapes.c @@ -39,26 +39,27 @@ int main() DrawText("some basic shapes available on raylib", 20, 20, 20, DARKGRAY); - DrawLine(18, 42, screenWidth - 18, 42, BLACK); - DrawCircle(screenWidth/4, 120, 35, DARKBLUE); - DrawCircleGradient(screenWidth/4, 220, 60, GREEN, SKYBLUE); - DrawCircleLines(screenWidth/4, 340, 80, DARKBLUE); DrawRectangle(screenWidth/4*2 - 60, 100, 120, 60, RED); + DrawRectangleLines(screenWidth/4*2 - 40, 320, 80, 60, ORANGE); // NOTE: Uses QUADS internally, not lines DrawRectangleGradientH(screenWidth/4*2 - 90, 170, 180, 130, MAROON, GOLD); - DrawRectangleLines(screenWidth/4*2 - 40, 320, 80, 60, ORANGE); DrawTriangle((Vector2){screenWidth/4*3, 80}, (Vector2){screenWidth/4*3 - 60, 150}, (Vector2){screenWidth/4*3 + 60, 150}, VIOLET); + DrawPoly((Vector2){screenWidth/4*3, 320}, 6, 80, 0, BROWN); + + DrawCircleGradient(screenWidth/4, 220, 60, GREEN, SKYBLUE); + + // NOTE: We draw all LINES based shapes together to optimize internal drawing, + // this way, all LINES are rendered in a single draw pass + DrawLine(18, 42, screenWidth - 18, 42, BLACK); + DrawCircleLines(screenWidth/4, 340, 80, DARKBLUE); DrawTriangleLines((Vector2){screenWidth/4*3, 160}, (Vector2){screenWidth/4*3 - 20, 230}, (Vector2){screenWidth/4*3 + 20, 230}, DARKBLUE); - - DrawPoly((Vector2){screenWidth/4*3, 320}, 6, 80, 0, BROWN); - EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/src/config.h b/src/config.h index bec0c5fc..baafb934 100644 --- a/src/config.h +++ b/src/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.1-dev" +#define RAYLIB_VERSION "2.2-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) diff --git a/src/rlgl.h b/src/rlgl.h index f2134e70..5ead869f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -102,18 +102,19 @@ // Defines and Macros //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: This is the maximum amount of lines, triangles and quads per frame, be careful! - #define MAX_LINES_BATCH 8192 - #define MAX_TRIANGLES_BATCH 4096 - #define MAX_QUADS_BATCH 8192 + // This is the maximum amount of elements (quads) per batch + // NOTE: Be careful with text, every letter maps to a quad + #define MAX_BATCH_ELEMENTS 8192 #elif defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: Reduce memory sizes for embedded systems (RPI and HTML5) + // We reduce memory sizes for embedded systems (RPI and HTML5) // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care... - #define MAX_LINES_BATCH 1024 // Critical for wire shapes (sphere) - #define MAX_TRIANGLES_BATCH 2048 // Critical for some shapes (sphere) - #define MAX_QUADS_BATCH 1024 // Be careful with text, every letter maps a quad + #define MAX_BATCH_ELEMENTS 2048 #endif +#define MAX_BATCH_BUFFERING 1 // Max number of buffers for batching (multi-buffering) +#define MAX_MATRIX_STACK_SIZE 32 // Max size of Matrix stack +#define MAX_DRAWCALL_REGISTERED 256 // Max draws by state changes (mode, texture) + // Texture parameters (equivalent to OpenGL defines) #define RL_TEXTURE_WRAP_S 0x2802 // GL_TEXTURE_WRAP_S #define RL_TEXTURE_WRAP_T 0x2803 // GL_TEXTURE_WRAP_T @@ -431,7 +432,7 @@ void rlClearScreenBuffers(void); // Clear used screen buf //------------------------------------------------------------------------------------ void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) -void rlglDraw(void); // Update and Draw default buffers (lines, triangles, quads) +void rlglDraw(void); // Update and draw default internal buffers int rlGetVersion(void); // Returns current OpenGL version bool rlCheckBufferLimit(int type, int vCount); // Check internal buffer overflow for a given number of vertex @@ -593,11 +594,6 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MATRIX_STACK_SIZE 16 // Matrix stack max size -#define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes -#define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) - // NOTE: Every vertex are 3 floats (12 bytes) - #ifndef GL_SHADING_LANGUAGE_VERSION #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #endif @@ -692,29 +688,24 @@ typedef struct DynamicBuffer { } DynamicBuffer; // Draw call type -// NOTE: Used to track required draw-calls, organized by texture typedef struct DrawCall { - int vertexCount; - GLuint vaoId; - GLuint textureId; - GLuint shaderId; - - Matrix projection; - Matrix modelview; - - // TODO: Store additional draw state data - //int blendMode; - //Guint fboId; + int mode; // Drawing mode: LINES, TRIANGLES, QUADS + int vertexCount; // Number of vertex of the draw + //GLuint vaoId; // Vertex Array id to be used on the draw + //GLuint shaderId; // Shader id to be used on the draw + GLuint textureId; // Texture id to be used on the draw + //Matrix projection; // Projection matrix for this draw + //Matrix modelview; // Modelview matrix for this draw } DrawCall; #if defined(SUPPORT_VR_SIMULATOR) // VR Stereo rendering configuration for simulator typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer - Shader distortionShader; // VR stereo rendering distortion shader - Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports - Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices - Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + RenderTexture2D stereoFbo; // VR stereo rendering framebuffer + Shader distortionShader; // VR stereo rendering distortion shader + Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices } VrStereoConfig; #endif @@ -801,31 +792,28 @@ typedef struct VrStereoConfig { #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -static Matrix stack[MATRIX_STACK_SIZE]; +static Matrix stack[MAX_MATRIX_STACK_SIZE] = { 0 }; static int stackCounter = 0; static Matrix modelview = { 0 }; static Matrix projection = { 0 }; static Matrix *currentMatrix = NULL; static int currentMatrixMode = -1; - -static int currentDrawMode = -1; - static float currentDepth = -1.0f; -static DynamicBuffer lines = { 0 }; // Default dynamic buffer for lines data -static DynamicBuffer triangles = { 0 }; // Default dynamic buffer for triangles data -static DynamicBuffer quads = { 0 }; // Default dynamic buffer for quads data (used to draw textures) +// Default dynamic buffer for elements data +// NOTE: A multi-buffering system is supported +static DynamicBuffer vertexData[MAX_BATCH_BUFFERING] = { 0 }; +static int currentBuffer = 0; + +// Transform matrix to be used with rlTranslate, rlRotate, rlScale +static Matrix transformMatrix = { 0 }; +static bool useTransformMatrix = false; // Default buffers draw calls static DrawCall *draws = NULL; static int drawsCounter = 0; -// Temp vertex buffer to be used with rlTranslate, rlRotate, rlScale -static Vector3 *tempBuffer = NULL; -static int tempBufferCount = 0; -static bool useTempBuffer = false; - // Shaders static unsigned int defaultVShaderId; // Default vertex shader id (used by default shader program) static unsigned int defaultFShaderId; // Default fragment shader Id (used by default shader program) @@ -844,10 +832,10 @@ static bool texCompASTCSupported = false; // ASTC texture compression support #if defined(SUPPORT_VR_SIMULATOR) // VR global variables -static VrStereoConfig vrConfig; // VR stereo configuration for simulator -static bool vrSimulatorReady = false; // VR simulator ready flag -static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag - // NOTE: This flag is useful to render data over stereo image (i.e. FPS) +static VrStereoConfig vrConfig; // VR stereo configuration for simulator +static bool vrSimulatorReady = false; // VR simulator ready flag +static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag + // NOTE: This flag is useful to render data over stereo image (i.e. FPS) #endif // defined(SUPPORT_VR_SIMULATOR) #endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -894,7 +882,7 @@ static Shader LoadShaderDefault(void); // Load default shader (just vertex static void SetShaderDefaultLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) static void UnloadShaderDefault(void); // Unload default shader -static void LoadBuffersDefault(void); // Load default internal buffers (lines, triangles, quads) +static void LoadBuffersDefault(void); // Load default internal buffers static void UpdateBuffersDefault(void); // Update default internal buffers (VAOs/VBOs) with vertex data static void DrawBuffersDefault(void); // Draw default internal buffers vertex data static void UnloadBuffersDefault(void); // Unload default internal buffers vertex data from CPU and GPU @@ -963,30 +951,36 @@ void rlMatrixMode(int mode) currentMatrixMode = mode; } -// Push the current matrix to stack +// Push the current matrix into stack void rlPushMatrix(void) { - if (stackCounter == MATRIX_STACK_SIZE - 1) + if (stackCounter >= MAX_MATRIX_STACK_SIZE) TraceLog(LOG_ERROR, "Matrix stack overflow"); + + if (currentMatrixMode == RL_MODELVIEW) { - TraceLog(LOG_ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE); + useTransformMatrix = true; + currentMatrix = &transformMatrix; } stack[stackCounter] = *currentMatrix; - rlLoadIdentity(); // TODO: Review matrix stack logic! stackCounter++; - - if (currentMatrixMode == RL_MODELVIEW) useTempBuffer = true; } // Pop lattest inserted matrix from stack void rlPopMatrix(void) -{ +{ if (stackCounter > 0) { Matrix mat = stack[stackCounter - 1]; *currentMatrix = mat; stackCounter--; } + + if ((stackCounter == 0) && (currentMatrixMode == RL_MODELVIEW)) + { + currentMatrix = &modelview; + useTransformMatrix = false; + } } // Reset current matrix to identity matrix @@ -1095,108 +1089,63 @@ void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } // Initialize drawing mode (how to organize vertex) void rlBegin(int mode) { - // Draw mode can only be RL_LINES, RL_TRIANGLES and RL_QUADS - currentDrawMode = mode; + // Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS + // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer + if (draws[drawsCounter - 1].mode != mode) + { + if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); + + draws[drawsCounter - 1].mode = mode; + draws[drawsCounter - 1].vertexCount = 0; + draws[drawsCounter - 1].textureId = whiteTexture; + } } // Finish vertex providing void rlEnd(void) { - if (useTempBuffer) + // Make sure current draws[i].vertexCount is multiple of 4, to align with index processing + // NOTE: It implies adding some extra vertex at the end of the draw, those vertex will be + // processed but are placed in a single point to not result in a fragment output... + // TODO: System could be improved (a bit) just storing every draw alignment value + // and adding it to vertexOffset on drawing... maybe in a future... + int vertexToAlign = draws[drawsCounter - 1].vertexCount%4; + for (int i = 0; i < vertexToAlign; i++) rlVertex3f(-1, -1, -1); + + // Make sure vertexCount is the same for vertices, texcoords, colors and normals + // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls + + // Make sure colors count match vertex count + if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].cCounter) { - // NOTE: In this case, *currentMatrix is already transposed because transposing has been applied - // independently to translation-scale-rotation matrices -> t(M1 x M2) = t(M2) x t(M1) - // This way, rlTranslatef(), rlRotatef()... behaviour is the same than OpenGL 1.1 - - // Apply transformation matrix to all temp vertices - for (int i = 0; i < tempBufferCount; i++) tempBuffer[i] = Vector3Transform(tempBuffer[i], *currentMatrix); - - // Deactivate tempBuffer usage to allow rlVertex3f do its job - useTempBuffer = false; - - // Copy all transformed vertices to right VAO - for (int i = 0; i < tempBufferCount; i++) rlVertex3f(tempBuffer[i].x, tempBuffer[i].y, tempBuffer[i].z); + int addColors = vertexData[currentBuffer].vCounter - vertexData[currentBuffer].cCounter; - // Reset temp buffer - tempBufferCount = 0; + for (int i = 0; i < addColors; i++) + { + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 4]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 1] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 3]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 2] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 2]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 3] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 1]; + vertexData[currentBuffer].cCounter++; + } } - - // Make sure vertexCount is the same for vertices-texcoords-normals-colors - // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls. - switch (currentDrawMode) + + // Make sure texcoords count match vertex count + if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].tcCounter) { - case RL_LINES: - { - if (lines.vCounter != lines.cCounter) - { - int addColors = lines.vCounter - lines.cCounter; - - for (int i = 0; i < addColors; i++) - { - lines.colors[4*lines.cCounter] = lines.colors[4*lines.cCounter - 4]; - lines.colors[4*lines.cCounter + 1] = lines.colors[4*lines.cCounter - 3]; - lines.colors[4*lines.cCounter + 2] = lines.colors[4*lines.cCounter - 2]; - lines.colors[4*lines.cCounter + 3] = lines.colors[4*lines.cCounter - 1]; + int addTexCoords = vertexData[currentBuffer].vCounter - vertexData[currentBuffer].tcCounter; - lines.cCounter++; - } - } - } break; - case RL_TRIANGLES: - { - if (triangles.vCounter != triangles.cCounter) - { - int addColors = triangles.vCounter - triangles.cCounter; - - for (int i = 0; i < addColors; i++) - { - triangles.colors[4*triangles.cCounter] = triangles.colors[4*triangles.cCounter - 4]; - triangles.colors[4*triangles.cCounter + 1] = triangles.colors[4*triangles.cCounter - 3]; - triangles.colors[4*triangles.cCounter + 2] = triangles.colors[4*triangles.cCounter - 2]; - triangles.colors[4*triangles.cCounter + 3] = triangles.colors[4*triangles.cCounter - 1]; - - triangles.cCounter++; - } - } - } break; - case RL_QUADS: + for (int i = 0; i < addTexCoords; i++) { - // Make sure colors count match vertex count - if (quads.vCounter != quads.cCounter) - { - int addColors = quads.vCounter - quads.cCounter; - - for (int i = 0; i < addColors; i++) - { - quads.colors[4*quads.cCounter] = quads.colors[4*quads.cCounter - 4]; - quads.colors[4*quads.cCounter + 1] = quads.colors[4*quads.cCounter - 3]; - quads.colors[4*quads.cCounter + 2] = quads.colors[4*quads.cCounter - 2]; - quads.colors[4*quads.cCounter + 3] = quads.colors[4*quads.cCounter - 1]; - - quads.cCounter++; - } - } - - // Make sure texcoords count match vertex count - if (quads.vCounter != quads.tcCounter) - { - int addTexCoords = quads.vCounter - quads.tcCounter; - - for (int i = 0; i < addTexCoords; i++) - { - quads.texcoords[2*quads.tcCounter] = 0.0f; - quads.texcoords[2*quads.tcCounter + 1] = 0.0f; - - quads.tcCounter++; - } - } - - // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P - - } break; - default: break; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter] = 0.0f; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter + 1] = 0.0f; + vertexData[currentBuffer].tcCounter++; + } } + // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P + // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, // as well as depth buffer bit-depth (16bit or 24bit or 32bit) // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) @@ -1204,84 +1153,36 @@ void rlEnd(void) // Verify internal buffers limits // NOTE: This check is combined with usage of rlCheckBufferLimit() - if ((lines.vCounter/2 >= (MAX_LINES_BATCH - 2)) || - (triangles.vCounter/3 >= (MAX_TRIANGLES_BATCH - 3)) || - (quads.vCounter/4 >= (MAX_QUADS_BATCH - 4))) + if ((vertexData[currentBuffer].vCounter/4) >= (MAX_BATCH_ELEMENTS - 4)) { // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! // Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw - - // TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly - rlPopMatrix(); rlglDraw(); } } // Define one vertex (position) +// NOTE: Vertex position data is the basic information required for drawing void rlVertex3f(float x, float y, float z) { - // NOTE: Temp buffer is processed and resetted at rlEnd() - // Between rlBegin() and rlEnd() can not be more than TEMP_VERTEX_BUFFER_SIZE rlVertex3f() calls - if (useTempBuffer && (tempBufferCount < TEMP_VERTEX_BUFFER_SIZE)) - { - tempBuffer[tempBufferCount].x = x; - tempBuffer[tempBufferCount].y = y; - tempBuffer[tempBufferCount].z = z; - tempBufferCount++; - } - else + Vector3 vec = { x, y, z }; + + // Transform provided vector if required + if (useTransformMatrix) vec = Vector3Transform(vec, transformMatrix); + + // Verify that MAX_BATCH_ELEMENTS limit not reached + if (vertexData[currentBuffer].vCounter/4 < MAX_BATCH_ELEMENTS) { - switch (currentDrawMode) - { - case RL_LINES: - { - // Verify that MAX_LINES_BATCH limit not reached - if (lines.vCounter/2 < MAX_LINES_BATCH) - { - lines.vertices[3*lines.vCounter] = x; - lines.vertices[3*lines.vCounter + 1] = y; - lines.vertices[3*lines.vCounter + 2] = z; - - lines.vCounter++; - } - else TraceLog(LOG_ERROR, "MAX_LINES_BATCH overflow"); - - } break; - case RL_TRIANGLES: - { - // Verify that MAX_TRIANGLES_BATCH limit not reached - if (triangles.vCounter/3 < MAX_TRIANGLES_BATCH) - { - triangles.vertices[3*triangles.vCounter] = x; - triangles.vertices[3*triangles.vCounter + 1] = y; - triangles.vertices[3*triangles.vCounter + 2] = z; - - triangles.vCounter++; - } - else TraceLog(LOG_ERROR, "MAX_TRIANGLES_BATCH overflow"); - - } break; - case RL_QUADS: - { - // Verify that MAX_QUADS_BATCH limit not reached - if (quads.vCounter/4 < MAX_QUADS_BATCH) - { - quads.vertices[3*quads.vCounter] = x; - quads.vertices[3*quads.vCounter + 1] = y; - quads.vertices[3*quads.vCounter + 2] = z; - - quads.vCounter++; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter] = vec.x; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 1] = vec.y; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 2] = vec.z; + vertexData[currentBuffer].vCounter++; - draws[drawsCounter - 1].vertexCount++; - } - else TraceLog(LOG_ERROR, "MAX_QUADS_BATCH overflow"); - - } break; - default: break; - } + draws[drawsCounter - 1].vertexCount++; } + else TraceLog(LOG_ERROR, "MAX_BATCH_ELEMENTS overflow"); } // Define one vertex (position) @@ -1300,13 +1201,9 @@ void rlVertex2i(int x, int y) // NOTE: Texture coordinates are limited to QUADS only void rlTexCoord2f(float x, float y) { - if (currentDrawMode == RL_QUADS) - { - quads.texcoords[2*quads.tcCounter] = x; - quads.texcoords[2*quads.tcCounter + 1] = y; - - quads.tcCounter++; - } + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter] = x; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter + 1] = y; + vertexData[currentBuffer].tcCounter++; } // Define one vertex (normal) @@ -1319,40 +1216,11 @@ void rlNormal3f(float x, float y, float z) // Define one vertex (color) void rlColor4ub(byte x, byte y, byte z, byte w) { - switch (currentDrawMode) - { - case RL_LINES: - { - lines.colors[4*lines.cCounter] = x; - lines.colors[4*lines.cCounter + 1] = y; - lines.colors[4*lines.cCounter + 2] = z; - lines.colors[4*lines.cCounter + 3] = w; - - lines.cCounter++; - - } break; - case RL_TRIANGLES: - { - triangles.colors[4*triangles.cCounter] = x; - triangles.colors[4*triangles.cCounter + 1] = y; - triangles.colors[4*triangles.cCounter + 2] = z; - triangles.colors[4*triangles.cCounter + 3] = w; - - triangles.cCounter++; - - } break; - case RL_QUADS: - { - quads.colors[4*quads.cCounter] = x; - quads.colors[4*quads.cCounter + 1] = y; - quads.colors[4*quads.cCounter + 2] = z; - quads.colors[4*quads.cCounter + 3] = w; - - quads.cCounter++; - - } break; - default: break; - } + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter] = x; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 1] = y; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 2] = z; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 3] = w; + vertexData[currentBuffer].cCounter++; } // Define one vertex (color) @@ -1385,8 +1253,7 @@ void rlEnableTexture(unsigned int id) if (draws[drawsCounter - 1].textureId != id) { if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; - - if (drawsCounter >= MAX_DRAWS_BY_TEXTURE) rlglDraw(); + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].textureId = id; draws[drawsCounter - 1].vertexCount = 0; @@ -1403,7 +1270,7 @@ void rlDisableTexture(void) #else // NOTE: If quads batch limit is reached, // we force a draw call and next batch starts - if (quads.vCounter/4 >= MAX_QUADS_BATCH) rlglDraw(); + if (vertexData[currentBuffer].vCounter/4 >= MAX_BATCH_ELEMENTS) rlglDraw(); #endif } @@ -1747,7 +1614,6 @@ void rlglInit(int width, int height) // Init default white texture unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); if (whiteTexture != 0) TraceLog(LOG_INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); @@ -1757,34 +1623,30 @@ void rlglInit(int width, int height) defaultShader = LoadShaderDefault(); currentShader = defaultShader; - // Init default vertex arrays buffers (lines, triangles, quads) + // Init default vertex arrays buffers LoadBuffersDefault(); - - // Init temp vertex buffer, used when transformation required (translate, rotate, scale) - tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); - - for (int i = 0; i < TEMP_VERTEX_BUFFER_SIZE; i++) tempBuffer[i] = Vector3Zero(); + + // Init transformations matrix accumulator + transformMatrix = MatrixIdentity(); // Init draw calls tracking system - draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); + draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWCALL_REGISTERED); - for (int i = 0; i < MAX_DRAWS_BY_TEXTURE; i++) + for (int i = 0; i < MAX_DRAWCALL_REGISTERED; i++) { + draws[i].mode = RL_QUADS; draws[i].vertexCount = 0; - draws[i].vaoId = 0; - draws[i].shaderId = 0; - draws[i].textureId = 0; - - draws[i].projection = MatrixIdentity(); - draws[i].modelview = MatrixIdentity(); + //draws[i].vaoId = 0; + //draws[i].shaderId = 0; + draws[i].textureId = whiteTexture; + //draws[i].projection = MatrixIdentity(); + //draws[i].modelview = MatrixIdentity(); } drawsCounter = 1; - draws[0].textureId = whiteTexture; // Set default draw texture id - currentDrawMode = RL_TRIANGLES; // Set default draw mode // Init internal matrix stack (emulating OpenGL 1.1) - for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); + for (int i = 0; i < MAX_MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); // Init internal projection and modelview matrices projection = MatrixIdentity(); @@ -1832,24 +1694,19 @@ void rlglClose(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) UnloadShaderDefault(); // Unload default shader - UnloadBuffersDefault(); // Unload default buffers (lines, triangles, quads) + UnloadBuffersDefault(); // Unload default buffers glDeleteTextures(1, &whiteTexture); // Unload default texture TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); free(draws); - free(tempBuffer); #endif } -// Drawing batches: triangles, quads, lines +// Update and draw internal buffers void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: In a future version, models could be stored in a stack... - //for (int i = 0; i < modelsCount; i++) rlDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); - - // NOTE: Default buffers upload and draw UpdateBuffersDefault(); DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside #endif @@ -1862,7 +1719,7 @@ int rlGetVersion(void) return OPENGL_11; #elif defined(GRAPHICS_API_OPENGL_21) #if defined(__APPLE__) - return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX + return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX #else return OPENGL_21; #endif @@ -1878,13 +1735,7 @@ bool rlCheckBufferLimit(int type, int vCount) { bool overflow = false; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - switch (type) - { - case RL_LINES: overflow = ((lines.vCounter + vCount)/2 >= MAX_LINES_BATCH); break; - case RL_TRIANGLES: overflow = ((triangles.vCounter + vCount)/3 >= MAX_TRIANGLES_BATCH); break; - case RL_QUADS: overflow = ((quads.vCounter + vCount)/4 >= MAX_QUADS_BATCH); break; - default: break; - } + if ((vertexData[currentBuffer].vCounter + vCount)/4 >= MAX_BATCH_ELEMENTS) overflow = true; #endif return overflow; } @@ -2098,6 +1949,7 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi } // Update already loaded texture in GPU with new data +// TODO: We don't know safely if internal texture format is the expected one... void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data) { glBindTexture(GL_TEXTURE_2D, id); @@ -2881,25 +2733,6 @@ void *rlReadTexturePixels(Texture2D texture) return pixels; } -/* -// TODO: Record draw calls to be processed in batch -// NOTE: Global state must be kept -void rlRecordDraw(void) -{ - // TODO: Before adding a new draw, check if anything changed from last stored draw -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - draws[drawsCounter].vertexCount = currentState.vertexCount; - draws[drawsCounter].vaoId = currentState.vaoId; // lines.id, trangles.id, quads.id? - draws[drawsCounter].textureId = currentState.textureId; // whiteTexture? - draws[drawsCounter].shaderId = currentState.shaderId; // defaultShader.id - draws[drawsCounter].projection = projection; - draws[drawsCounter].modelview = modelview; - - drawsCounter++; -#endif -} -*/ - //---------------------------------------------------------------------------------- // Module Functions Definition - Shaders Functions // NOTE: Those functions are exposed directly to the user in raylib.h @@ -2934,12 +2767,12 @@ Shader GetShaderDefault(void) // NOTE: text chars array should be freed manually char *LoadText(const char *fileName) { - FILE *textFile; + FILE *textFile = NULL; char *text = NULL; if (fileName != NULL) { - textFile = fopen(fileName,"r"); + textFile = fopen(fileName,"rt"); if (textFile != NULL) { @@ -2950,7 +2783,8 @@ char *LoadText(const char *fileName) if (size > 0) { text = (char *)malloc(sizeof(char)*(size + 1)); - fread(text, sizeof(char), size, textFile); + int count = fread(text, sizeof(char), size, textFile); + text[count] = '\0'; } fclose(textFile); @@ -3833,7 +3667,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad // Load default shader (just vertex positioning and texture coloring) -// NOTE: This shader program is used for batch buffers (lines, triangles, quads) +// NOTE: This shader program is used for internal buffers static Shader LoadShaderDefault(void) { Shader shader = { 0 }; @@ -3974,171 +3808,93 @@ static void UnloadShaderDefault(void) glDeleteProgram(defaultShader.id); } -// Load default internal buffers (lines, triangles, quads) +// Load default internal buffers static void LoadBuffersDefault(void) { - // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) + // Initialize CPU (RAM) arrays (vertex position, texcoord, color data and indexes) //-------------------------------------------------------------------------------------------- - - // Lines - Initialize arrays (vertex position and color data) - lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line - lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line - lines.texcoords = NULL; - lines.indices = NULL; - - for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0f; - for (int i = 0; i < (4*2*MAX_LINES_BATCH); i++) lines.colors[i] = 0; - - lines.vCounter = 0; - lines.cCounter = 0; - lines.tcCounter = 0; - - // Triangles - Initialize arrays (vertex position and color data) - triangles.vertices = (float *)malloc(sizeof(float)*3*3*MAX_TRIANGLES_BATCH); // 3 float by vertex, 3 vertex by triangle - triangles.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH); // 4 float by color, 3 colors by triangle - triangles.texcoords = NULL; - triangles.indices = NULL; - - for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0f; - for (int i = 0; i < (4*3*MAX_TRIANGLES_BATCH); i++) triangles.colors[i] = 0; - - triangles.vCounter = 0; - triangles.cCounter = 0; - triangles.tcCounter = 0; - - // Quads - Initialize arrays (vertex position, texcoord, color data and indexes) - quads.vertices = (float *)malloc(sizeof(float)*3*4*MAX_QUADS_BATCH); // 3 float by vertex, 4 vertex by quad - quads.texcoords = (float *)malloc(sizeof(float)*2*4*MAX_QUADS_BATCH); // 2 float by texcoord, 4 texcoord by quad - quads.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_QUADS_BATCH); // 4 float by color, 4 colors by quad + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) + { + vertexData[i].vertices = (float *)malloc(sizeof(float)*3*4*MAX_BATCH_ELEMENTS); // 3 float by vertex, 4 vertex by quad + vertexData[i].texcoords = (float *)malloc(sizeof(float)*2*4*MAX_BATCH_ELEMENTS); // 2 float by texcoord, 4 texcoord by quad + vertexData[i].colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS); // 4 float by color, 4 colors by quad #if defined(GRAPHICS_API_OPENGL_33) - quads.indices = (unsigned int *)malloc(sizeof(unsigned int)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) + vertexData[i].indices = (unsigned int *)malloc(sizeof(unsigned int)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) #elif defined(GRAPHICS_API_OPENGL_ES2) - quads.indices = (unsigned short *)malloc(sizeof(unsigned short)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) + vertexData[i].indices = (unsigned short *)malloc(sizeof(unsigned short)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) #endif - for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0f; - for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0f; - for (int i = 0; i < (4*4*MAX_QUADS_BATCH); i++) quads.colors[i] = 0; + for (int j = 0; j < (3*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].vertices[j] = 0.0f; + for (int j = 0; j < (2*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].texcoords[j] = 0.0f; + for (int j = 0; j < (4*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].colors[j] = 0; - int k = 0; + int k = 0; - // Indices can be initialized right now - for (int i = 0; i < (6*MAX_QUADS_BATCH); i+=6) - { - quads.indices[i] = 4*k; - quads.indices[i+1] = 4*k+1; - quads.indices[i+2] = 4*k+2; - quads.indices[i+3] = 4*k; - quads.indices[i+4] = 4*k+2; - quads.indices[i+5] = 4*k+3; - - k++; - } + // Indices can be initialized right now + for (int j = 0; j < (6*MAX_BATCH_ELEMENTS); j += 6) + { + vertexData[i].indices[j] = 4*k; + vertexData[i].indices[j + 1] = 4*k + 1; + vertexData[i].indices[j + 2] = 4*k + 2; + vertexData[i].indices[j + 3] = 4*k; + vertexData[i].indices[j + 4] = 4*k + 2; + vertexData[i].indices[j + 5] = 4*k + 3; + + k++; + } - quads.vCounter = 0; - quads.tcCounter = 0; - quads.cCounter = 0; + vertexData[i].vCounter = 0; + vertexData[i].tcCounter = 0; + vertexData[i].cCounter = 0; + } - TraceLog(LOG_INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); + TraceLog(LOG_INFO, "Internal buffers initialized successfully (CPU)"); //-------------------------------------------------------------------------------------------- - // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) - // NOTE: Default buffers are linked to use currentShader (defaultShader) + // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs //-------------------------------------------------------------------------------------------- - - // Upload and link lines vertex buffers - if (vaoSupported) - { - // Initialize Lines VAO - glGenVertexArrays(1, &lines.vaoId); - glBindVertexArray(lines.vaoId); - } - - // Lines - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(2, &lines.vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - - // Vertex color buffer (shader-location = 3) - glGenBuffers(2, &lines.vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId); - else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]); - - // Upload and link triangles vertex buffers - if (vaoSupported) + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) { - // Initialize Triangles VAO - glGenVertexArrays(1, &triangles.vaoId); - glBindVertexArray(triangles.vaoId); - } - - // Triangles - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(1, &triangles.vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - - // Vertex color buffer (shader-location = 3) - glGenBuffers(1, &triangles.vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId); - else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (triangles)", triangles.vboId[0], triangles.vboId[1]); - - // Upload and link quads vertex buffers - if (vaoSupported) - { - // Initialize Quads VAO - glGenVertexArrays(1, &quads.vaoId); - glBindVertexArray(quads.vaoId); - } + if (vaoSupported) + { + // Initialize Quads VAO + glGenVertexArrays(1, &vertexData[i].vaoId); + glBindVertexArray(vertexData[i].vaoId); + } - // Quads - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(1, &quads.vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - - // Vertex texcoord buffer (shader-location = 1) - glGenBuffers(1, &quads.vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - - // Vertex color buffer (shader-location = 3) - glGenBuffers(1, &quads.vboId[2]); - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - // Fill index buffer - glGenBuffers(1, &quads.vboId[3]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &vertexData[i].vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[i].vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &vertexData[i].vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_BATCH_ELEMENTS, vertexData[i].texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &vertexData[i].vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS, vertexData[i].colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + // Fill index buffer + glGenBuffers(1, &vertexData[i].vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexData[i].vboId[3]); #if defined(GRAPHICS_API_OPENGL_33) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW); #elif defined(GRAPHICS_API_OPENGL_ES2) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW); #endif - - if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId); - else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]); + } + + TraceLog(LOG_INFO, "Internal buffers uploaded successfully (GPU)"); // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); @@ -4150,74 +3906,48 @@ static void LoadBuffersDefault(void) // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) static void UpdateBuffersDefault(void) { - // Update lines vertex buffers - if (lines.vCounter > 0) - { - // Activate Lines VAO - if (vaoSupported) glBindVertexArray(lines.vaoId); - - // Lines - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*lines.vCounter, lines.vertices); // target - offset (in bytes) - size (in bytes) - data pointer - - // Lines - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*lines.cCounter, lines.colors); - } - - // Update triangles vertex buffers - if (triangles.vCounter > 0) - { - // Activate Triangles VAO - if (vaoSupported) glBindVertexArray(triangles.vaoId); - - // Triangles - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*triangles.vCounter, triangles.vertices); - - // Triangles - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*triangles.cCounter, triangles.colors); - } - - // Update quads vertex buffers - if (quads.vCounter > 0) + // Update vertex buffers data + if (vertexData[currentBuffer].vCounter > 0) { - // Activate Quads VAO - if (vaoSupported) glBindVertexArray(quads.vaoId); - - // Quads - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*quads.vCounter, quads.vertices); - - // Quads - texture coordinates buffer - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*quads.vCounter, quads.texcoords); - - // Quads - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*quads.vCounter, quads.colors); - - // Another option would be using buffer mapping... - //quads.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); - // Now we can modify vertices - //glUnmapBuffer(GL_ARRAY_BUFFER); + // Activate elements VAO + if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId); + + // Vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].vertices); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer + + // Texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].texcoords); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer + + // Colors buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].colors); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer + + // NOTE: glMapBuffer() causes sync issue. + // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job. + // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer(). + // If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new + // allocated pointer immediately even if GPU is still working with the previous data. + + // Another option: map the buffer object into client's memory + // Probably this code could be moved somewhere else... + // vertexData[currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // if(vertexData[currentBuffer].vertices) + // { + // Update vertex data + // } + // glUnmapBuffer(GL_ARRAY_BUFFER); + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); } - //-------------------------------------------------------------- - - // Unbind the current VAO - if (vaoSupported) glBindVertexArray(0); } // Draw default internal buffers vertex data -// NOTE: We draw in this order: lines, triangles, quads static void DrawBuffersDefault(void) { Matrix matProjection = projection; @@ -4230,13 +3960,14 @@ static void DrawBuffersDefault(void) for (int eye = 0; eye < eyesCount; eye++) { - #if defined(SUPPORT_VR_SIMULATOR) +#if defined(SUPPORT_VR_SIMULATOR) if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); - #endif +#endif - // Set current shader and upload current MVP matrix - if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) + // Draw quads buffers + if (vertexData[currentBuffer].vCounter > 0) { + // Set current shader and upload current MVP matrix glUseProgram(currentShader.id); // Create modelview-projection matrix @@ -4247,117 +3978,50 @@ static void DrawBuffersDefault(void) glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); // NOTE: Additional map textures not considered for default buffers drawing - } - - // Draw lines buffers - if (lines.vCounter > 0) - { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, whiteTexture); + + int vertexOffset = 0; - if (vaoSupported) - { - glBindVertexArray(lines.vaoId); - } + if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId); else { // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - - // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - } - - glDrawArrays(GL_LINES, 0, lines.vCounter); - - if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - // Draw triangles buffers - if (triangles.vCounter > 0) - { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, whiteTexture); - - if (vaoSupported) - { - glBindVertexArray(triangles.vaoId); - } - else - { - // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - - // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - } - - glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); - - if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - // Draw quads buffers - if (quads.vCounter > 0) - { - int quadsCount = 0; - int numIndicesToProcess = 0; - int indicesOffset = 0; - - if (vaoSupported) - { - glBindVertexArray(quads.vaoId); - } - else - { - // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]); glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); // Bind vertex attrib: texcoord (shader-location = 1) - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]); glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]); glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexData[currentBuffer].vboId[3]); } + glActiveTexture(GL_TEXTURE0); + for (int i = 0; i < drawsCounter; i++) { - quadsCount = draws[i].vertexCount/4; - numIndicesToProcess = quadsCount*6; // Get number of Quads*6 index by Quad - - //TraceLog(LOG_DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); - - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, draws[i].textureId); - // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process - #if defined(GRAPHICS_API_OPENGL_33) - glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*indicesOffset)); - #elif defined(GRAPHICS_API_OPENGL_ES2) - glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*indicesOffset)); - #endif - //GLenum err; - //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(LOG_INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! + if ((draws[i].mode == RL_LINES) || (draws[i].mode == RL_TRIANGLES)) glDrawArrays(draws[i].mode, vertexOffset, draws[i].vertexCount); + else + { +#if defined(GRAPHICS_API_OPENGL_33) + // We need to define the number of indices to be processed: quadsCount*6 + // NOTE: The final parameter tells the GPU the offset in bytes from the + // start of the index buffer to the location of the first index to process + glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*vertexOffset/4*6)); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLuint)*vertexOffset/4*6)); +#endif + } - indicesOffset += draws[i].vertexCount/4*6; + vertexOffset += draws[i].vertexCount; } if (!vaoSupported) @@ -4375,13 +4039,9 @@ static void DrawBuffersDefault(void) } // Reset vertex counters for next frame - lines.vCounter = 0; - lines.cCounter = 0; - triangles.vCounter = 0; - triangles.cCounter = 0; - quads.vCounter = 0; - quads.tcCounter = 0; - quads.cCounter = 0; + vertexData[currentBuffer].vCounter = 0; + vertexData[currentBuffer].tcCounter = 0; + vertexData[currentBuffer].cCounter = 0; // Reset depth for next draw currentDepth = -1.0f; @@ -4391,9 +4051,14 @@ static void DrawBuffersDefault(void) modelview = matModelView; // Reset draws counter - drawsCounter = 1; - draws[0].textureId = whiteTexture; + draws[0].mode = RL_QUADS; draws[0].vertexCount = 0; + draws[0].textureId = whiteTexture; + drawsCounter = 1; + + // Change to next buffer in the list + currentBuffer++; + if (currentBuffer >= MAX_BATCH_BUFFERING) currentBuffer = 0; } // Unload default internal buffers vertex data from CPU and GPU @@ -4408,35 +4073,23 @@ static void UnloadBuffersDefault(void) glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - // Delete VBOs from GPU (VRAM) - glDeleteBuffers(1, &lines.vboId[0]); - glDeleteBuffers(1, &lines.vboId[1]); - glDeleteBuffers(1, &triangles.vboId[0]); - glDeleteBuffers(1, &triangles.vboId[1]); - glDeleteBuffers(1, &quads.vboId[0]); - glDeleteBuffers(1, &quads.vboId[1]); - glDeleteBuffers(1, &quads.vboId[2]); - glDeleteBuffers(1, &quads.vboId[3]); - - if (vaoSupported) + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) { - // Delete VAOs from GPU (VRAM) - glDeleteVertexArrays(1, &lines.vaoId); - glDeleteVertexArrays(1, &triangles.vaoId); - glDeleteVertexArrays(1, &quads.vaoId); - } + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &vertexData[i].vboId[0]); + glDeleteBuffers(1, &vertexData[i].vboId[1]); + glDeleteBuffers(1, &vertexData[i].vboId[2]); + glDeleteBuffers(1, &vertexData[i].vboId[3]); - // Free vertex arrays memory from CPU (RAM) - free(lines.vertices); - free(lines.colors); - - free(triangles.vertices); - free(triangles.colors); + // Delete VAOs from GPU (VRAM) + if (vaoSupported) glDeleteVertexArrays(1, &vertexData[i].vaoId); - free(quads.vertices); - free(quads.texcoords); - free(quads.colors); - free(quads.indices); + // Free vertex arrays memory from CPU (RAM) + free(vertexData[i].vertices); + free(vertexData[i].texcoords); + free(vertexData[i].colors); + free(vertexData[i].indices); + } } // Renders a 1x1 XY quad in NDC