diff --git a/CMakeLists.txt b/CMakeLists.txt index d23c5c9c..ab3f4290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,8 @@ set(SOURCES "environment/sky.cpp" "environment/sun.cpp" "environment/stars.cpp" +"environment/starcatalog.cpp" +"environment/weather.cpp" "rendering/lightarray.cpp" "environment/skydome.cpp" "audio/sound.cpp" @@ -238,6 +240,7 @@ set(SOURCES "widgets/map_objects.cpp" "widgets/popup.cpp" "widgets/time.cpp" +"widgets/weatherpanel.cpp" "widgets/vehicleparams.cpp" "widgets/trainingcard.cpp" "widgets/perfgraphs.cpp" @@ -335,6 +338,7 @@ if (WITH_OPENGL_MODERN) "rendering/opengl46/opengl46light.cpp" "rendering/opengl46/opengl46skydome.cpp" "rendering/opengl46/opengl46sky.cpp" + "rendering/opengl46/opengl46weather.cpp" "rendering/opengl46/opengl46exposure.cpp" "rendering/opengl46/opengl46ao.cpp" "rendering/opengl46/opengl46locallights.cpp" diff --git a/application/editormode.cpp b/application/editormode.cpp index 32f991c5..4d4b41ba 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -36,6 +36,7 @@ extern char **environ; #include "simulation/simulation.h" #include "simulation/simulationtime.h" #include "simulation/simulationenvironment.h" +#include "widgets/weatherpanel.h" #include "utilities/Timer.h" #include "Console.h" #include "rendering/renderer.h" @@ -3556,6 +3557,9 @@ void editor_mode::render_environment_window() Global.AirTemperature = std::clamp(temperature, -35.0f, 40.0f); simulation::Environment.compute_weather(); } + // the layers of clouds, the fog and a weather report, as the weather section of a scenery describes them + if (ImGui::CollapsingHeader(STR_C("Clouds, fog and weather report"))) + ui::weather_controls(simulation::Environment.weather()); ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); ImGui::TextWrapped("%s", STR_C("Seen in the editor only; the scenery keeps its own time and weather")); ImGui::PopStyleColor(); diff --git a/docs/opengl46-renderer.md b/docs/opengl46-renderer.md index 96009c67..b6e86436 100644 --- a/docs/opengl46-renderer.md +++ b/docs/opengl46-renderer.md @@ -187,6 +187,44 @@ shadows of the sun, see `headlight shadows:` in Shift+F12); with the vehicle on | `gfx.lights.shadows.headlights` | `2` | 0..4: how many sets of headlights (vehicle ends) cast shadows; 0: none | | `gfx.lights.shadows.headlights.range` | `250` | 10..1000: distance ahead of the vehicle within which things cast the shadows of its headlights, metres | +### Weather + +The clouds, the fog and the stars are drawn from the weather of the simulation (a `weather` section of the scenery, or its +`atmo` section as before, see `weather.md`). The cloud model of the `sky` section and the starry dome (`skydome_stars.t3d`) +are no longer loaded. + +- **Clouds** are ray marched through layers of the genera of aviation reports (cumulus, cumulonimbus, stratus, cirrus...), + each with its own shape, height and depth, moving with the wind. The main view gets them at a fraction of its resolution + and blended over frames; reflections and the other views read a panorama of the sky with them, renewed in bands over a few + frames. Low clouds hide the hills and the far scenery they are in. +- **Cloud shadows**: the clouds cast their shadows on the ground, the models, the vehicles and into the fog, from a map + around the camera along the sun (or the moon). With them on, the overcast no longer dims the sun and softens its shadows + as a whole: the sun is dimmed where the clouds are, and shines in the gaps. +- **Fog**: the haze, thinning out with the height, and the ground fog of the weather. `volumetric`: a volume over the view + (froxels) holds the light the fog scatters, the sun with the shadows of the terrain, models and clouds in it (shafts of + light), and the lamps and headlights; past its depth the fog is worked out along the ray. `analytic`: the same fog along + the ray only, without the shafts and the lamps, as the old fog was drawn; it takes the same weather and looks the same + from afar, so the two can be switched without changing anything else. The colour of the fog comes from the sky around the + horizon, towards and away from the sun. +- **Stars** come from a catalogue built into the simulator (about 9100 stars down to magnitude 6.5), with their colours, + turned with the sidereal time and the latitude of the scenery, dimmed near the horizon, by the fog and the clouds. + +| Entry | Default | Meaning | +|---|---|---| +| `gfx.fog` | `volumetric` | `volumetric` (`yes`) or `analytic` (`old`, `no`), see above | +| `gfx.fog.quality` | `medium` | `low`/`medium`/`high`/`ultra` (or 1..4): the size of the fog volume, 80x45x48 to 240x135x128 cells | +| `gfx.fog.range` | `1500` | 100..10000: the depth of the fog volume, metres; farther, the fog is worked out along the ray | +| `gfx.fog.lights` | `1.0` | 0..10: the brightness of the lamps and headlights in the fog; 0: none | +| `gfx.fog.shadows yes/no` | `yes` | the sun and the headlights cast their shadows into the fog (shafts of light) | +| `gfx.fog.temporal yes/no` | `yes` | the fog volume is blended over frames (smoother, a little lag in quick turns) | +| `gfx.clouds` | `medium` | `no`, or the quality `low`/`medium`/`high`/`ultra` (or 1..4): the resolution and the number of steps of the clouds. `no`: no clouds are drawn, the overcast dims the sun as before | +| `gfx.clouds.distance` | `40000` | 5000..200000: the farthest the clouds are drawn, metres | +| `gfx.clouds.shadows yes/no` | `yes` | the clouds cast shadows, see above | +| `gfx.clouds.shadows.size` | `512` | 64..2048: the size of the cloud shadow map, texels | +| `gfx.clouds.shadows.range` | `16000` | 1000..100000: the side of the cloud shadow map, metres; farther the clouds cast no shadows | +| `gfx.stars yes/no` | `yes` | the stars of the catalogue | +| `gfx.stars.brightness` | `1.0` | 0..16: their brightness | + ### Models drawn from the GPU Many models of the scenery (trees, bushes, poles, fences: models without lights and animations) are drawn in batches, one @@ -255,6 +293,11 @@ editor goes through the first `dynamiclights` headlights, the reflections throug Shift+F12 shows the GPU time of `atmosphere:` (look-up tables, only when the sun moved or the weather changed), `envmap ggx:` (prefiltering, with each update of the environment map), `exposure:` and `bloom:`. The same window holds the ambient occlusion mode and its settings; Shift+F12 shows its time as `ambient occlusion:` (gtao only). +- **Weather** window (next to the Sky and exposure window): the switches above, the fog range in use, the share of the sun + in the light of the fog and how much light the clouds let through on average; below them the weather: a report (METAR) + of it, which can be edited and applied with a time of change, and the layers of clouds, the fog, the precipitation and + the wind, which change at once. These changes are of this computer only. Shift+F12 shows the GPU time of `clouds:` (the + main view), `clouds panorama:` (a band of the panorama), `cloud shadows:` and `fog volume:`. - **Instance culling** window: the mode of `gfx.gpucull` and the two switches above. With `gfx.gpucull yes` Shift+F12 shows `gpu cull:` (models drawn and hidden by the occlusion test, as counted on the graphics card a few frames before, the levels of detail, the draw calls with those of the depth prepass, and the processor time of the whole step) and the timings diff --git a/docs/weather.md b/docs/weather.md new file mode 100644 index 00000000..f8699ba4 --- /dev/null +++ b/docs/weather.md @@ -0,0 +1,93 @@ +# Weather: clouds, fog and precipitation + +The weather of a scenery is a set of cloud layers, a fog and a precipitation. The simulation keeps it +(`environment/weather.h`), the OpenGL 4.6 renderer draws it: volumetric clouds with their shadows on the ground, and a +fog with the shafts of the sun and the light of lamps and headlights in it (see `opengl46-renderer.md`, *Weather*). + +A scenery describes it in a `weather` section. A scenery without one is drawn from its `atmo` section as before: the +overcast gives the clouds and the precipitation, the fog range the visibility. Whatever sets the old overcast or fog range +while the simulation runs (the sliders of the user interface, the `__simulation.weather` memory cell, the `setweather` +command of the network) does so too, and the clouds and the visibility follow it. + +While the weather is described, the overcast and the fog range the rest of the simulation goes by (lamps turning on, the +sound and the look of the rain, friction, the `__simulation.weather` memory cell) are worked out from it. + +## The weather section + +``` +weather + metar "27015KT 3000 -RA BR BKN008 OVC015 08/06" + visibility 12000 + haze 800 + groundfog 40 180 + fognoise 0.6 + clouds cumulus sct 1100 2600 + clouds cirrus 3 8500 9200 0.8 + precipitation rain 0.5 + cloudwind 250 12 + temperature 8 + elevation 310 + seed 7 +endweather +``` + +Every entry is optional and they may come in any order; a later entry changes what an earlier one (or the report) set. +The section wins over an `atmo` section, wherever the two are in the scenery. Heights are in metres above the ground +reference (see `elevation`). + +| Entry | Meaning | +|---|---| +| `metar "report"` | a weather report (METAR) read first: the wind, the visibility, the present weather (rain, snow, drizzle, mist, fog, shallow and patchy fog, haze, showers, thunderstorms), the clouds with their heights in feet, the vertical visibility and the temperature. The station, time, pressure, runway visual range and anything after `TEMPO`, `BECMG`, `NOSIG` or `RMK` are skipped. Reports name only the convective genera (`CB`, `TCU`); the others are taken from the height, the amount and the precipitation (low and broken: stratocumulus; low, overcast and precipitating: nimbostratus; very low: stratus; middle: altocumulus or altostratus; high: cirrus or cirrostratus) | +| `visibility m` | meteorological visibility at the ground: how far a dark object is still seen against the horizon (5% contrast) | +| `haze m` | how fast the haze thins out with the height: it is e times less dense this many metres higher. Low for a mist, high for a dusty or smoky air | +| `groundfog thickness visibility` | fog lying on the ground, this many metres thick, with this visibility inside it. It fills the valleys below the ground reference too. `0` removes it | +| `fognoise 0..1` | how patchy the ground fog is | +| `clouds genus amount base [top [density]]` | a layer of clouds (at most 4, the ones covering the most are kept). The genus: `cumulus` (`cu`), `towering_cumulus` (`tcu`), `cumulonimbus` (`cb`), `stratocumulus` (`sc`), `stratus` (`st`), `nimbostratus` (`ns`), `altocumulus` (`ac`), `altostratus` (`as`), `cirrus` (`ci`), `cirrostratus` (`cs`), `cirrocumulus` (`cc`). The amount: `few`, `sct`, `bkn`, `ovc`, or oktas 0..8. Without the top the layer gets the typical depth of the genus; the density multiplies the typical one (0..4) | +| `noclouds` | removes the layers set before (by a report, say) | +| `precipitation kind [intensity]` | `rain`, `snow`, `auto` (by the air temperature, as the overcast above 1 always did) or `none`, intensity 0..1 | +| `cloudwind direction speed` | the wind at the height of the clouds: where it blows from (degrees from the north, clockwise) and its speed in m/s. The higher the layer, the faster it goes | +| `temperature degrees` | the air temperature, applied with the weather | +| `elevation m` | world height (y) of the ground the heights of the weather are counted from. By default the ground under the camera where the simulation starts; give it for hilly sceneries, so the clouds and the valley fog stay at the same height whichever place the simulation starts at | +| `seed n` | another arrangement of the clouds | + +What each genus looks like: + +| Genus | Drawn as | +|---|---| +| cumulus, towering cumulus | heaps with flat bases and rounded tops of different heights; towering ones a few kilometres tall | +| cumulonimbus | storm towers up to the tropopause, whose tops spread into anvils | +| stratocumulus, altocumulus | lumpy sheets; altocumulus higher, thinner and of smaller lumps | +| stratus, nimbostratus | even, grey sheets; nimbostratus thick and dark, the rain cloud | +| altostratus | a thin, even veil the sun shines through dimly | +| cirrus, cirrostratus, cirrocumulus | thin sheets of ice high up: strands drawn out along the wind, an even veil, small ripples | + +## Changing the weather while the simulation runs + +The `__simulation.metar` memory cell holds a report of the weather the simulation goes to. Setting its text (with an +`updatevalues` event, or from a script) changes the weather: the text is a report, or entries of the weather section +(`visibility 800 groundfog 60 300 clouds stratus ovc 150 400`); its value 1 is the time of the change in seconds. A text +of an event can't hold spaces: commas, semicolons or underscores separate the words as well +(`clouds,cumulonimbus,bkn,900`, `24015G25KT_4000_TSRA_BKN030CB`). The change goes smoothly: the layers of the same genera +change their sizes and heights, different ones fade out and in. A change of the old overcast or fog range takes over the +clouds or the fog at once, as it always did. + +The weather can also be changed in the editor (**Weather and time**, *Clouds, fog and weather report*) and in the +**Weather** debug window of the renderer. These change the weather of this computer only; they are not sent over the +network. + +## What the weather made of the old overcast looks like + +| Overcast | Clouds | +|---|---| +| up to 0.5 | fair weather cumulus, more and taller with the overcast, and a high cirrus | +| 0.5 to 0.9 | a broken stratocumulus sheet and altocumulus above it | +| 0.9 to 1 | an overcast stratocumulus sheet and altostratus | +| above 1 | nimbostratus, lower and denser the heavier the precipitation, with cumulonimbus above 1.6 | + +The visibility is 2.5 times the old fog range, divided by the overcast as the old fog was. The old fog thinned out +a dark object to 5% at 1.7 fog ranges, the new one at the visibility: the two cross about where the old fog took a +third. + +The cloud model of the `sky` section (`Global.asSky`) and the model of the starry dome (`skydome_stars.t3d`) are no +longer loaded: the clouds come from the weather, the stars from a catalogue built into the simulator (the Yale Bright Star +Catalogue, 5th edition, about 9100 stars down to magnitude 6.5), turned with the sidereal time of the scenery. diff --git a/environment/sky.cpp b/environment/sky.cpp index f3f09f82..600535c0 100644 --- a/environment/sky.cpp +++ b/environment/sky.cpp @@ -17,11 +17,9 @@ http://mozilla.org/MPL/2.0/. void TSky::Init() { - if (Global.asSky != "1" - && Global.asSky != "0" ) { - - mdCloud = TModelsManager::GetModel( Global.asSky ); - } + // the clouds are worked out from the weather (environment/weather.h) and drawn by the renderer; the cloud model the sky + // section of a scenery names (Global.asSky) is no longer loaded + mdCloud = nullptr; }; //--------------------------------------------------------------------------- diff --git a/environment/starcatalog.cpp b/environment/starcatalog.cpp new file mode 100644 index 00000000..5537803c --- /dev/null +++ b/environment/starcatalog.cpp @@ -0,0 +1,1168 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "environment/starcatalog.h" + +namespace +{ + +// generated from the Yale Bright Star Catalogue (BSC5): right ascension, declination, magnitude, colour temperature +catalogue_star const STARS[]{ + {129,4523,670,975},{127,-50,629,510},{133,-571,461,500},{142,1340,551,550},{157,5844,596,550},{158,-4908,570,590},{161,6420,559,1200},{165,2902,613,500},{171,-2311,618,825}, + {183,-1739,619,850},{193,-255,643,1400},{195,-2251,594,950},{201,-3353,568,485},{205,-245,607,470},{210,2909,206,1400},{207,-882,599,520},{217,3663,619,630},{214,-1758,606,350}, + {222,2546,623,500},{233,7971,601,825},{229,5915,227,720},{226,1821,553,510},{226,-5400,633,560},{234,-2799,542,720},{235,-4575,388,500},{251,1115,551,1200},{258,4607,503,720}, + {262,5717,674,1600},{258,-525,584,485},{251,-8222,528,520},{268,-1258,585,500},{266,-7322,664,720},{282,-1547,489,645},{289,-2780,541,425},{293,-3513,525,690},{300,4815,616,440}, + {304,-1794,525,425},{321,3769,673,2600},{331,1518,283,2600},{335,2699,630,600},{338,4104,572,750},{343,-2602,594,425},{343,-2628,631,425},{351,3321,625,975},{365,2021,480,320}, + {362,-778,512,305},{333,-8499,577,350},{366,-1893,444,305},{373,2228,624,975},{374,882,579,750},{373,-957,575,1200},{378,3154,645,425},{379,2728,635,975},{374,-3490,617,455}, + {406,7695,635,1400},{409,4359,615,950},{404,-3145,567,425},{398,-7591,649,520},{414,824,611,520},{424,6153,574,520},{418,-2021,647,1400},{429,4795,589,1600},{427,3868,461,950}, + {420,-7878,677,705},{443,5143,614,3300},{439,-1905,645,690},{445,169,617,500},{458,3679,452,950},{457,1121,605,500},{468,4379,611,1400},{466,3152,587,1000},{467,-805,646,600}, + {468,-4324,633,470},{486,-882,356,485},{492,4073,633,485},{502,4887,652,1000},{502,-6487,423,615},{510,3094,590,1600},{519,3291,579,425},{515,819,537,455},{523,1098,656,1000}, + {528,3797,518,675},{516,-6962,551,1200},{538,-2898,518,550},{544,-2006,512,275},{561,1348,623,455},{537,-7743,597,455},{572,-1221,639,580},{580,-3104,655,1200},{601,3858,739,260}, + {606,5202,557,2000},{620,6183,540,1200},{612,-222,607,485},{628,5305,574,1200},{635,194,577,550},{644,-7725,280,580},{657,-4231,239,500},{655,-4368,394,825},{666,-5,619,520}, + {681,-2555,598,520},{701,1789,506,305},{706,4439,517,950},{698,-3301,481,290},{705,1644,606,425},{708,1019,604,660},{709,-2033,643,600},{711,-3992,543,350},{724,3690,626,550}, + {718,-5053,626,500},{773,7702,621,500},{758,5998,594,1200},{753,2975,523,825},{747,-1486,614,720},{745,-3212,657,440},{751,-396,572,350},{759,-2379,519,875},{762,-4094,619,720}, + {761,-4822,569,720},{786,6652,618,1800},{786,3358,587,485},{794,5452,473,1400},{792,5284,560,470},{785,-4880,477,1000},{789,-6296,437,1200},{789,-6297,454,950},{811,4349,670,975}, + {833,7098,642,1000},{825,6293,416,2800},{815,2029,538,500},{810,696,567,1200},{814,2758,667,950},{820,2828,630,500},{829,5490,593,500},{818,-6303,509,1000},{860,6675,648,1200}, + {842,-2956,555,470},{835,-7127,613,825},{862,-5237,557,705},{873,1337,640,500},{881,-359,520,630},{889,-51,593,675},{903,5417,508,1600},{898,1321,641,645},{911,6033,579,900}, + {892,-4800,551,660},{889,-5482,606,500},{908,2725,650,1400},{901,-1497,645,510},{903,-2284,606,850},{919,4449,513,425},{924,5390,366,2600},{922,3372,436,2000},{920,1523,589,2600}, + {928,2401,647,470},{934,3540,548,580},{995,8249,640,660},{934,-2477,557,520},{916,-6512,642,470},{938,314,639,440},{933,-5439,641,500},{964,2931,437,540},{979,4935,543,395}, + {983,3086,327,455},{984,2125,587,500},{998,2144,536,500},{1013,5654,223,500},{967,-7314,685,975},{999,-3396,669,630},{997,-4480,601,485},{1012,-1652,649,550},{1014,-2380,614,570}, + {1018,-435,591,500},{1028,3946,533,520},{1011,-5945,589,590},{1051,6615,583,510},{1040,2463,604,825},{1052,5051,480,2600},{1033,-4608,459,520},{1063,5875,617,1200}, + {1044,-5650,570,875},{1087,4702,494,875},{1068,-3846,606,1000},{1067,-6026,598,425},{1062,-6547,539,660},{1090,-1799,204,510},{1111,4786,567,2000},{1096,-1201,602,470}, + {1084,-5746,436,1000},{1141,7499,566,950},{1118,4828,454,2000},{1105,-1061,476,500},{1105,-3842,590,485},{1132,5522,542,950},{1118,-2201,524,735},{1124,-4268,594,750}, + {1113,-6250,607,675},{1166,6932,633,705},{1135,-463,615,350},{1125,-5372,615,645},{1137,-1288,615,600},{1156,5531,652,950},{1155,4486,605,1200},{1142,-1642,647,735}, + {1168,5957,639,600},{1194,7485,541,1400},{1144,-4755,580,590},{1155,-2252,550,570},{1164,1548,538,290},{1204,7268,587,500},{1176,1197,550,520},{1181,1958,613,750}, + {1183,2427,406,485},{1185,674,599,520},{1198,2093,654,630},{1193,-1806,570,455},{1228,5782,344,615},{1200,-2172,557,1200},{1207,730,593,520},{1210,528,575,470}, + {1221,5097,489,1200},{1217,758,443,440},{1224,1694,507,630},{1245,4108,453,2000},{1236,-1356,559,425},{1231,-2414,590,470},{1224,-4670,627,520},{1247,2771,700,690}, + {1247,2771,710,675},{1239,-2336,628,925},{1268,6425,539,600},{1258,4500,615,1000},{1253,-1064,519,645},{1215,-7492,507,425},{1282,6181,607,470},{1274,5151,639,720}, + {1252,-4339,648,775},{1372,8371,562,900},{1289,5157,621,1200},{1267,-5099,522,720},{1283,338,637,500},{1327,6112,482,630},{1297,-4371,690,720},{1322,3855,669,950}, + {1317,-2401,546,470},{1325,-114,477,350},{1337,3742,606,455},{1345,5269,627,875},{1330,-2478,646,660},{1310,-6950,622,645},{1375,5897,483,470},{1365,1919,574,975}, + {1357,-874,616,550},{1377,4868,627,320},{1341,-6287,570,290},{1374,2363,547,485},{1381,2456,620,290},{1405,5800,621,455},{1423,6878,637,690},{1399,2721,609,875}, + {1393,-735,585,425},{1418,6072,247,3000},{1417,5918,463,520},{1420,6036,555,1200},{1401,-1127,531,440},{1398,-2778,610,350},{1419,3850,387,875},{1375,-6953,545,470}, + {1430,2342,442,520},{1442,4584,612,470},{1463,6635,597,1000},{1446,2899,542,540},{1456,3395,598,500},{1448,1370,632,520},{1458,2140,637,950},{1513,7098,639,900}, + {1468,-1138,561,530},{1465,-2936,431,1600},{1459,-6070,623,875},{1501,4471,684,950},{1501,4471,604,1200},{1496,648,611,320},{1719,8626,425,470},{2346,8902,646,925}, + {1558,5104,647,925},{1533,-3892,559,500},{1576,4738,645,775},{1573,4134,598,875},{1570,3180,550,1200},{1590,6108,592,750},{1561,-3155,550,950},{1574,789,428,500}, + {1551,-5700,611,520},{1576,-484,543,500},{1608,6158,584,660},{1601,5250,599,470},{1571,-4640,536,520},{1582,-2953,629,550},{1595,137,604,735},{1619,5101,654,2400}, + {1612,2966,619,645},{1568,-6546,621,320},{1615,3999,672,750},{1906,8715,625,470},{1622,566,600,425},{1627,1495,568,690},{1660,6276,654,875},{1642,2147,534,975}, + {1642,2147,556,1000},{1730,8001,629,540},{1645,491,635,705},{1646,491,725,660},{1640,-998,612,500},{1675,5693,643,470},{1652,-984,558,975},{1679,5350,638,470},{1664,1296,612,520}, + {1653,-2399,614,520},{1707,5492,517,550},{1652,-4672,331,520},{1661,-3566,661,975},{1700,4394,503,925},{1680,-2400,637,825},{1714,5826,579,1400},{1701,3201,625,675}, + {1699,2074,555,925},{1694,-979,582,645},{1709,565,552,750},{1695,-4149,521,925},{1683,-6178,537,550},{1807,7967,564,925},{1715,-1018,345,485},{1738,4724,425,1600}, + {1766,6878,529,1000},{1743,3562,206,350},{1710,-5525,392,1800},{1745,1966,555,600},{1758,4208,565,630},{1758,2546,580,395},{1786,6420,555,1200},{1778,5515,433,825}, + {1755,1567,606,350},{1792,6502,557,1200},{1755,-891,640,500},{1764,245,595,440},{1779,3772,581,1600},{1778,3142,516,750},{1753,-5769,641,540},{1786,2103,466,520}, + {1792,3009,451,500},{1793,-225,594,440},{1829,6171,641,1200},{1814,4534,611,335},{1825,3006,619,510},{1913,7991,626,675},{1810,-3080,652,645},{1819,-3786,592,750}, + {1844,2458,465,500},{1843,758,524,825},{1844,758,630,645},{1852,2853,643,305},{1853,1613,598,1400},{1905,7174,783,485},{1860,-792,513,675},{1868,700,603,540},{1871,-97,570,675}, + {1910,4808,661,1200},{1880,-4553,496,630},{1908,3311,602,485},{1927,4490,634,425},{1915,-250,541,550},{1935,3174,673,500},{2008,7757,631,550},{1954,4742,625,500}, + {1894,-6888,486,660},{1945,361,516,925},{1970,3739,646,875},{1926,-6640,624,1000},{2050,7624,638,750},{2002,5823,498,750},{1987,2726,476,925},{2027,6466,634,950}, + {1995,-51,587,520},{2094,7873,607,1000},{2014,-325,623,485},{2012,-1124,615,485},{2028,2874,523,425},{2058,4553,488,500},{2084,5814,645,690},{2065,173,620,425},{2064,-45,649,500}, + {2063,-1908,635,660},{2092,3772,558,950},{2091,3425,629,520},{2085,2047,597,425},{2144,7098,649,1000},{2148,6813,474,500},{2088,-3095,584,425},{2101,-801,621,750}, + {2101,-818,360,500},{2145,6024,268,875},{2109,-691,591,705},{2117,-1566,614,550},{2120,-285,615,520},{2140,2351,618,675},{2117,-4149,542,500},{2158,4346,596,645}, + {2154,3458,631,645},{2117,-4453,626,485},{2140,-1460,490,470},{2156,1917,538,720},{2167,1924,550,485},{2178,3438,627,645},{2161,-40,641,500},{2191,4541,483,675}, + {2186,4110,646,825},{2172,354,658,1200},{2127,-6437,593,425},{2172,-1306,566,750},{2195,4034,660,1000},{2174,-3254,579,360},{3795,8926,202,645},{2194,-1090,613,440}, + {2210,796,620,485},{2281,7026,581,660},{2272,6610,614,950},{2209,-4332,341,350},{2253,4701,527,500},{2218,-3376,658,750},{2247,1836,602,900},{2240,-2163,512,975}, + {2255,614,484,440},{2238,-4676,631,320},{2260,-2621,593,440},{2287,1535,362,530},{2303,3480,639,1600},{2336,5833,570,500},{2281,-4907,395,500},{2293,-3028,582,500}, + {2348,5923,471,510},{2291,-4558,617,950},{2326,-901,659,1000},{2323,-3687,551,500},{2357,3724,588,1200},{2315,-4973,628,690},{2343,-703,576,580},{2434,7430,658,1200}, + {2370,1846,589,320},{2366,-1568,563,925},{2397,4108,638,485},{2371,-3189,612,500},{2411,4872,592,485},{2394,1466,622,1200},{2463,7304,528,520},{2398,1743,580,750}, + {2420,4141,409,630},{2400,-1540,542,470},{2381,-5814,601,720},{2453,5798,556,550},{2404,-2991,569,720},{2427,1214,557,750},{2450,4863,357,455},{2463,4540,636,1200}, + {2441,-940,624,645},{2341,-7850,611,500},{2419,-5827,618,305},{2484,4439,498,520},{2505,5387,639,425},{2461,-3653,594,500},{2443,-5724,46,2400},{2472,-2128,558,900}, + {2471,-2502,670,1200},{2492,1641,597,470},{2517,4330,561,775},{2514,4058,494,1400},{2559,6804,559,1000},{2470,-5344,684,675},{2573,7062,518,1200},{2551,6104,671,1400}, + {2557,5863,637,1400},{2545,4261,495,590},{2533,2575,617,720},{2541,3005,599,500},{2495,-5620,587,500},{2495,-5619,576,425},{2574,6142,634,1600},{2536,549,444,455}, + {2551,3525,564,1200},{2583,6055,578,1400},{2544,-1132,575,675},{2562,2027,524,485},{2536,-3813,617,720},{2582,4532,634,470},{2592,5069,407,2600},{2554,-3233,525,500}, + {2551,-3683,572,975},{2607,5754,621,925},{2568,-369,499,455},{2542,-5004,664,925},{2619,5709,625,950},{2596,3219,634,520},{2611,4614,635,425},{2545,-6079,571,470}, + {2562,-5374,552,615},{2598,-477,619,500},{2623,2008,627,570},{2602,-1594,350,520},{2635,916,426,520},{2694,6385,563,500},{2448,-8298,587,580},{2650,-573,534,440}, + {2641,-2505,531,735},{2665,1741,655,775},{2534,-7915,633,540},{2650,-2735,639,720},{2695,4623,632,660},{2652,-5082,549,305},{2653,-5352,504,975},{2716,3795,594,500}, + {2705,1696,584,1200},{2717,3269,579,630},{2682,-4176,618,425},{2437,-8477,569,520},{2732,4790,582,925},{2711,369,591,510},{2695,-3716,632,500},{2774,5193,590,675}, + {2754,2228,586,485},{2740,-1069,467,705},{2733,-3107,634,470},{2800,5515,552,2800},{2772,1104,594,720},{2745,-3840,637,630},{2804,5079,579,1200},{2758,-4782,614,485}, + {2821,5147,626,925},{2786,-1034,373,500},{2845,5560,645,875},{2773,-5021,594,975},{2860,6367,338,2400},{2832,4073,540,485},{2827,2958,341,660},{2838,1930,483,1200}, + {2838,1929,475,975},{2822,-1693,580,750},{2900,6869,499,1400},{2839,319,462,500},{2694,-8018,606,750},{2872,4070,624,470},{2874,3713,626,500},{2866,2081,264,875}, + {2835,-3859,610,485},{2841,-4630,441,290},{2898,3728,589,350},{2904,3725,567,500},{2859,-4250,511,925},{2896,2358,574,485},{2897,185,601,645},{2964,6170,602,2000}, + {2948,4169,678,1400},{2934,1782,510,485},{2943,2780,582,320},{2917,-2253,485,440},{2899,-5161,370,520},{2991,6462,526,1000},{2964,4920,569,500},{2948,2360,479,750}, + {2873,-6765,469,520},{2894,-6086,606,750},{3074,7792,604,500},{2925,-5177,610,660},{2929,-4738,483,520},{3049,7091,454,925},{2961,-3307,635,500},{2990,2106,587,500}, + {2986,1229,609,850},{3079,7385,623,875},{3086,7242,398,950},{3128,7728,538,750},{3004,310,588,580},{2994,-2082,541,335},{2947,-6542,637,520},{3000,-2108,400,350}, + {3072,6490,600,975},{3011,-852,551,305},{2991,-4203,557,485},{3075,6439,558,1400},{3058,5449,504,1400},{2969,-6157,286,750},{3138,7611,522,520},{2896,-7835,616,675}, + {3031,-3000,535,550},{3051,276,523,925},{3051,276,433,1000},{3236,8130,605,975},{3117,6510,652,1000},{3074,3328,550,950},{2992,-6607,610,485},{3065,1348,594,320}, + {3043,-4471,514,425},{3098,4233,226,455},{3098,4233,484,1400},{3091,2594,563,630},{3062,-2966,642,925},{3080,13,543,875},{3074,-1531,586,570},{3093,1825,621,440}, + {3095,-34,593,550},{3092,-410,562,425},{3112,-2930,469,1200},{3164,2265,503,950},{3155,825,631,320},{3170,2570,615,1200},{3162,3,628,500},{3179,2346,200,470},{3217,5842,567,975}, + {3214,4446,642,520},{3212,3786,482,875},{3253,5384,631,520},{3239,3499,300,875},{3236,2594,498,720},{3235,1722,643,675},{3219,-1778,610,320},{3334,7403,629,520}, + {3287,5765,636,2000},{3272,3904,563,1200},{3272,3904,610,975},{3239,-2435,648,690},{3266,1950,570,305},{3229,-4352,585,485},{3280,2594,602,440},{3285,3153,623,975}, + {3284,857,563,600},{3252,-4382,632,520},{3261,-5082,612,485},{3284,-1005,601,675},{3290,-183,593,510},{3362,6652,607,600},{3342,5856,644,925},{3309,3030,494,550}, + {3331,4423,483,455},{3316,2417,596,500},{3340,5107,531,520},{3320,2121,527,675},{3351,4748,606,690},{3326,1528,571,350},{3325,885,437,540},{3320,-239,554,630},{3325,-2100,586,510}, + {3323,-3072,528,1200},{3399,4781,633,510},{3422,5706,648,2800},{3398,3336,528,1000},{3394,2578,579,660},{3393,2504,558,660},{3387,-947,655,1000},{3363,-4117,591,520}, + {3426,3422,487,600},{3450,5790,575,455},{3452,5752,598,530},{3439,4431,670,245},{3433,3385,401,975},{3429,2377,655,510},{3425,-642,551,520},{3356,-6784,555,335}, + {3505,6434,660,1200},{3453,1990,562,975},{3482,4738,530,975},{3480,4647,621,900},{3451,176,558,600},{3484,4896,637,690},{3413,-5151,356,1400},{3474,2864,503,950}, + {3474,2317,646,975},{3491,3984,663,1400},{3387,-6775,569,470},{3517,4731,611,2000},{3502,3019,647,500},{3484,-298,304,245},{3524,5015,559,1200},{3474,-2595,634,550}, + {3492,-435,650,925},{3559,5585,517,950},{3485,-4185,637,470},{3571,4140,582,750},{3498,-5594,581,425},{3549,40,528,320},{3597,5536,628,645},{3555,-88,542,750},{3551,-1078,546,750}, + {3552,-1766,587,500},{3610,5001,519,520},{3564,-2382,520,600},{3632,5661,625,2600},{3574,-1835,622,550},{3555,-4320,631,500},{3641,5028,471,440},{3578,-3758,653,470}, + {3573,-5109,592,875},{3620,1061,547,1600},{3608,-2585,644,455},{3624,-278,633,1000},{3544,-6866,409,925},{3614,-4084,618,580},{3727,6740,452,875},{3649,-1229,489,1200}, + {3697,5057,612,690},{3650,-1534,583,850},{3678,2701,618,425},{3687,3180,554,485},{3665,-2004,588,470},{3622,-6031,535,720},{3572,-7365,501,485},{3735,5554,651,950}, + {3704,2967,529,750},{3704,846,428,1200},{3700,196,645,500},{3720,2993,589,600},{3675,-4770,425,2000},{3636,-6649,641,275},{3731,2347,619,875},{3701,-3381,514,950}, + {3740,957,607,470},{3757,3383,625,485},{3715,-3110,611,470},{3764,2524,592,660},{3766,1986,615,775},{3748,-2268,677,775},{3773,1770,623,550},{3769,26,600,825},{3756,-2519,651,775}, + {3702,-6430,637,1000},{3764,-2255,610,335},{3803,3615,515,425},{3788,227,525,455},{3822,3454,583,500},{3804,-104,535,570},{3802,-1524,475,690},{3823,1503,604,630}, + {3806,-3643,630,520},{3951,7282,516,520},{3828,-3465,590,500},{3842,-2000,621,500},{3887,3966,636,1200},{3940,6575,578,425},{3891,3731,571,455},{3846,-2823,490,1200}, + {3894,3469,535,305},{3877,747,618,540},{3868,-786,575,440},{3902,689,582,455},{3897,559,486,520},{3848,-5109,624,660},{3924,3873,590,675},{3918,3161,610,500},{3926,3426,530,290}, + {3900,-783,553,350},{3934,3990,654,2000},{3928,3289,625,630},{3839,-6259,677,1200},{3916,1245,568,645},{3924,2465,709,690},{3925,2465,650,660},{3915,773,581,500}, + {3904,-3004,575,520},{3957,3773,618,660},{3962,3809,630,1200},{3950,770,639,660},{3942,-340,565,550},{3924,-3458,579,550},{3970,2196,543,825},{4195,8145,578,520}, + {3965,344,621,510},{3792,-7911,528,520},{3958,-3019,583,690},{3935,-5254,531,850},{3987,33,407,2600},{3960,-3799,649,600},{3989,-1187,484,675},{4017,2706,530,925}, + {4007,611,625,705},{4005,-945,578,660},{4076,5511,577,1600},{4001,-3063,652,500},{4075,5353,584,500},{4056,4019,491,615},{3995,-4289,475,950},{4031,-70,571,645}, + {4121,6782,595,875},{4076,4827,648,550},{4059,2001,569,1000},{4017,-3986,411,500},{4045,-321,605,510},{4039,-1455,598,675},{4062,1074,630,950},{3988,-6428,655,1400}, + {4105,4923,412,630},{4102,4430,543,600},{4086,2771,466,2400},{4016,-5455,521,690},{4096,2564,635,950},{4083,324,347,925},{4053,-3838,601,520},{3990,-6827,411,1200}, + {4054,-4652,610,485},{4108,1776,646,470},{4114,1531,577,1200},{4064,-5080,541,600},{4103,-1386,425,1600},{4124,1245,518,825},{4124,1011,427,750},{4083,-4053,636,1200}, + {4223,6963,618,925},{4134,471,603,750},{4109,-3252,622,975},{4128,-1857,447,660},{4174,3598,625,520},{4176,3556,630,775},{4104,-5257,615,950},{4132,-4629,685,470}, + {4086,-6671,626,675},{4198,2925,451,485},{4238,5708,625,875},{4169,-2164,649,705},{4180,-2249,647,720},{4213,1828,582,485},{4299,6889,580,675},{4219,2519,586,1000}, + {4236,3733,645,660},{4198,-1246,690,290},{4136,-6370,574,520},{4267,5590,376,455},{4189,-3555,651,500},{4232,1746,522,1800},{4139,-6762,484,950},{4250,2726,363,1400}, + {4294,5831,645,975},{4265,3832,423,720},{4227,-3241,446,500},{4292,4684,588,520},{4288,3506,453,425},{4246,-2456,614,485},{4248,-2794,539,1000},{4322,5300,636,1400}, + {4287,1508,549,1600},{4256,-3584,592,500},{4334,4857,626,550},{4276,-2100,475,500},{4267,-3568,547,425},{4226,-6281,526,950},{4270,-3993,636,1000},{4356,5276,395,560}, + {4343,3834,533,690},{4330,1648,631,675},{4313,-1277,604,470},{4298,-3081,640,630},{4321,-944,632,825},{4399,6152,559,690},{4410,6433,624,455},{4340,-2238,595,500}, + {4339,-3844,592,660},{4421,5126,622,425},{4414,4716,602,455},{4308,-6291,603,485},{4395,1833,591,260},{4347,-4989,400,245},{4411,1802,563,660},{4406,838,597,645}, + {4353,-5087,621,440},{4262,-7507,475,455},{4432,3193,511,1200},{4411,-890,389,485},{4416,-371,517,975},{4451,3862,604,455},{4427,450,611,290},{4452,2067,580,675}, + {4469,3966,470,950},{4359,-6444,656,470},{4653,7942,549,335},{4477,3518,493,470},{4435,-2386,545,875},{4430,-2986,629,950},{4496,4722,547,550},{4492,4103,589,470}, + {4480,2134,463,950},{4480,2134,463,950},{4452,-2361,584,470},{4522,5235,528,1600},{4522,5235,674,1200},{4468,-278,523,950},{4439,-3819,641,1000},{4505,3813,611,1400}, + {4470,-978,614,950},{4493,891,470,1800},{4457,-4030,324,900},{4457,-4030,435,975},{4492,-247,556,1200},{4478,-2891,614,485},{4490,-2527,571,720},{4518,1087,595,410}, + {4491,-3251,631,1000},{4521,-288,611,335},{4548,2646,590,925},{4793,8147,595,825},{4529,-766,575,500},{4547,534,625,500},{4470,-6407,499,925},{4559,435,561,1600}, + {4557,409,253,335},{4548,-996,583,540},{4554,-649,619,600},{4541,-2809,589,550},{4620,5351,293,520},{4588,2827,636,750},{4568,-769,532,500},{4638,5671,476,510}, + {4560,-2362,409,900},{4642,5607,611,510},{4629,3884,339,290},{4683,6406,589,1200},{4634,4058,605,500},{4617,1586,649,455},{4607,-760,526,800},{4616,186,605,500}, + {4636,2526,680,1600},{4636,2526,700,1600},{4573,-4698,582,470},{4702,5221,631,2000},{4660,1319,562,455},{4798,7439,487,950},{4695,4731,641,925},{4590,-5974,511,750}, + {4664,-609,527,305},{4704,4096,212,1400},{4727,4961,405,600},{4686,1788,611,2800},{4556,-7190,553,2400},{4709,1880,627,350},{4737,4486,380,500},{4716,847,628,510}, + {4696,-2783,619,875},{4740,2908,572,1400},{4754,2782,642,1000},{4761,2690,602,455},{4782,3961,463,485},{4766,1187,598,1400},{4811,4773,633,425},{4804,4238,615,2200}, + {4791,1973,435,470},{4784,1305,612,500},{4765,-2374,638,500},{4806,2726,579,1200},{4783,-381,605,335},{4835,4818,590,510},{4782,-1603,626,500},{4811,666,556,485}, + {4696,-6927,615,485},{4761,-4873,612,485},{5008,7773,545,850},{4819,-120,506,630},{4802,-2899,387,630},{4895,5714,579,1000},{5308,8491,561,570},{4874,4250,607,470}, + {4938,6566,636,925},{4811,-4442,593,645},{4905,5094,503,540},{4826,-3594,627,1400},{4884,3056,552,975},{4873,2104,489,975},{4902,4535,616,320},{4841,-2980,616,485}, + {4895,3286,631,690},{4901,3469,625,975},{4814,-5732,574,400},{4915,3218,606,520},{4930,4048,645,1200},{4875,-2610,625,875},{4688,-7899,557,720},{4945,4402,547,1400}, + {4900,-592,617,1200},{4896,-882,480,875},{5000,6565,484,2600},{4944,3928,596,950},{4966,5022,515,2400},{4915,-915,614,735},{4978,5010,503,2000},{4905,-3083,665,1000}, + {4968,3422,482,470},{4959,-93,538,510},{4951,-2880,591,705},{4959,-2251,488,540},{4998,2707,590,540},{4984,337,483,550},{4967,-1856,571,750},{4936,-4775,585,470}, + {5008,2905,447,470},{5011,2566,612,455},{5047,4907,593,660},{5036,4333,495,925},{4988,-2176,369,305},{4990,-2412,561,320},{5031,2115,528,2000},{4944,-6258,554,580}, + {5028,368,569,520},{4998,-4307,427,520},{5117,6459,523,350},{4955,-6251,524,590},{5080,4921,529,2000},{5055,2761,552,550},{5019,-2661,639,645},{4950,-6693,605,925}, + {5069,2074,509,455},{5035,-2364,552,540},{5108,4986,179,675},{5057,-2559,635,1000},{5112,3354,561,1000},{5145,5392,651,775},{5039,-4778,639,500},{5108,2472,550,440}, + {5091,488,638,550},{5082,-779,620,580},{4899,-7739,552,690},{5129,4126,651,1000},{5111,2080,608,975},{5104,1263,604,500},{5149,4912,609,1600},{5120,903,360,540}, + {5094,-3271,650,455},{5258,7186,632,320},{5210,6026,649,1200},{5201,4906,498,2000},{5227,5994,421,1200},{5176,1876,657,925},{5222,4985,558,1800},{5179,973,374,1200}, + {5183,1274,628,1000},{5248,5888,454,1000},{5209,3381,561,950},{5148,-3592,639,975},{5255,5937,613,950},{5234,4951,467,2400},{5159,-2732,593,550},{5250,5545,509,975}, + {5236,4694,624,1600},{5211,2280,603,500},{5155,-4164,632,1000},{5200,-1129,573,470},{5265,4810,582,1400},{5264,4800,436,455},{5101,-6962,615,600},{5189,-3568,571,470}, + {5380,7335,657,1000},{5287,4921,629,1000},{5205,-3585,650,975},{5295,4940,639,1200},{5241,-680,599,550},{5260,1134,514,1000},{5240,-1267,559,875},{5304,4802,547,1400}, + {5140,-6934,596,705},{5284,2757,596,950},{5272,1294,411,500},{5269,619,594,520},{5338,5876,640,900},{5311,4606,531,690},{5265,-508,473,1200},{5342,5787,637,645}, + {5316,4486,641,2600},{5341,5497,598,925},{5317,3546,590,2800},{5248,-4263,578,875},{5256,-4137,612,645},{5375,6004,646,705},{5340,3990,581,875},{5315,937,577,1200}, + {5485,7574,627,510},{5265,-4738,599,975},{5297,-2561,638,925},{5234,-6294,472,675},{5323,-946,373,470},{5353,1783,617,485},{5361,2446,592,925},{5412,4819,423,2000}, + {5345,-2163,427,1400},{5370,642,649,600},{5314,-5038,568,470},{5366,-987,625,950},{5272,-6649,583,1400},{5349,-3108,620,675},{5458,5693,630,1200},{5364,-3187,640,455}, + {5322,-6102,641,500},{5450,4258,642,1400},{5399,-1119,557,530},{5420,59,571,510},{5407,-1747,523,1200},{5422,40,428,615},{5445,1543,639,875},{5475,2092,650,875}, + {5360,-6576,675,925},{5554,6322,510,305},{5427,-4027,458,485},{6251,8663,586,675},{5462,-739,585,550},{5250,-7835,570,520},{5486,1654,616,500},{5475,-563,596,485}, + {5568,5997,576,455},{5528,3758,557,1600},{5470,-2794,601,1000},{5496,306,557,540},{5491,-339,623,550},{5486,-1044,619,550},{5519,2533,611,950},{5500,-112,612,485}, + {5505,-1523,633,550},{5516,-521,553,1200},{5573,4779,301,2000},{5559,3397,497,3000},{5650,6720,580,750},{5531,-1180,649,675},{5558,1970,569,1200},{5603,4852,606,440}, + {5534,-1958,659,925},{5651,6334,480,600},{5617,4610,611,825},{5608,3229,383,2800},{5595,1966,614,520},{5613,3646,559,950},{5556,-3194,500,2000},{5630,4258,377,675}, + {5581,-976,354,500},{5612,2093,610,1000},{5731,7087,544,950},{5589,-1049,560,875},{5620,2429,546,1600},{5650,4568,566,1800},{5622,2411,370,1800},{5571,-3731,459,470}, + {5629,2484,564,1400},{5630,2447,430,1800},{5613,-116,525,1600},{5688,5592,610,1200},{5759,7133,463,950},{5646,2437,387,1400},{5624,-30,555,440},{5648,2455,576,1400}, + {5651,2453,643,1000},{5642,605,535,2400},{5412,-7832,629,470},{5738,6553,447,320},{5658,2395,418,1800},{5603,-4066,645,485},{5740,6330,585,875},{5654,680,591,510}, + {5708,5074,614,1400},{5733,5712,646,1000},{5654,-1210,442,320},{5697,3360,657,2600},{5695,3219,625,600},{5687,2410,287,1600},{5792,6851,632,1200},{5621,-4806,649,520}, + {5614,-5427,630,485},{5632,-4736,573,485},{5728,4396,602,775},{5661,-2934,590,925},{5709,2342,545,1400},{5671,-2325,423,705},{5707,1114,507,2400},{5605,-6481,385,470}, + {5752,4497,566,580},{5739,3309,511,925},{5729,2405,363,1400},{5683,-2990,655,630},{5730,2414,509,1400},{5692,-2387,524,950},{5716,23,591,455},{5743,2371,617,1400}, + {5698,-3017,554,540},{5748,2224,607,1400},{5696,-3611,621,1400},{5715,-2090,581,425},{5758,2558,526,950},{5715,-3762,540,975},{5715,-3762,473,1200},{5797,3436,577,2800}, + {5843,5798,580,875},{5790,2203,683,500},{5782,1305,630,1200},{5736,-3620,417,510},{5913,7182,634,875},{5802,3117,625,875},{5841,4865,576,500},{5800,653,567,2000}, + {5766,-3643,686,1200},{5829,1733,597,690},{5817,-536,548,1400},{5853,3188,285,2800},{5936,6307,503,1200},{5928,6111,500,455},{5830,-1843,622,425},{5899,4787,537,1800}, + {5681,-7424,324,320},{5885,3105,610,3300},{5915,5070,528,675},{5857,-295,614,950},{5857,-295,479,520},{5843,-2461,465,1800},{5841,-3473,511,1800},{5912,3508,549,2800}, + {5839,-4689,593,470},{5882,-1210,600,875},{5922,2248,563,720},{5860,-4036,571,500},{5946,4001,289,3000},{5927,2318,606,1200},{5936,2446,616,500},{5951,3481,653,800}, + {5926,604,609,950},{5916,-975,619,735},{5962,3884,630,485},{5864,-5269,646,900},{5974,3579,404,3900},{5992,3882,638,975},{6251,8070,510,520},{5951,-1351,295,350}, + {5972,-547,583,510},{5992,1033,637,675},{6031,3699,641,1000},{5988,-1257,560,425},{5902,-6346,614,485},{6015,1730,632,1000},{6020,1819,589,690},{6017,1249,347,2400}, + {5998,-2402,466,1800},{6151,6868,587,470},{6111,5916,506,750},{6044,1000,567,2000},{6038,-155,528,2000},{5968,-5710,605,720},{6017,-3049,593,950},{5969,-6140,456,320}, + {6166,6552,617,925},{6065,-27,538,675},{6007,-5156,651,335},{6079,599,391,975},{6109,2411,547,600},{6094,544,533,2400},{6099,820,546,720},{6165,5401,631,500},{6117,2208,436,500}, + {6104,283,536,660},{6085,-2014,646,2600},{6090,-2016,701,470},{6196,6233,699,3000},{6165,5035,429,1000},{6133,2201,590,550},{6104,-1659,639,470},{6022,-6216,451,290}, + {6109,-1279,561,500},{6033,-6108,497,440},{6117,-2038,613,500},{6165,2760,520,1200},{6175,2900,523,735},{6236,5991,628,520},{5563,-8526,641,1200},{6149,-886,626,925}, + {6217,4771,404,2400},{6144,-2051,634,550},{6141,-2765,559,735},{6234,5483,618,675},{6206,3773,609,485},{6215,3804,551,645},{6192,1516,601,705},{6200,1734,589,425}, + {6344,7213,603,485},{6322,6850,632,500},{6229,1961,550,470},{6226,1340,595,1200},{6185,-4292,659,500},{6275,3359,572,485},{6271,2648,541,720},{6232,-1639,537,2200}, + {6705,8381,557,2000},{6259,-692,544,520},{6214,-4586,659,720},{6283,552,572,690},{6270,-882,570,510},{6184,-6422,638,570},{6313,1728,609,500},{6376,5746,608,900}, + {6321,2241,612,1200},{6297,-684,404,720},{6269,-3527,644,485},{6290,-2036,579,950},{6350,3797,645,510},{6271,-4199,493,775},{6372,4841,414,600},{6750,8334,546,520}, + {6422,6185,570,2000},{6372,4048,471,550},{6340,1021,623,1400},{6338,889,651,950},{6339,772,529,720},{6346,1275,625,500},{6348,926,484,550},{6341,-115,644,2000},{6428,5786,571,485}, + {6418,5361,519,950},{6365,1001,522,1200},{6313,-4437,671,455},{6676,8082,543,540},{6360,-1026,487,455},{6394,1540,632,675},{6388,889,429,2400},{6386,620,693,550}, + {6387,619,631,600},{6340,-4036,637,455},{6456,5030,461,950},{6382,-765,443,485},{6350,-4229,386,485},{6517,6514,527,550},{6453,4214,622,1200},{6432,2058,494,925}, + {6481,5005,545,825},{6460,2158,565,750},{6433,-647,594,520},{6505,5092,555,2800},{6460,949,654,900},{6545,6074,539,350},{6361,-6247,335,520},{6506,4181,592,500}, + {6401,-5149,425,690},{6486,2114,535,1200},{6370,-6219,545,500},{6490,2177,538,1000},{6547,5651,588,925},{6510,3457,493,520},{6504,3195,616,425},{6457,-2072,600,520}, + {6495,1563,365,500},{6447,-3380,356,1200},{6509,2735,495,485},{6491,1012,631,500},{6539,4650,485,2200},{6499,1404,559,750},{6574,5962,619,900},{6466,-2297,607,875}, + {6510,1874,612,705},{6412,-5930,444,470},{6515,1510,526,750},{6409,-6095,637,875},{6522,1386,617,660},{6476,-3390,637,925},{6517,613,577,520},{6520,923,653,900}, + {6516,-625,627,520},{6518,-759,585,2000},{6482,-4427,534,470},{6467,-5286,609,645},{6536,-10,586,455},{6516,-2064,538,950},{6551,1408,572,925},{6565,2563,537,1200}, + {6559,2082,591,350},{6590,4243,623,975},{6442,-6326,587,1200},{6573,1754,376,500},{6538,-2573,601,750},{6589,2098,599,1400},{6585,1678,564,975},{6612,3413,573,1400}, + {6600,2430,636,2400},{6616,3396,576,690},{6602,1744,480,825},{6597,946,512,925},{6675,5759,632,1000},{6592,-375,517,950},{6577,-2489,583,425},{6624,1904,598,690}, + {6578,-3554,639,485},{6634,2229,422,825},{6635,2220,528,825},{6637,1793,429,950},{6653,3144,528,485},{6641,1594,646,645},{6658,2281,428,800},{6601,-3402,396,440}, + {6659,1562,449,750},{6547,-6339,524,520},{6665,1471,469,530},{6659,859,606,1800},{6624,-3476,655,720},{6682,2300,553,1600},{6675,208,623,485},{6838,7253,594,800}, + {6687,1121,588,1400},{6700,2162,572,875},{6633,-4416,639,660},{6605,-5707,629,560},{6722,3036,640,645},{6711,1636,497,470},{6710,1474,590,750},{6715,1918,353,510}, + {6674,-2408,611,950},{6714,1596,384,500},{6717,1587,340,825},{6702,186,615,510},{6721,1305,503,825},{6713,138,555,2400},{6627,-6124,594,440},{6801,5391,577,3000}, + {6678,-4695,610,675},{6766,3246,621,1200},{6743,1052,679,2000},{6716,-1946,596,485},{6754,1564,558,750},{6728,-1305,560,2800},{6785,4001,626,1400},{6751,1026,648,520}, + {6450,-8021,569,470},{6764,1619,478,850},{6766,1569,548,875},{6704,-4196,644,335},{6766,1372,540,750},{6754,-1359,624,550},{6797,1585,602,690},{6733,-4652,616,485}, + {6835,4306,609,750},{6694,-6252,575,500},{6802,541,639,675},{6797,-4,491,455},{6786,-1364,621,925},{6767,-3565,596,500},{6910,6426,594,975},{6816,-321,581,1200}, + {6839,1802,625,1200},{6771,-4495,507,2600},{6846,1484,465,800},{6866,2896,588,1200},{6845,941,601,520},{6834,-1079,606,500},{6853,557,568,950},{6848,-674,572,1200}, + {6856,-684,609,470},{6855,-823,511,305},{6855,-897,526,440},{6838,-2977,451,500},{6917,4126,425,440},{6893,1988,636,550},{6524,-8158,579,750},{6898,1651,85,425}, + {6891,1016,425,875},{6912,2334,602,675},{6881,-974,637,975},{6875,-1992,613,455},{6901,-361,633,1600},{6908,-335,393,2600},{6889,-3056,382,520},{6850,-5504,327,1000}, + {6999,5347,535,800},{6998,5308,505,500},{7150,7661,649,645},{6931,100,531,1600},{6962,2694,647,720},{6957,2068,592,1400},{6954,1603,579,750},{6954,1251,427,850}, + {6940,-247,523,750},{6839,-6282,579,485},{6921,-3072,630,1200},{6985,2522,622,875},{6979,1580,507,900},{6982,1592,469,875},{6978,787,539,875},{6954,-1430,387,470}, + {7035,4830,567,1000},{6972,-1212,501,950},{7001,1220,546,1400},{6571,-8290,676,825},{7083,5952,650,825},{6983,-1436,545,485},{6995,-105,610,500},{7046,3828,599,600}, + {7033,2862,578,950},{7221,7594,606,775},{6919,-6208,540,230},{7084,4997,587,1200},{7073,4336,529,975},{7003,-2448,558,540},{7011,-1967,432,290},{7056,2296,428,2400}, + {6977,-5167,644,470},{7081,2409,613,645},{7105,4079,608,1400},{7095,3287,645,800},{7014,-4186,445,720},{7051,-3714,505,735},{7008,-5894,653,550},{7089,-879,682,690}, + {7090,-880,670,520},{7111,1115,540,750},{7102,-850,590,2600},{7079,-3077,568,470},{7265,7094,637,1000},{7200,5676,530,925},{7143,2363,635,2000},{7103,-1867,553,925}, + {7168,4031,597,510},{7203,5560,626,750},{7069,-5048,531,500},{7157,1874,601,440},{7093,-4106,625,455},{7151,1171,537,950},{7138,-325,402,2000},{7127,-2128,572,470}, + {7160,-295,633,950},{7509,8119,507,455},{7146,-3401,686,1000},{7161,-2809,619,925},{7148,-3936,605,485},{7302,6351,544,305},{7233,3259,586,800},{7230,3144,558,470}, + {7109,-5973,527,800},{6959,-7766,605,470},{7190,-1693,551,580},{7248,3749,488,455},{7219,359,603,485},{7279,4874,566,510},{7215,-567,578,630},{7243,1590,608,455}, + {7214,-1633,577,660},{7196,-3002,637,485},{7124,-6323,646,500},{7077,-7093,554,1400},{7351,6634,429,3300},{7246,696,319,660},{7265,890,436,975},{7243,-1377,626,675}, + {7329,5284,641,950},{7284,1884,510,825},{7214,-4398,672,520},{7255,-1622,503,500},{7320,4259,571,925},{7316,3670,478,470},{7280,560,369,2600},{7293,998,611,2000}, + {7320,2790,597,720},{7376,5526,552,1200},{7313,1425,474,305},{7257,-4132,607,720},{7371,4406,608,1000},{7287,-3491,586,975},{7322,-545,439,690},{7403,5287,575,950}, + {7334,251,533,335},{7273,-5346,561,750},{7273,-5346,642,690},{7348,157,661,975},{7374,1949,637,705},{7356,244,372,2400},{7432,5375,447,975},{7370,1143,519,925}, + {7372,1015,465,1000},{7370,778,533,485},{7558,7427,606,425},{7408,3617,607,2600},{7371,47,599,2000},{7406,2459,637,750},{7396,1504,581,1200},{7425,3317,269,455}, + {7399,540,650,1000},{7378,-1674,570,510},{7409,1351,407,470},{7383,-1642,572,560},{7410,-517,551,1800},{7388,-2573,672,750},{7373,-3963,610,455},{7434,1715,548,485}, + {7445,2395,579,520},{7605,7376,666,500},{7494,5316,608,440},{7617,7407,596,975},{7454,2505,581,1000},{7432,-107,623,720},{7481,3789,494,975},{7540,6108,603,690}, + {7571,6682,619,660},{7444,-1423,615,2800},{7454,-221,635,950},{7372,-5855,612,705},{7338,-6668,641,350},{7508,3939,595,675},{7475,1454,609,1600},{7464,171,447,470}, + {7510,3966,658,440},{7585,6044,403,590},{7476,-1638,566,705},{7549,4382,299,750},{7327,-7241,628,660},{7490,-1481,771,360},{7496,-1026,538,560},{7514,362,703,1200}, + {7514,362,666,1200},{7498,-1254,479,750},{7562,4108,375,440},{7517,-207,632,800},{7520,-575,622,455},{7583,4144,614,950},{8295,8594,651,875},{7536,-717,481,2400},{7546,72,592,500}, + {7550,161,624,1400},{7577,2159,464,825},{7536,-2005,491,1200},{7654,5897,508,2600},{7655,5902,608,500},{7662,6117,604,425},{7569,-421,585,455},{7606,3049,614,500}, + {7615,3232,662,900},{7554,-2628,502,485},{7380,-7494,547,440},{7659,5441,724,550},{7539,-3972,603,550},{7616,2770,660,750},{7609,2128,619,500},{7569,-2280,575,485}, + {7560,-3177,594,520},{7740,6964,641,485},{7667,5160,500,750},{7614,1540,468,1000},{7650,3594,652,875},{7597,-1437,641,2600},{7663,4123,317,2400},{7638,1981,644,875}, + {7809,7395,543,1200},{7671,4317,620,720},{7597,-2439,561,925},{7623,-304,605,2000},{7744,6492,641,660},{7635,118,617,360},{7570,-4915,538,720},{7865,7647,637,1000}, + {7597,-4174,631,630},{7610,-3548,455,455},{7611,-3571,634,735},{7637,-2237,319,425},{7632,-2615,573,470},{7686,1864,500,560},{7669,-466,512,1200},{7695,2042,530,875}, + {7703,2427,550,2600},{7698,2170,589,2600},{7665,-1312,605,1000},{7691,947,617,590},{7624,-4958,503,425},{7697,850,534,875},{7685,-1249,597,645},{7696,-509,279,925}, + {7625,-5441,627,320},{7768,4696,568,675},{7758,3730,602,455},{7744,2803,601,875},{7708,-867,578,1400},{7733,983,543,950},{7718,-446,512,720},{7638,-5747,472,645}, + {7826,6185,617,950},{7743,1560,482,720},{7568,-7131,531,520},{7838,6269,650,825},{7729,-875,427,2600},{7706,-3572,652,485},{7751,-57,610,500},{7506,-7830,629,425}, + {7956,7327,574,1000},{7792,1605,518,425},{7774,-225,625,485},{8064,7923,505,660},{7783,-249,590,675},{7880,5941,615,470},{7836,3848,486,900},{7792,51,667,1200},{7794,104,589,550}, + {7868,5321,620,1000},{7784,-1185,568,260},{7769,-2591,641,485},{7689,-6340,520,305},{7807,-1187,445,1400},{7820,-606,591,530},{7832,286,446,500},{7790,-3740,657,425}, + {7654,-7304,627,975},{7838,197,609,875},{7823,-1621,331,1200},{7845,56,632,350},{7839,-815,637,2000},{7831,-1294,436,1200},{7885,3269,502,775},{7932,5359,650,245}, + {7917,4600,8,550},{7868,516,550,440},{7850,-1461,621,720},{7887,2228,627,950},{7908,3431,596,3300},{7863,-820,12,1400},{8595,8567,660,950},{7844,-3583,698,440}, + {7471,-8247,585,520},{7883,-141,615,750},{7902,1134,556,1000},{7987,5812,613,2000},{8009,6265,561,440},{7862,-3598,576,520},{7957,4279,548,290},{7885,-2694,507,1200}, + {7917,195,642,1000},{7967,4047,618,500},{7954,3337,454,470},{7847,-5203,605,455},{7958,3377,614,1200},{7979,4010,471,590},{7895,-3493,666,950},{7920,-1714,656,2400}, + {7975,3375,541,1000},{8001,4443,662,500},{7985,3399,649,825},{7940,-684,360,2000},{8016,4696,654,580},{7942,-1352,550,510},{8006,4109,552,925},{7982,2210,494,520}, + {8000,3396,503,875},{7981,2013,608,520},{7891,-5218,649,470},{7937,-3490,483,500},{7844,-6719,483,470},{8236,7798,656,825},{7980,260,534,675},{7971,-1813,596,600}, + {7990,-141,634,2800},{8045,4180,523,2400},{8025,2796,633,1200},{8087,5754,528,1000},{8030,2957,576,975},{7982,-1852,636,2400},{7983,-1851,654,1400},{8024,1981,618,500}, + {7989,-1318,429,3000},{8000,-1232,530,1600},{7985,-2737,599,1000},{8011,-537,639,1400},{8071,4103,554,925},{8033,401,657,2000},{8011,-2124,471,1000},{8043,843,580,2800}, + {8038,-42,568,2400},{8044,-38,473,2600},{8009,-3470,634,720},{7984,-5061,545,645},{8085,2894,646,1200},{8046,-1376,656,1400},{8071,354,500,2800},{8044,-2477,506,530}, + {8032,-3435,609,2000},{8116,3739,499,440},{8091,1670,608,950},{8116,3122,628,485},{8116,3114,594,1200},{8088,532,635,950},{8083,-842,590,1400},{8130,3486,655,485}, + {8111,1738,499,630},{8093,-16,570,2800},{8096,-87,611,645},{8088,-1393,525,2600},{8099,-781,414,520},{8080,-2671,649,660},{8115,235,632,2200},{8112,-89,508,510}, + {8112,-240,336,2800},{8119,185,495,2800},{8128,635,164,2600},{8157,2861,165,1600},{8112,-1698,565,975},{8085,-3968,571,335},{8173,3546,615,470},{8170,3439,594,1000}, + {8171,3326,615,500},{8091,-3734,682,485},{8152,1670,625,2600},{8126,-1033,561,425},{8138,-54,657,1200},{8059,-5613,611,1400},{8254,6307,542,335},{8145,52,616,2600}, + {8178,3021,574,1200},{8191,3448,507,455},{8151,-552,623,1200},{8166,687,642,1000},{8179,1796,542,2000},{8181,1526,616,975},{8191,2194,488,2600},{8171,310,459,2600}, + {8150,-1970,589,720},{8123,-4423,608,485},{8194,1587,550,1200},{7811,-8154,651,485},{8201,1724,577,335},{8177,-1190,635,645},{8119,-5232,627,1000},{8215,1368,635,900}, + {8201,130,641,2600},{8232,2515,547,1400},{8242,2919,624,660},{8190,-2138,607,530},{8270,4146,600,500},{8269,3983,637,510},{8224,-331,639,1000},{8177,-4094,587,825}, + {8314,5722,648,630},{8206,-2076,284,550},{8235,-345,579,485},{8268,2246,629,500},{8261,1536,594,925},{8248,179,578,2600},{8243,-109,471,425},{8206,-3723,557,975}, + {8158,-5891,514,540},{8258,420,621,470},{8228,-3012,675,520},{8270,595,420,2000},{8259,-743,633,2600},{8281,329,546,2800},{8318,3219,476,2000},{8493,7504,617,350}, + {8305,1859,438,320},{8337,4211,655,1200},{8306,1706,546,1600},{8284,-671,622,2600},{8278,-2086,555,1000},{8336,3280,648,950},{8300,-28,685,2600},{8300,-30,223,3300}, + {8432,6670,626,800},{8341,3473,627,925},{8298,-730,462,3000},{8254,-4708,546,570},{8431,6415,615,1000},{8338,1854,569,2600},{8175,-6862,603,750},{8341,2047,618,1800}, + {8317,-159,535,2800},{8280,-3547,387,485},{8328,-172,646,2200},{8348,1431,564,2400},{8318,-1782,258,750},{8415,5443,573,350},{8232,-6231,659,440},{8338,-116,534,2800}, + {8407,4772,611,750},{8290,-4593,586,455},{8349,141,659,2600},{8357,377,536,950},{8352,-104,622,2600},{8352,-147,593,425},{8386,2404,538,2600},{8370,949,441,3000}, + {8321,-3851,548,470},{8398,2766,627,500},{8378,993,354,3600},{8379,994,561,3000},{8328,-3514,578,500},{8257,-6393,619,750},{8381,1024,560,1200},{8422,4018,609,570}, + {9033,8518,611,350},{8375,-601,567,2800},{8376,-600,478,3000},{8347,-2985,653,1000},{8413,2594,649,675},{8384,-449,656,2800},{8384,-443,624,2600},{8385,-484,459,2800}, + {8382,-539,673,3000},{8382,-539,796,3000},{8382,-539,513,4200},{8382,-539,670,3000},{8385,-542,508,3300},{8388,-436,638,2600},{8386,-591,277,3300},{8390,-325,640,2400}, + {8391,-486,526,750},{8429,2692,583,1200},{8405,-120,170,3000},{8444,3356,633,500},{8427,1704,554,750},{8406,-565,654,2400},{8423,929,409,500},{8427,1104,594,440}, + {8381,-3308,578,470},{8441,2114,300,2200},{8415,-606,572,2800},{8344,-5490,643,630},{8433,895,612,2600},{8466,3049,540,615},{8404,-2871,626,950},{8561,6570,560,425}, + {8325,-6423,534,520},{8436,-594,605,2800},{8429,-1178,611,950},{8450,754,588,1400},{8474,2662,637,510},{8341,-6249,376,660},{8447,-481,619,2600},{8483,2922,596,2600}, + {8533,5348,623,485},{8432,-2787,616,750},{8401,-4731,611,470},{8493,2590,518,2400},{8486,2176,634,975},{8376,-5887,675,440},{8469,-260,381,3300},{8470,-259,665,2600}, + {8466,-657,596,2800},{8480,412,457,2400},{8444,-2869,531,690},{8374,-6118,632,550},{8472,-721,480,900},{8515,3136,604,1200},{8518,3192,611,320},{8488,-356,600,800}, + {8576,5658,605,500},{8488,-971,650,1600},{8604,6148,615,550},{8482,-1785,638,1600},{8534,2949,643,1400},{8532,1653,486,2400},{8468,-4071,582,1400},{8519,-194,205,3300}, + {8519,-194,421,3000},{8516,-282,622,2800},{8548,2333,659,1400},{8521,-113,495,2600},{8297,-7634,519,470},{8552,2266,636,470},{8527,34,593,750},{8491,-3407,264,1600}, + {8519,-1041,652,1200},{8496,-3263,545,540},{8542,-290,642,775},{8423,-6656,631,1200},{8583,2320,621,2600},{8542,-1673,621,2400},{8562,147,491,485},{8369,-7374,578,305}, + {8556,-1753,615,500},{8536,-3340,634,1000},{8572,-680,602,705},{8556,-2237,587,950},{8663,5612,594,900},{8579,-161,631,440},{8648,4983,547,950},{8555,-3054,619,1000}, + {8556,-3467,529,1200},{8646,4051,658,925},{8584,-1856,573,1000},{8727,6281,613,900},{8641,2070,695,400},{8626,401,609,750},{8681,4253,629,500},{8612,-2013,634,600}, + {8588,-3941,625,705},{8611,-2242,615,470},{8612,-2245,360,660},{8592,-4583,639,775},{8669,1582,600,1400},{8651,-427,634,485},{8672,952,579,520},{8665,117,595,560}, + {8680,1449,572,925},{8686,1773,549,750},{8390,-7882,605,1400},{8764,5692,654,900},{8693,1390,529,2600},{8823,6847,620,510},{8729,3918,452,520},{8650,-3231,517,3300}, + {8709,2087,607,1200},{8674,-1482,355,925},{8700,645,527,875},{8678,-1624,617,580},{8686,-1053,603,900},{8725,2457,486,520},{8773,5151,629,485},{8694,-967,206,3000}, + {8677,-2864,622,2000},{8807,5896,614,1000},{8715,-409,597,560},{8661,-4660,531,500},{8683,-3567,632,485},{8739,1265,491,1200},{8776,3731,474,350},{8787,3915,397,510}, + {8774,2797,556,530},{8751,987,580,520},{8619,-6574,435,825},{8762,1431,552,510},{8699,-4065,661,485},{8786,3212,625,305},{8755,442,597,470},{8682,-5107,385,875}, + {8740,-1448,549,540},{8429,-8047,565,590},{8680,-5436,618,455},{8762,202,598,520},{8816,3957,645,950},{8747,-2297,587,950},{8874,5989,520,950},{8817,3392,598,335}, + {8871,5571,499,950},{8810,1987,606,580},{8784,-752,535,2600},{8739,-4488,638,485},{8809,1417,559,1200},{8833,2761,458,1000},{8783,-2088,381,500},{8787,-2293,617,500}, + {8811,186,478,485},{8833,2030,671,1200},{8803,-904,597,950},{8774,-3577,312,470},{8940,6610,625,500},{8746,-5617,451,485},{8800,-2945,645,455},{8762,-5277,635,500}, + {8906,5180,649,875},{8875,3170,590,875},{8860,2028,441,600},{8856,1059,612,510},{8772,-5211,517,520},{8863,1176,659,950},{8857,323,631,500},{8874,1975,592,2600}, + {8814,-3763,563,485},{8927,4903,647,520},{8820,-3853,670,485},{8828,-3380,487,2000},{8868,97,600,500},{8864,-406,657,2800},{8256,-8478,620,1200},{8849,-1964,669,900}, + {8879,741,50,335},{8695,-7270,653,500},{8896,2018,540,260},{8747,-6690,511,1800},{8868,-1177,566,425},{8914,2894,632,950},{8901,1393,660,550},{8856,-2915,636,705}, + {8835,-4292,655,455},{8888,-462,587,470},{8890,-479,628,950},{8808,-5716,594,645},{8785,-6403,636,520},{8923,2425,602,1000},{8912,951,599,1000},{8921,1152,587,470}, + {8988,5428,372,500},{9129,7559,640,425},{8994,5532,644,875},{8995,5455,614,470},{8984,4992,589,550},{8872,-3996,557,350},{8854,-5036,652,520},{8950,2595,482,3000}, + {8910,-1417,371,735},{8906,-2284,596,500},{8887,-3712,497,485},{8988,4495,190,950},{8867,-4963,610,2400},{8914,-2322,636,500},{8998,4594,426,305},{8909,-3138,550,720}, + {8948,122,622,350},{8871,-5264,529,750},{8993,3721,262,1000},{9008,4459,622,470},{8955,-99,622,500},{8920,-3198,644,520},{8972,1281,570,470},{8960,184,590,875}, + {9024,4790,573,1200},{8853,-6309,465,485},{8971,55,522,975},{8855,-6448,663,750},{9043,4896,596,500},{8938,-3528,436,2600},{8975,-938,612,720},{8977,-956,503,850}, + {8991,-144,663,1400},{9029,3103,598,950},{9025,2757,605,1200},{9070,4991,605,1200},{9001,-307,453,485},{8931,-5343,645,425},{9072,4338,642,500},{9042,2240,637,1400}, + {8966,-4403,581,520},{9007,-1290,622,750},{9082,4291,610,500},{8979,-4282,396,500},{9128,5939,634,485},{9073,3264,624,690},{9112,5157,645,825},{9060,965,412,950}, + {8757,-7936,547,1200},{9166,6345,639,500},{9057,169,659,1400},{9046,-1060,495,2000},{9030,-2542,605,975},{9086,1969,514,1400},{9032,-3391,555,425},{9126,4298,587,705}, + {9085,1168,608,1000},{9103,2326,416,530},{9098,2014,463,2600},{9064,-1450,620,550},{9126,3796,634,500},{9020,-5122,567,875},{9139,3360,623,1200},{9081,-2628,504,455}, + {9154,3539,612,600},{9106,-671,521,2600},{9165,3848,536,900},{9124,542,567,530},{9124,416,563,560},{9159,2951,608,305},{9186,4185,612,500},{9125,-1648,493,950}, + {9108,-3217,565,2600},{9136,-1024,587,690},{9054,-6010,645,290},{9250,5894,536,520},{9210,4106,636,510},{9166,-419,538,2000},{9154,-1494,467,975},{9144,-2420,695,260}, + {9112,-4504,635,600},{9117,-4508,593,690},{9189,1477,442,2400},{9136,-3551,580,975},{9172,-1117,666,2400},{9120,-4846,658,540},{9163,-2311,547,875},{9152,-2976,581,975}, + {9321,6572,532,485},{9174,-2181,578,290},{9220,867,655,1200},{9192,-1917,531,320},{9238,2219,593,440},{9177,-3431,583,2200},{9167,-4230,612,875},{9294,5265,630,875}, + {9243,2311,575,2600},{9224,250,573,925},{9290,4871,682,800},{9290,4871,609,925},{9188,-3725,502,1200},{9176,-4509,651,485},{9240,-571,617,705},{9224,-2243,550,1000}, + {9197,-4215,550,1000},{9233,-1813,635,1200},{9239,-1458,556,470},{9276,1813,633,485},{9288,2442,580,500},{9245,-2277,571,660},{9214,-4436,627,1400},{9344,5117,604,485}, + {9308,3269,578,320},{9296,2187,656,350},{9287,1364,604,1000},{9245,-2670,627,520},{9301,1979,575,1200},{9154,-6604,571,1000},{9276,-675,615,1000},{9176,-6215,505,470}, + {9308,2291,639,335},{9301,1613,495,2000},{9298,1421,448,2400},{9264,-2715,572,485},{9392,6000,535,455},{9293,-467,618,1200},{9254,-4035,558,320},{9235,-4956,649,645}, + {9297,-655,505,2600},{9281,-2648,609,1000},{9339,1868,658,1400},{9330,1063,645,580},{9471,6932,480,1000},{9318,-250,662,950},{9267,-4528,631,1000},{9257,-5497,481,2400}, + {9319,-1776,652,2400},{9362,1791,588,875},{9448,6152,498,305},{9372,2251,328,305},{9391,3615,692,690},{9348,-374,583,520},{9384,2950,435,520},{9371,1916,520,660}, + {9218,-6884,506,1400},{9379,1385,591,2800},{9385,1614,530,1600},{9365,-457,583,2000},{9344,-2386,639,550},{9339,-2940,654,1600},{9371,-627,398,485},{9439,4642,652,750}, + {9394,1255,533,1200},{9408,2397,608,520},{9392,607,607,1800},{9395,428,664,2400},{9389,-51,565,660},{9387,-492,599,875},{9410,1718,639,425},{9398,117,637,675},{9386,-904,610,1200}, + {9491,5901,448,950},{9457,4636,638,470},{9424,2374,625,2400},{9411,1227,504,675},{9378,-2027,591,500},{9382,-1848,599,500},{9394,-1372,501,1200},{9281,-6559,501,320}, + {9409,108,663,1800},{9428,994,539,950},{9424,705,657,1600},{9403,-1662,592,2600},{9439,1406,659,1000},{9432,510,571,615},{9399,-2979,667,440},{9452,1438,616,925}, + {9426,-2272,607,600},{9440,-1073,675,750},{9414,-3514,437,500},{9552,5937,594,925},{9476,1732,632,1200},{9467,905,624,500},{9442,-1682,514,455},{9256,-7475,509,540}, + {9415,-3926,600,950},{9426,-3774,553,485},{9544,5345,536,675},{9429,-3725,587,925},{9456,-1997,552,2600},{9471,-939,536,485},{9470,-1502,606,350},{9502,1465,569,455}, + {9478,-859,622,1200},{9475,-2093,581,2200},{9530,2954,643,975},{9493,-782,527,2600},{9408,-5921,643,600},{9500,-294,490,335},{9522,1176,654,2000},{9536,1776,635,600}, + {9447,-5273,641,470},{9492,-3440,578,1200},{9536,227,631,900},{9470,-5036,704,580},{9508,-3006,302,2600},{9377,-7170,664,630},{9535,-1177,564,2800},{9706,7054,597,900}, + {9574,2251,288,305},{9565,1257,600,750},{9515,-3414,553,2800},{9622,4929,491,425},{9503,-4874,660,570},{9661,5628,564,925},{9583,376,640,2400},{9670,5842,521,440}, + {9568,-1796,198,2800},{9584,-469,667,950},{9553,-3344,385,530},{9622,2971,671,1400},{9594,459,444,875},{9594,460,672,675},{9601,888,626,1400},{9590,-987,619,425}, + {9622,1606,633,510},{9594,-1507,624,470},{9639,2333,606,950},{9604,-1153,522,455},{9595,-1979,660,1400},{9581,-3179,634,520},{9637,1472,624,400},{9609,-1296,612,2000}, + {9630,709,598,645},{9598,-2558,563,425},{9633,150,666,1200},{9632,-95,587,630},{9696,4741,656,1000},{9644,227,651,1000},{9600,-3671,562,540},{9645,-389,635,510}, + {9618,-2878,639,580},{9690,3256,643,500},{9573,-5637,561,875},{9650,-789,640,900},{9591,-5218,598,520},{9619,-4028,631,1200},{9666,-151,587,925},{9664,-460,615,2600}, + {9599,-5270,-72,750},{9674,84,671,975},{9669,-751,627,1000},{9637,-3506,625,455},{9699,2050,622,950},{9770,5816,588,500},{9714,3049,555,630},{9684,291,555,510},{9681,30,520,485}, + {9682,-28,555,425},{9595,-5854,648,925},{9576,-6368,627,335},{9751,4669,590,440},{9724,2697,647,675},{9712,1624,623,550},{9620,-5281,651,425},{9708,1030,615,470}, + {9724,2021,415,1800},{9699,-476,506,2600},{9606,-6028,580,1000},{10007,7960,654,975},{9707,191,648,1200},{9643,-4818,576,1200},{9680,-2586,607,615},{10113,8212,665,875}, + {9725,1102,659,775},{9566,-6998,556,425},{9678,-3790,648,705},{9611,-6343,646,570},{9731,265,616,335},{9720,-703,460,2400},{9721,-703,540,2400},{9721,-703,560,2400}, + {9716,-1747,577,550},{9623,-6383,627,1800},{9704,-3258,448,2200},{9752,903,657,1000},{10012,7800,573,425},{9716,-3237,574,2200},{9948,7370,624,720},{9779,1694,620,470}, + {9755,-1008,593,500},{9718,-4107,632,720},{9677,-5800,582,455},{9779,1125,614,2600},{9791,1590,640,800},{9811,3245,587,925},{9764,-1315,616,2800},{9791,1179,665,1800}, + {9795,1154,523,925},{9864,5535,645,500},{9637,-6969,538,520},{9810,1167,603,500},{9785,-1239,515,455},{9769,-2777,593,2600},{9796,-816,543,470},{9808,486,584,500}, + {9843,3302,642,925},{9745,-5024,527,720},{9823,733,450,1000},{9810,-587,560,750},{9796,-2342,433,3000},{9780,-3526,584,520},{9737,-5685,522,500},{9775,-4092,620,455}, + {9840,1416,553,470},{9820,-1117,624,510},{9790,-3694,634,335},{9942,6148,594,520},{9841,-122,510,2000},{10013,7175,592,500},{9816,-3203,569,2600},{9880,2802,527,1000}, + {9809,-3770,524,520},{9783,-5183,560,630},{10156,7956,545,630},{9941,5686,585,950},{9836,-2092,640,550},{9869,757,645,1000},{9914,3845,529,400},{9882,999,588,440}, + {9829,-3863,644,520},{9751,-6557,629,750},{9882,89,580,1400},{9779,-6188,615,2400},{9846,-3623,542,455},{9799,-5875,570,1200},{9900,450,655,1200},{9876,-2296,454,1000}, + {9865,-3272,562,1400},{9836,-5233,619,520},{9936,2459,644,925},{9915,-521,552,1400},{9966,3939,569,425},{9970,3990,520,1400},{9943,1640,193,1000},{9935,614,606,3600}, + {9909,-1866,570,520},{9885,-3678,559,1000},{9960,2898,579,1200},{9940,1085,638,500},{9983,4249,479,455},{9919,-1332,597,425},{9917,-1926,395,485},{9942,270,617,485}, + {9898,-3609,635,600},{9947,496,615,2800},{9917,-2262,635,2000},{9999,4401,641,485},{9874,-5298,439,1000},{9977,2203,604,510},{9942,-1298,612,500},{9989,2826,603,1600}, + {9988,2460,638,630},{9959,-254,614,470},{9971,470,657,1400},{9966,161,621,3300},{9947,-1824,443,485},{9926,-3815,604,500},{9921,-4156,634,500},{9931,-3699,571,1600}, + {9945,-3234,527,470},{9965,-1687,603,975},{9995,1298,597,950},{9982,-1415,482,470},{9944,-4320,317,1400},{10041,3593,646,630},{10034,2820,642,550},{10013,637,651,1400}, + {9990,-2370,605,1000},{10024,990,466,3900},{10034,1640,628,975},{10032,1100,611,335},{10077,4452,502,425},{9993,-3047,571,500},{10027,50,579,1400},{9966,-4822,493,520}, + {10105,5330,627,500},{10081,3715,619,500},{9999,-3816,658,485},{10060,1765,521,950},{10050,634,637,4200},{9950,-6153,618,590},{10048,-917,519,350},{10156,5944,487,925}, + {10105,3611,631,950},{10098,2513,298,520},{10078,303,619,500},{10031,-4035,612,2200},{10021,-4767,665,320},{10171,5717,535,500},{10100,1323,449,500},{10091,393,590,3000}, + {10119,2897,544,440},{10069,-2245,613,750},{10057,-3840,629,925},{10168,4358,525,600},{10132,1290,336,675},{10205,5570,633,660},{10205,5570,628,675},{10192,4879,522,500}, + {10085,-3919,630,800},{10148,1269,646,775},{10274,6757,514,2200},{10129,-1672,-146,975},{10112,-3107,520,2600},{10122,-2734,645,580},{10164,859,593,2600},{10135,-2346,605,500}, + {10126,-3059,654,1400},{10150,-1480,532,950},{10185,1819,620,950},{10134,-3179,592,660},{10138,-3095,580,2600},{10166,-1011,566,1200},{10183,804,477,440},{10171,-1443,529,1200}, + {10024,-7178,651,485},{10196,241,447,500},{10151,-3954,662,1800},{10190,-900,507,335},{10176,-2102,608,1600},{10155,-3778,621,2200},{10343,6889,512,1600},{10242,3261,571,455}, + {10136,-5220,657,540},{10208,-132,575,735},{10147,-5241,580,455},{10269,4178,502,455},{10227,100,615,2400},{10184,-3793,526,1200},{10246,1620,585,1600},{10327,5945,533,560}, + {10232,-227,574,1400},{10224,-1514,539,1800},{10172,-5127,540,485},{10167,-5469,646,540},{10261,1341,565,455},{10183,-5554,561,425},{10502,7698,455,440},{10243,-2408,633,975}, + {10289,2176,527,950},{10271,-54,577,720},{10090,-7312,637,520},{10328,4484,626,800},{10300,2360,565,425},{10268,-804,629,950},{10259,-1708,579,500},{10123,-7043,611,455}, + {10252,-2733,704,2600},{10246,-3251,396,2800},{10326,3887,612,720},{10320,3396,360,925},{10331,3844,630,675},{10326,3579,601,455},{10291,304,638,950},{10265,-2578,633,2600}, + {10260,-3171,570,1800},{10249,-4545,655,425},{10349,3851,648,1200},{10248,-4661,514,675},{10272,-3437,499,425},{10205,-6194,327,825},{10321,838,577,750},{10310,-532,630,500}, + {10248,-5061,293,485},{10246,-5362,440,540},{10334,1100,624,500},{10381,4583,634,500},{10381,4391,613,775},{10293,-3623,596,925},{10001,-8081,564,875},{10432,5842,435,550}, + {10430,5756,605,455},{10250,-6025,611,645},{10289,-4829,642,470},{10366,1318,465,750},{10333,-1903,564,735},{10334,-1893,614,875},{10325,-2696,640,290},{10413,4627,587,1400}, + {10383,2538,573,600},{10354,-585,641,850},{10339,-2022,483,2800},{10360,-113,545,900},{10423,4671,586,500},{10355,-1204,407,440},{10317,-4250,652,675},{10339,-2854,604,550}, + {10368,-176,621,2400},{10348,-2454,621,975},{10320,-4398,646,1400},{10353,-2418,387,470},{10534,7081,568,440},{10374,-280,604,540},{10355,-2393,691,1588},{10389,832,629,1000}, + {10440,4509,490,950},{10425,3368,589,550},{10320,-5409,657,550},{10376,-2040,574,925},{10411,996,592,1400},{10391,-2014,468,720},{10361,-4237,632,480},{10319,-5934,641,925}, + {10403,-1404,500,550},{10351,-5061,626,500},{10394,-2294,530,2400},{10403,-1705,437,2400},{10436,1191,627,720},{10398,-3179,636,2400},{10425,-818,634,500},{10476,3805,600,470}, + {10470,2608,610,645},{10286,-7096,540,1600},{10431,-2220,661,2800},{10419,-3534,629,470},{10487,2591,640,1400},{10466,762,627,925},{10439,-2463,546,750},{10407,-4872,495,335}, + {11513,8702,507,320},{10474,360,597,520},{10443,-2754,623,2600},{10432,-3551,623,630},{10483,732,635,1600},{10453,-2716,637,2600},{10507,1608,568,455},{10465,-2541,559,2400}, + {10647,7073,650,500},{10466,-2897,150,2600},{10460,-3411,506,2400},{10532,3241,659,775},{10468,-3100,642,2400},{10508,-537,630,600},{10491,-2160,626,2600},{10510,-841,596,1200}, + {10503,-2016,631,2200},{10467,-4577,622,1000},{10516,-920,649,2800},{10508,-2212,653,2600},{10542,482,663,925},{10560,2422,518,550},{10561,1776,594,335},{10557,1534,574,485}, + {10548,556,659,2600},{10466,-5573,627,470},{10564,1667,582,320},{10547,-135,617,550},{10518,-2849,627,675},{10465,-5639,645,720},{10548,-572,520,320},{10527,-2522,563,2600}, + {10521,-3347,640,520},{10651,5980,644,520},{10588,2934,593,560},{10642,5276,612,925},{10629,4778,638,1200},{10543,-2793,347,395},{10582,914,597,925},{10573,-424,499,2800}, + {10591,1095,513,455},{10603,2057,379,645},{10596,1259,598,425},{10521,-5140,514,335},{10576,-2383,302,2400},{10608,149,657,1000},{10602,-532,562,455},{10599,-1012,645,1200}, + {10594,-1563,412,1400},{10556,-4340,643,1000},{10633,2264,602,1400},{10655,3447,555,520},{10527,-5894,602,800},{10496,-6792,517,455},{10641,919,578,350},{10620,-2203,609,500}, + {10684,3401,591,440},{10601,-4234,520,875},{10599,-4361,554,570},{10600,-4361,679,500},{10685,2818,648,1200},{10646,-1066,649,3000},{10684,2270,768,305},{10597,-4958,493,900}, + {10706,3383,628,485},{10582,-5918,550,1200},{10715,3744,616,485},{10678,491,611,1200},{10638,-3478,614,720},{10667,-1129,539,3000},{10665,-1239,648,3900},{10650,-3066,634,2400}, + {10849,7182,635,500},{10696,747,575,500},{10608,-5675,517,1000},{10709,1593,544,520},{10651,-3838,611,600},{10672,-2496,608,470},{10632,-5036,646,455},{10675,-2666,662,2600}, + {10414,-7942,545,1200},{10684,-2384,571,2600},{10678,-4089,579,2400},{10753,2125,643,510},{10710,-2639,184,630},{10733,-1035,621,4200},{10721,-2404,665,2600},{10791,3932,490,440}, + {10779,3025,441,470},{10681,-5197,596,500},{10739,-1623,603,2800},{10785,2686,578,900},{10756,-424,492,500},{10721,-3966,483,2600},{10835,5143,547,305},{10743,-2523,569,2600}, + {10754,-1869,623,750},{10811,2413,585,675},{10785,-30,545,800},{10758,-2749,546,520},{11134,8126,631,1000},{10796,565,609,975},{10820,2722,643,630},{10656,-6884,647,500}, + {10803,547,616,500},{10797,-49,415,950},{10898,5964,520,470},{10792,-2088,584,1000},{10834,1616,500,290},{10805,-2594,592,2600},{10770,-4893,514,470},{10802,-3082,610,800}, + {10896,4724,558,600},{10861,2471,689,975},{10828,-1125,578,500},{10810,-2747,659,875},{10867,2488,582,335},{10811,-3654,596,2400},{10807,-4050,531,925},{10864,1212,562,540}, + {10858,311,535,470},{10835,-2267,601,470},{10855,-390,575,425},{10856,-995,590,455},{10845,-2291,636,2600},{10840,-2736,612,3000},{10718,-7050,569,720},{10719,-7050,378,500}, + {10939,5213,592,485},{10899,2790,571,335},{10862,-1032,603,3000},{10814,-4676,449,750},{10845,-3108,660,2600},{11277,8241,496,290},{10849,-3034,633,2600},{10883,-16,641,550}, + {10856,-2635,466,2400},{10831,-4518,489,1000},{10891,798,582,290},{10838,-4464,510,275},{10870,-2677,385,2600},{10871,-2704,558,440},{10963,4946,505,900},{10893,-1058,595,500}, + {10951,4088,578,875},{10801,-6319,602,1000},{10895,-2374,632,1000},{10884,-3069,536,2400},{10952,3096,624,1000},{10906,-1559,546,950},{10874,-4143,594,2200},{10932,668,665,1400}, + {10869,-4685,572,1000},{10866,-4827,476,1400},{10952,1654,358,925},{10915,-2332,479,455},{10938,-668,629,470},{10915,-2788,464,305},{10884,-5250,597,485},{10924,-3090,632,775}, + {10913,-3832,580,2200},{10921,-3659,503,2600},{10906,-4677,566,440},{11057,5990,635,800},{10929,-3710,270,455},{10945,-2680,646,2600},{11026,4266,635,500},{11032,4523,577,825}, + {11003,2198,353,720},{10984,274,589,510},{10995,714,591,630},{11003,1514,645,950},{10967,-2456,498,3900},{10968,-2495,440,3300},{11071,5528,653,1200},{11072,5528,545,1400}, + {10976,-1928,609,750},{10971,-2659,528,580},{10958,-3673,466,2600},{10987,-1640,570,735},{10952,-4399,585,1400},{10966,-3674,511,2600},{10964,-3921,525,975},{11056,3900,640,500}, + {11051,3676,513,500},{10981,-3373,630,470},{11049,2044,510,350},{11024,-1436,545,550},{11367,8090,641,530},{11032,-888,655,675},{11022,-2285,661,2600},{11023,-2696,601,2600}, + {11051,18,599,1400},{11027,-2589,587,290},{10921,-6796,398,660},{11124,5189,580,425},{11104,4067,519,485},{11051,-898,643,3300},{11058,-298,623,675},{11087,2505,503,520}, + {11186,6633,647,1200},{11087,2295,602,975},{11061,-598,582,705},{11056,-1902,496,1600},{11009,-5231,605,750},{11009,-5231,660,600},{11016,-5209,539,1200},{11114,2764,576,722}, + {11112,1552,641,2600},{11168,4921,464,975},{11075,-3192,543,2000},{11124,1167,530,875},{11143,2780,379,510},{11087,-2783,538,470},{11088,-3220,539,2400},{11098,-3022,660,2600}, + {11117,-1620,533,2600},{11107,-2291,619,1400},{11102,-2930,245,2000},{11141,928,499,540},{11099,-3584,631,1600},{11272,6847,564,470},{11121,-1901,624,950},{11128,-1375,578,750}, + {11146,-578,597,570},{11118,-3181,535,520},{11171,2154,654,660},{11162,1061,637,950},{11173,2026,593,720},{11151,-454,676,1000},{11133,-2198,605,800},{11167,1101,641,1800}, + {11136,-2522,578,1600},{11120,-3729,697,750},{11120,-3729,684,750},{11222,4818,572,1200},{11179,829,290,1400},{11194,2144,522,675},{11143,-3174,631,2600},{10174,-8702,647,705}, + {11248,4967,536,660},{11167,-2371,656,975},{11201,694,525,750},{11228,3178,418,750},{11178,-1786,563,875},{11204,893,432,455},{11175,-2309,561,3000},{11168,-3414,590,2600}, + {11234,2812,505,900},{11220,1511,622,1200},{11197,-1156,579,520},{11193,-2286,595,2000},{11245,2792,501,470},{11159,-5102,510,500},{11200,-2916,554,1200},{11245,1201,454,485}, + {11233,-191,559,425},{11236,-755,586,630},{11234,-1033,575,425},{11234,-1500,605,645},{11210,-3781,658,975},{11221,-3185,638,2400},{11221,-3185,713,2200},{11298,3890,654,950}, + {11227,-3146,577,2600},{11246,-2302,485,875},{11227,-3881,543,2000},{11271,-523,624,500},{11295,1709,542,470},{11231,-4330,325,425},{11321,2289,654,500},{11302,191,525,750}, + {11268,-3096,465,570},{11268,-3734,665,560},{11302,-888,590,675},{11250,-5265,587,500},{11286,-3615,668,2200},{11340,1583,525,975},{11330,329,559,720},{11221,-6451,639,425}, + {11293,-3589,661,1000},{11365,3189,288,950},{11365,3189,198,975},{11263,-5440,596,335},{11352,1057,628,975},{11420,5576,592,470},{11309,-3596,630,1200},{11379,3096,533,500}, + {11334,-1434,621,2800},{11398,4303,630,750},{11333,-1941,566,455},{11329,-2471,585,825},{11357,337,581,1000},{11345,-1452,497,320},{11413,4618,565,350},{11369,272,655,825}, + {11398,2690,406,350},{11351,-2230,445,660},{11331,-4006,626,1400},{11331,-4309,652,520},{11358,-2347,583,690},{11358,-2347,587,660},{11346,-3634,554,2400},{11362,-2612,665,1000}, + {11355,-3346,611,775},{11447,4877,592,875},{11432,4003,638,335},{11365,-2701,577,485},{11349,-3991,676,470},{11414,586,591,600},{11141,-7909,553,470},{11407,-831,627,470}, + {11402,-1449,570,2600},{11384,-2837,464,1400},{11403,-2216,634,550},{11464,3505,556,500},{11366,-5147,628,975},{11456,2436,627,750},{11432,-411,513,660},{11417,-1970,574,2600}, + {11521,5708,606,425},{11479,3458,490,705},{11480,2422,617,950},{11441,-1444,653,1200},{11432,-2378,637,750},{11392,-5253,494,455},{11506,3834,573,350},{11498,3201,617,660}, + {11434,-3497,453,1400},{11487,1767,505,425},{11530,4813,556,500},{11418,-4883,572,1200},{11401,-5589,639,485},{11444,-3528,660,485},{11483,522,38,675},{11457,-2536,470,1400}, + {11444,-3801,638,440},{11575,5871,499,925},{11478,-1868,672,1800},{11471,-2680,450,1800},{11471,-2680,462,2000},{11503,523,602,1000},{11524,2302,589,425},{11460,-3999,659,1000}, + {11520,1377,624,500},{11468,-3650,580,2600},{11464,-3878,619,520},{11486,-2686,650,1600},{11458,-4860,568,690},{11515,-819,601,925},{11510,-1526,494,455},{11506,-1966,593,500}, + {11486,-3831,484,2600},{11568,3400,602,645},{11493,-3814,573,2000},{11495,-3826,576,2600},{11547,1348,577,320},{11540,362,594,1000},{11551,1421,556,305},{11499,-3758,600,1800}, + {11602,5043,527,1000},{11531,-955,393,500},{11475,-5327,606,1200},{11518,-2795,676,2400},{11583,2888,428,485},{11522,-3166,656,580},{11667,6546,592,470},{11535,-2234,618,335}, + {11661,6283,649,750},{11584,2240,621,500},{11384,-7428,716,1200},{11384,-7428,726,1200},{11532,-3853,542,2000},{11577,19,619,500},{11603,2578,531,440},{11534,-4463,641,1600}, + {11611,2440,357,520},{11549,-3853,654,1600},{11606,1286,643,500},{11570,-2635,564,485},{11603,240,647,750},{11633,2803,114,500},{11629,2032,633,975},{11591,-2550,655,925}, + {11588,-2841,459,455},{11580,-3605,560,2000},{11578,-3886,689,540},{11595,-2895,396,950},{11907,8027,656,520},{11574,-4517,506,540},{11666,3752,518,320},{11402,-7763,618,350}, + {11593,-3820,640,1200},{11592,-4093,517,485},{11653,1851,488,440},{11614,-2467,562,2800},{11578,-4999,657,950},{11554,-5863,643,2600},{11604,-3606,580,720},{11657,1077,530,975}, + {11637,-1469,689,800},{11637,-1469,607,950},{11614,-3794,588,1600},{11572,-5823,621,500},{11688,3342,514,335},{11651,-677,549,425},{11649,-1456,504,750},{11627,-3789,654,2400}, + {11631,-3797,361,470},{11640,-3417,537,600},{11669,-1268,639,1000},{11633,-4375,603,1800},{11714,2314,618,580},{11654,-3793,588,1600},{11680,-2252,590,2600},{11546,-7261,395,500}, + {11664,-4006,657,2600},{11691,-1599,634,485},{11694,-1601,643,320},{11777,5413,602,660},{11699,-1219,548,675},{11726,1337,604,500},{11640,-5672,612,750},{11677,-3933,631,1400}, + {11725,433,653,580},{11702,-2594,450,3000},{11685,-3851,508,2600},{11618,-6607,638,540},{11688,-4661,523,2800},{11605,-6982,618,1000},{11815,5521,638,1000},{11776,3323,603,950}, + {11704,-4065,614,320},{11737,-1335,623,1400},{11726,-2491,533,600},{11742,-1723,518,455},{11732,-2486,334,540},{11708,-4708,471,500},{11754,-918,561,455},{11744,-2021,656,570}, + {11731,-3524,593,1200},{11770,328,618,485},{11752,-1952,612,500},{11740,-3329,560,425},{11799,1933,599,485},{11773,-1113,616,500},{11731,-4637,411,3000},{11708,-5647,633,500}, + {11737,-4475,632,500},{11730,-4686,584,2800},{11792,177,514,1400},{11775,-2453,645,1000},{11803,328,631,290},{11728,-5641,559,550},{11792,-1282,636,720},{11794,-1390,517,600}, + {11862,4739,625,470},{11868,4756,545,440},{11837,2677,497,925},{11793,-2117,563,520},{11768,-4458,645,540},{11730,-6028,578,735},{11760,-5051,591,470},{11820,-543,576,675}, + {11808,-1485,569,735},{11784,-4310,632,2600},{12005,7392,541,455},{11748,-6005,672,500},{11911,5650,672,950},{11792,-4289,604,2600},{11807,-3471,501,675},{11805,-4058,373,485}, + {11742,-6620,579,1200},{12120,7948,542,1000},{11892,3541,623,925},{11816,-3886,449,2600},{11826,-3636,543,500},{11892,1988,535,1000},{11888,886,586,720},{11812,-5437,570,2800}, + {11827,-4961,463,2800},{11833,-4810,424,3000},{11855,-3588,549,2600},{11867,-3485,615,440},{11910,449,617,520},{11957,4398,634,500},{11925,1579,578,455},{11881,-3092,644,440}, + {11932,864,605,500},{11932,113,635,645},{11910,-3029,633,260},{11875,-5258,638,1400},{11894,-4384,602,1800},{11921,-2288,420,645},{11952,721,641,1000},{11963,1652,599,500}, + {11872,-5730,563,455},{12034,5905,577,693},{11910,-4074,678,2600},{12422,8406,649,925},{12043,6032,601,950},{11959,222,529,500},{11924,-4241,609,455},{12063,6309,640,590}, + {11942,-3033,479,950},{11924,-4350,535,2600},{11990,1324,602,470},{11933,-4411,509,2600},{11919,-5298,347,2400},{11933,-4789,622,2000},{12065,5727,649,630},{11908,-6053,574,455}, + {11947,-4558,517,470},{11993,-368,493,470},{11977,-2331,511,470},{12023,2539,583,520},{12020,1982,625,485},{11921,-5913,625,350},{12025,2358,634,485},{12020,1731,555,455}, + {11956,-4924,441,2800},{12048,3541,634,500},{11997,-1840,461,950},{12043,2509,631,975},{11959,-5145,644,800},{12038,1646,599,1200},{12018,-288,651,2600},{12031,488,565,975}, + {11976,-4522,599,2200},{11945,-6030,560,600},{11930,-6330,614,1400},{11987,-3930,524,675},{12031,-139,468,440},{11980,-4998,632,2600},{11981,-4997,634,2600},{12046,891,622,600}, + {12057,233,439,470},{12008,-4546,661,485},{11971,-6082,581,2600},{12006,-4898,602,950},{12088,2779,494,485},{12061,-634,633,600},{12012,-4887,612,1000},{11992,-6021,633,2000}, + {11991,-6059,517,335},{12041,-3728,595,925},{12053,-3705,634,335},{12021,-5415,587,2000},{12035,-5452,610,2600},{12119,1884,615,1200},{12008,-6357,482,2400},{12077,-3246,582,470}, + {12038,-5546,628,2200},{12069,-4131,552,1200},{12127,1312,512,975},{12140,2753,621,1000},{12090,-4000,225,4500},{12087,-4295,629,520},{12179,4326,626,975},{12117,-1973,613,2400}, + {12158,2264,599,305},{12107,-3267,531,335},{11982,-7324,634,925},{12146,-57,641,500},{12211,5151,484,950},{12161,-924,623,1200},{12252,5825,593,440},{12194,2158,530,590}, + {12144,-3357,614,590},{12118,-5059,595,550},{12134,-4698,619,2400},{12127,-5311,553,455},{12235,4243,627,455},{12320,6847,532,530},{12183,-2055,538,875},{12218,1364,627,705}, + {12189,-2430,281,660},{12118,-6284,630,2600},{12167,-4527,505,455},{12215,-298,434,580},{12224,-1134,632,1000},{12218,-2036,636,825},{12261,2551,573,530},{12226,-1924,440,2000}, + {12284,3873,658,600},{12237,-1625,568,2600},{12216,-3768,637,2200},{12228,-3032,665,425},{12614,8243,632,950},{12274,1463,623,975},{12229,-3546,620,520},{12346,5645,585,510}, + {12282,982,607,1800},{12267,-1380,554,660},{12229,-4868,570,1400},{12240,-4412,521,2600},{12245,-4264,626,1600},{12237,-4735,427,2800},{12238,-4734,178,1409},{12305,1765,563,630}, + {12305,1765,602,615},{12306,1765,620,550},{12282,-1293,472,510},{12289,-777,536,540},{12243,-4794,523,2800},{12309,1400,654,825},{12329,2966,564,1200},{12488,7576,554,520}, + {12210,-6380,628,1800},{12239,-5609,566,925},{12276,-3729,644,3300},{12225,-6130,476,675},{12396,6038,645,825},{12325,1651,601,520},{12198,-6862,435,1800},{12342,2314,656,900}, + {12284,-3962,445,455},{12286,-4299,475,875},{12280,-4846,582,2400},{12355,1768,647,1000},{12333,-1579,499,550},{12319,-2991,652,975},{12359,1305,638,520},{12300,-4664,576,645}, + {12321,-3792,643,925},{12313,-4626,603,2000},{12446,5957,564,825},{12517,7241,598,350},{12337,-3590,478,2800},{12342,-3357,637,470},{12355,-3214,606,2800},{12349,-3632,508,2800}, + {12350,-3634,611,2600},{12355,-3549,578,485},{12351,-4035,444,485},{12340,-4699,513,2600},{12482,6251,571,520},{12457,5414,627,425},{12339,-5020,551,335},{12414,1173,713,245}, + {12413,919,352,440},{12360,-4583,583,2600},{12397,-3093,621,550},{12438,887,629,520},{12400,-3590,616,470},{12511,5774,589,690},{12423,-2132,660,1000},{12385,-5045,644,455}, + {12435,-1628,616,950},{12512,5357,649,750},{12460,-1263,598,530},{12382,-6292,516,950},{12449,-3000,645,500},{12502,2722,514,660},{12568,6063,641,500},{12509,2075,583,485}, + {12481,-1017,632,705},{12457,-3545,558,470},{12455,-3737,670,1000},{12513,2402,598,1200},{12496,395,605,520},{12464,-3666,445,825},{12505,-91,618,615},{12507,-533,613,470}, + {12487,-3459,643,1200},{12448,-5917,642,675},{12571,4319,425,440},{12511,-2292,613,500},{12595,5322,551,925},{12533,-160,650,950},{12534,-2008,558,580},{12458,-6561,507,485}, + {12548,-1759,575,485},{12535,-3305,483,470},{12534,-3648,520,2600},{12584,1833,595,775},{12563,-618,615,800},{12535,-3962,616,925},{12618,4200,602,425},{12573,-754,596,335}, + {12570,-1305,611,530},{12598,1063,608,320},{12571,-2635,590,705},{12627,3501,606,500},{12530,-5797,597,2800},{12563,-4849,482,2800},{12615,-472,601,720},{12582,-3829,632,335}, + {12615,-375,561,705},{12528,-6411,612,540},{12646,1705,614,660},{12573,-5212,585,1000},{12495,-7152,537,1200},{12500,-7151,565,1000},{12744,6730,588,520},{12662,2789,557,425}, + {12640,210,573,425},{12648,756,513,530},{12563,-5951,186,455},{12623,-2315,568,950},{12690,4565,632,550},{12670,2693,632,850},{12670,2694,630,925},{12667,2453,702,750}, + {12667,2454,781,645},{12641,-391,390,1000},{12627,-2405,528,425},{12633,-2105,601,705},{12641,-1744,644,500},{12463,-7692,407,675},{12668,1265,550,305},{12648,-1493,598,875}, + {12661,-399,559,875},{12624,-4277,598,2400},{12757,6072,336,550},{12667,-1253,554,470},{12682,-641,659,690},{12647,-4215,547,2400},{12657,-3906,653,1200},{12658,-3906,725,950}, + {12715,2414,610,750},{12638,-5173,517,2600},{12671,-2922,673,1200},{12822,6932,631,500},{12716,1421,595,875},{12552,-7340,529,1000},{12689,-2084,656,950},{12682,-3167,633,520}, + {12712,-252,639,875},{12708,-882,643,500},{12697,-2613,662,950},{12516,-7748,435,470},{12660,-5281,605,1000},{12721,-975,600,720},{12700,-3511,575,2400},{12715,-2307,651,950}, + {12722,-2095,667,1400},{12646,-6460,597,520},{12643,-6614,377,485},{12783,3727,618,1000},{12686,-5501,653,1000},{12690,-5309,509,775},{12814,5311,624,500},{12920,7472,631,950}, + {12736,-2733,670,2200},{12865,6515,547,950},{12788,2408,575,750},{12728,-4416,579,2600},{12790,1809,535,425},{12727,-4793,533,2600},{12736,-4472,499,2600},{12823,3802,590,470}, + {12798,981,683,1200},{12762,-3216,565,470},{12744,-4633,599,2000},{12762,-3672,669,320},{12834,3644,624,675},{12818,2044,533,455},{12788,-1958,542,1000},{12740,-5519,636,600}, + {12825,2408,636,510},{12682,-7009,553,1000},{12766,-4474,630,2200},{12817,1007,646,975},{12785,-3906,631,2200},{12814,-1503,638,750},{12780,-4787,639,2600},{12844,1326,628,500}, + {12868,3642,578,950},{12843,476,587,550},{12993,7363,615,530},{12856,845,603,705},{12827,-2461,619,800},{12787,-5439,634,455},{12851,-215,581,975},{12821,-3150,638,500}, + {12824,-3463,636,500},{12802,-5321,569,500},{12883,1959,658,600},{12833,-3837,649,2600},{12841,-3885,596,2000},{12838,-4697,624,455},{12980,6502,564,590},{12885,274,633,510}, + {12608,-8091,569,520},{12903,1531,632,750},{12896,662,599,630},{12896,662,725,550},{12863,-3260,643,550},{12887,-798,572,975},{12862,-3761,630,425},{12959,5340,566,520}, + {12979,5994,648,950},{12887,-2684,596,975},{13005,6433,460,485},{12880,-3997,647,590},{12975,5292,642,500},{12927,966,588,925},{12868,-4994,501,485},{12982,5271,591,485}, + {12958,3280,594,470},{12941,570,416,975},{12936,-493,619,500},{12952,957,653,975},{12897,-5097,580,1400},{12883,-5801,486,500},{12881,-5822,526,2400},{12958,-666,651,975}, + {12818,-7336,612,520},{12969,334,444,485},{12937,-3375,648,750},{12947,-2626,527,1000},{12933,-4015,655,560},{13025,4583,537,520},{13008,3194,610,675},{12985,802,645,485}, + {12967,-1974,633,425},{12941,-4299,414,850},{13003,2001,639,520},{13009,1967,644,510},{13011,1955,630,800},{12978,-2266,505,540},{13001,-1248,498,440},{12933,-6285,547,500}, + {12993,-2956,489,560},{12984,-3661,613,735},{12969,-5309,647,1400},{13075,4690,622,520},{13026,-905,663,975},{13003,-3531,397,530},{13008,-4026,520,1200},{12985,-5344,548,2000}, + {13043,-1594,488,510},{12999,-5306,519,2200},{12980,-6032,636,1000},{13015,-4519,571,350},{13016,-4665,384,705},{13054,-1197,645,875},{13007,-5292,362,2400},{13007,-5302,561,1600}, + {13082,2147,466,975},{13080,1268,564,925},{13104,3692,633,645},{13030,-4732,477,875},{13027,-4892,590,2800},{13081,340,430,2400},{13018,-5755,634,925},{13049,-4541,523,1800}, + {13015,-5976,433,2800},{13100,433,637,1200},{13092,-723,462,590},{12977,-7039,520,1000},{13117,1815,394,500},{13057,-4810,551,2800},{13074,-3594,642,1000},{13134,3070,613,550}, + {13119,1008,566,975},{13058,-5310,552,1400},{13061,-5311,486,2400},{13090,-3319,368,2800},{13126,568,613,850},{13221,6671,620,1400},{13059,-5577,629,470},{13134,-260,641,720}, + {13123,-2117,611,875},{13167,2877,657,925},{13167,2876,402,530},{13092,-4982,516,3000},{13110,-4265,407,550},{13151,-205,570,500},{13122,-3715,576,2600},{13153,-1101,625,425}, + {13173,1211,587,975},{13169,642,338,550},{13146,-2539,610,950},{13159,-1355,432,520},{13118,-5471,196,975},{13181,-190,529,925},{13151,-4604,391,975},{13160,-4113,621,1200}, + {13127,-5872,621,1600},{13171,-3462,637,2200},{13098,-6821,632,455},{13211,584,436,1000},{13202,-656,609,500},{13163,-4591,546,2400},{13112,-6583,605,900},{13183,-4616,575,720}, + {13192,-4174,636,600},{13168,-5677,449,2400},{13263,3329,625,645},{13234,-344,531,1200},{13204,-4246,643,2600},{13033,-7896,547,1400},{13200,-5285,630,1400},{13269,1883,616,950}, + {13334,6196,573,720},{13327,5906,625,675},{13244,-2105,647,825},{13299,4373,515,500},{13305,4531,599,500},{13276,1535,638,590},{13304,4200,599,500},{13246,-3278,521,550}, + {13251,-2946,587,520},{13241,-4032,548,950},{13259,-2862,617,1200},{13247,-3914,639,875},{13263,-2771,401,455},{13314,3247,566,800},{13245,-4531,493,950},{13312,2826,623,305}, + {13315,2833,595,520},{13289,-718,554,900},{13080,-7907,605,875},{13259,-4209,600,2800},{13310,534,633,875},{13264,-4653,510,3000},{13348,3554,614,900},{13313,-1323,613,500}, + {13287,-4250,655,975},{13416,6460,558,530},{13356,3058,539,530},{13311,-3251,650,470},{13316,-3655,642,520},{13320,-3872,582,350},{13290,-5763,559,1400},{13265,-6679,535,675}, + {13357,-543,600,580},{13316,-4836,591,2400},{13392,2793,522,520},{13385,1723,664,360},{13317,-5213,639,1000},{13148,-7950,579,425},{13246,-7255,611,875},{13421,4563,574,485}, + {13413,4020,589,705},{13385,595,311,510},{13346,-4045,647,500},{13327,-5665,603,1000},{13398,1163,541,425},{13346,-4752,533,775},{13387,-797,691,825},{13387,-797,667,950}, + {13380,-1824,575,470},{13424,3291,545,825},{13388,-2768,489,925},{13415,424,614,520},{13428,1714,617,485},{13398,-2382,639,825},{13345,-6035,578,2000},{13431,1532,520,875}, + {13383,-4504,626,2400},{13449,3023,629,660},{13414,-1671,596,500},{13440,1558,567,750},{13461,3580,651,950},{13442,939,619,520},{13373,-5824,638,2000},{13480,4804,314,825}, + {13380,-5497,571,660},{13376,-6064,384,1400},{13462,1186,425,875},{13453,154,659,950},{13408,-5272,469,2000},{13489,3242,520,520},{13564,6763,476,305},{13479,1813,638,290}, + {13468,-1613,586,660},{13516,4178,397,675},{13513,3760,644,425},{13884,8418,633,720},{13424,-5923,492,2600},{13448,-4857,587,520},{13492,-1921,618,660},{13482,-2881,625,520}, + {13542,3971,636,775},{13535,3225,582,950},{13472,-4723,518,875},{13545,2790,607,800},{13538,564,607,485},{13502,-4125,445,520},{13600,5428,575,950},{13509,-4317,607,2600}, + {13591,4716,360,975},{13568,2445,545,1000},{13549,-48,567,500},{13530,-2666,620,485},{13485,-5908,516,705},{13569,730,585,455},{13534,-4186,555,2000},{13604,2790,638,975}, + {13553,-3940,627,485},{13635,4853,595,660},{13519,-6096,579,520},{13544,-5219,523,1200},{13623,3238,646,800},{13579,-2550,674,1400},{13668,5934,645,950},{13710,6687,514,425}, + {13529,-6868,588,335},{13577,-5355,640,1800},{13663,3845,456,530},{13649,509,497,470},{13604,-4710,375,470},{13561,-6640,400,950},{13760,6713,480,660},{13686,2298,640,690}, + {13675,146,617,335},{13722,5160,448,750},{13702,3254,650,720},{13700,2965,543,520},{13620,-5785,644,800},{13694,1067,524,1400},{13773,6351,467,705},{13721,3388,593,615}, + {13720,2663,598,550},{13734,2205,514,510},{13701,-2586,458,440},{13664,-5580,611,2600},{13718,-859,560,1200},{13681,-5121,673,440},{13653,-6450,637,455},{13851,7166,655,520}, + {13700,-4343,221,440},{13744,1156,648,720},{13730,-1236,577,540},{13718,-2677,615,900},{13727,-1833,573,950},{13766,3096,595,260},{13759,2200,601,550},{13740,-879,546,520}, + {13641,-7054,471,2600},{13629,-7260,448,615},{13749,-3037,559,900},{13897,7295,596,750},{13760,-2318,653,975},{13744,-4942,648,775},{13859,6142,513,615},{13798,547,635,775}, + {13807,1500,651,510},{13805,387,614,1000},{13845,4322,532,1400},{13799,-1975,573,510},{13777,-4487,500,2000},{13811,-711,611,925},{13792,-3926,600,1800},{13841,2128,648,950}, + {13789,-4658,579,2600},{13774,-5897,344,2600},{13896,5674,527,425},{13813,-4361,557,1400},{13905,5402,483,875},{13782,-6232,397,2400},{13881,3463,597,540},{13859,231,388,1200}, + {13998,7402,650,520},{13836,-3862,631,1000},{13833,-4227,629,470},{13881,1494,534,485},{13839,-4734,592,1200},{13853,-4415,585,2000},{13823,-5941,554,540},{13860,-4323,525,2200}, + {13885,-1502,635,1000},{13938,4682,597,975},{13874,-3760,586,600},{13417,-8566,542,800},{13857,-5557,527,520},{13881,-4556,625,1400},{13917,-635,524,470},{13890,-3857,494,500}, + {13917,-874,547,1200},{13894,-3741,462,675},{13830,-6972,168,950},{13961,3536,575,800},{13928,-1457,584,500},{13902,-4490,604,975},{13946,1150,641,950},{13971,3680,382,925}, + {13882,-5839,602,1600},{13910,-4427,512,455},{13890,-5758,632,520},{13924,-3940,533,425},{13805,-7666,614,485},{13905,-5754,434,335},{14018,5127,613,705},{14043,5670,547,290}, + {13927,-5928,225,800},{13943,-5450,633,455},{14025,3819,612,720},{13989,-1131,662,950},{13952,-5105,526,1600},{13989,-1583,578,440},{14026,3439,313,395},{13994,-1198,479,520}, + {14036,3290,616,455},{13968,-5156,587,1800},{14012,-956,480,520},{13995,-3410,639,1400},{14031,1537,653,975},{13932,-6869,539,690},{13947,-6705,611,470},{14023,-1562,633,660}, + {14018,-3176,682,1200},{14012,-3758,605,455},{13989,-5519,628,2000},{14037,-2597,472,335},{14196,7510,629,875},{13936,-7489,529,975},{13936,-7473,586,1000},{14143,6394,628,470}, + {14088,2518,641,550},{14071,-984,653,875},{14123,5157,631,580},{14046,-4219,558,335},{14109,3659,667,750},{14024,-6240,481,540},{14065,-3977,654,485},{14060,-4605,575,540}, + {14116,2618,446,470},{14046,-5552,563,925},{14080,-2883,469,520},{14053,-5501,250,2600},{14094,-3776,648,975},{14139,1659,629,510},{14107,-3943,606,825},{14135,-512,559,425}, + {14100,-5174,608,825},{14086,-6030,630,485},{14159,-146,601,470},{14102,-6165,599,500},{14217,4560,541,500},{14181,-922,654,950},{14169,-2879,610,1800},{14162,-4050,620,825}, + {14245,5575,645,705},{14190,-866,198,455},{14183,-2234,469,470},{14194,-607,538,580},{14427,8133,429,455},{14136,-6195,577,900},{14158,-5338,511,1800},{14211,906,541,615}, + {14212,819,571,500},{14191,-3501,665,925},{14288,6306,367,750},{14226,-126,627,750},{14229,-277,460,660},{14235,-221,614,750},{14168,-6493,605,925},{14238,-425,626,540}, + {14230,-2075,566,335},{14268,3366,585,520},{14231,-3595,451,455},{14232,-3840,619,825},{14246,-2335,624,500},{14372,7221,572,645},{14289,3510,537,335},{14248,-2659,548,455}, + {14362,6983,456,560},{14259,-1558,585,455},{14293,2297,431,425},{14403,7432,646,1200},{14321,5168,317,660},{14220,-6227,592,485},{14178,-7160,547,470},{14328,4944,676,900}, + {14299,972,507,470},{14269,-3189,700,950},{14269,-3189,618,1000},{14299,1130,497,500},{14213,-6670,591,1000},{14252,-5152,545,1400},{14291,-1055,614,750},{14268,-4047,360,705}, + {14300,-118,457,925},{14298,-1037,613,500},{14288,-3187,593,775},{14289,-3572,587,440},{14338,3649,618,440},{14333,2837,653,925},{14260,-5836,588,320},{14317,186,611,645}, + {14104,-8079,536,705},{14308,-1940,574,875},{14358,4690,652,750},{14308,-2863,646,775},{14371,5205,450,950},{14356,3640,455,520},{14326,-851,612,500},{14323,-1352,594,425}, + {14281,-5703,313,425},{14350,2345,625,395},{14333,-719,624,500},{14448,7308,642,720},{14308,-4065,535,520},{14330,-2112,501,500},{14377,3962,481,510},{14336,-2286,591,1000}, + {14384,3996,676,720},{14328,-3913,643,750},{14289,-6672,627,485},{14364,-592,556,485},{14392,3581,541,520},{14306,-6279,610,260},{14344,-4900,512,2200},{14397,1438,636,975}, + {14354,-5126,501,2800},{14418,3116,556,320},{14290,-7308,547,440},{14389,-1958,631,950},{14380,-3582,649,645},{14487,6727,594,425},{14361,-5923,408,2000},{14426,1644,569,485}, + {14430,684,500,485},{14414,-2470,653,720},{14459,4024,525,750},{14425,-2530,570,485},{14411,-4875,617,875},{14446,-942,640,975},{14429,-3218,563,500},{14461,465,468,455}, + {14437,-3610,598,470},{14421,-4936,435,875},{14419,-5294,619,500},{14556,6924,569,510},{14574,7225,517,500},{14430,-5367,545,950},{14402,-6495,656,950},{14451,-4319,550,520}, + {14638,7813,623,470},{14467,-3961,670,675},{14496,-114,391,470},{14495,-1057,631,1000},{14683,7914,617,750},{14508,-1077,637,950},{14508,-1433,506,2000},{14540,3128,589,410}, + {14550,3976,562,520},{14529,989,352,660},{14541,2591,624,470},{14568,4843,639,975},{14578,5436,647,875},{14484,-6133,452,1200},{14568,3509,614,705},{14532,-2359,477,1800}, + {14615,6498,617,705},{14347,-8094,511,2000},{14589,2997,564,950},{14556,-2392,494,615},{14518,-5798,532,925},{14526,-5726,580,500},{14648,6365,634,720},{14593,1402,535,320}, + {14567,-3550,641,875},{14545,-5521,600,2200},{14612,1886,650,500},{14663,5713,520,305},{14605,-2777,479,800},{14586,-5123,615,1800},{14646,2377,298,590},{14607,-3957,682,720}, + {14593,-5389,556,1000},{14654,671,579,335},{14660,1181,563,440},{14634,-3020,645,3000},{14660,179,565,690},{14686,1157,645,825},{14715,4602,509,600},{14689,1143,602,230}, + {14642,-5719,646,1400},{14631,-6251,369,550},{14760,6559,631,775},{14663,-4476,555,2600},{14648,-5879,622,675},{14775,5904,380,720},{14746,2118,609,825},{14678,-6507,301,850}, + {14678,-6507,626,1600},{14737,-3719,597,485},{14763,434,624,645},{14803,5406,459,925},{14717,-5641,606,500},{14776,1307,646,350},{14746,-3627,637,500},{14749,-4573,508,1600}, + {14781,-424,601,800},{14797,2440,532,875},{14784,-618,642,630},{14659,-7678,545,520},{14787,-1485,412,530},{14768,-4693,573,500},{14819,2601,388,470},{14805,245,602,1000}, + {14805,8,635,510},{14800,-1653,608,500},{14813,-810,505,975},{14783,-4619,562,470},{14876,6112,627,500},{14792,-4655,458,550},{14780,-5943,579,470},{14773,-6275,557,485}, + {14843,596,595,320},{14824,-2733,630,590},{14893,4982,527,925},{14960,7288,583,455},{14855,-2593,488,470},{14825,-5537,648,2800},{14863,-2249,624,950},{14931,5742,593,550}, + {14872,-1901,494,335},{14846,-5115,593,2600},{14857,-4528,571,2400},{14911,893,585,470},{14871,-5024,572,1000},{14942,4106,514,660},{14949,4541,630,500},{14902,-4082,641,335}, + {14919,-2655,628,900},{14915,-3342,584,485},{14923,-2748,632,900},{15214,8392,637,500},{14909,-5134,637,2600},{14961,2776,630,705},{14956,1244,526,1200},{14953,831,604,455}, + {14997,5681,548,425},{14922,-5457,354,2000},{14930,-5264,612,2400},{14990,2965,573,510},{14943,-4841,605,2400},{14904,-7139,635,1200},{14993,338,670,750},{14978,-2395,621,2200}, + {14972,-3589,523,735},{14931,-6449,658,500},{14925,-6910,620,2400},{15005,804,470,320},{15025,3192,536,570},{15070,2195,566,2600},{15014,-5695,652,500},{15115,5389,574,675}, + {15042,-5336,620,2600},{15071,-3058,654,485},{15049,-5735,620,485},{15129,5237,614,875},{15092,-957,612,500},{15050,-6042,594,775},{15104,320,645,690},{15092,-2532,670,1000}, + {15101,-1810,586,1000},{15084,-4664,612,1000},{15109,-2429,570,800},{15075,-6018,619,850},{15071,-6216,642,455},{15110,-3998,643,485},{15142,1576,637,720},{15128,-1306,460,1200}, + {15089,-6188,614,1600},{15131,-3638,627,485},{15170,561,621,485},{15186,3524,448,825},{15183,1676,352,1000},{15155,-4737,508,485},{15179,-1714,560,395},{15153,-5219,652,485}, + {15207,3160,624,690},{15198,1000,437,455},{15198,-37,449,1000},{15209,1197,135,1600},{15018,-8221,552,1000},{15201,-3733,636,500},{15219,-1088,653,950},{15215,-1561,627,1000}, + {15275,4066,632,455},{15249,-1210,624,875},{15253,-841,591,975},{15223,-5181,486,2400},{15252,-1282,531,675},{15238,-3586,613,630},{15280,3740,585,455},{15265,-1235,361,500}, + {15218,-6582,528,500},{15273,-842,565,470},{15103,-8157,660,540},{15291,1336,644,645},{15234,-6155,560,320},{15282,-732,625,950},{15266,-4172,598,470},{15238,-6868,581,1000}, + {15301,-2861,628,975},{15320,461,577,500},{15316,-1915,644,660},{15346,2714,604,570},{15290,-5883,640,305},{15378,5999,625,350},{15294,-5806,572,2600},{15310,-5216,616,470}, + {15333,-2703,625,775},{15362,2117,602,615},{15335,-3303,638,590},{15378,3147,646,520},{15344,-4035,590,485},{15450,7307,640,720},{15334,-5123,528,825},{15326,-5992,610,2200}, + {15349,-4031,635,520},{15337,-5176,578,925},{15446,7106,666,825},{15334,-6166,641,2600},{15368,-4212,385,950},{15406,2931,535,1000},{15338,-6637,516,875},{15451,6511,582,800}, + {15412,2868,649,600},{15407,1774,655,775},{15384,-3652,619,500},{15413,2350,597,590},{15417,2342,344,750},{15417,2537,584,470},{15427,4291,345,950},{15404,-1120,608,500}, + {15417,1373,541,335},{15388,-4311,560,440},{15343,-7004,332,1400},{15382,-5497,616,2600},{15431,2311,582,630},{15419,-2067,657,705},{15454,2742,652,1000},{15441,-807,524,720}, + {15401,-5990,622,875},{15475,4676,643,485},{15417,-5121,630,290},{15486,4840,600,500},{15526,6875,596,825},{15475,2471,640,500},{15453,-2899,534,1000},{15427,-6133,340,455}, + {15506,5378,645,615},{15513,5422,600,455},{15466,-3680,630,470},{15493,1947,479,660},{15482,-1253,600,775},{15462,-4167,596,1200},{15499,1984,261,485},{15499,1984,380,530}, + {15488,-511,637,500},{15500,-906,632,735},{15466,-5611,581,660},{15742,8425,550,750},{15490,-5503,457,455},{15526,229,666,2600},{15477,-6468,567,975},{15507,-4770,565,425}, + {15554,4123,576,645},{15528,-1798,651,705},{15558,4150,305,350},{15546,1498,612,975},{15537,-2371,650,925},{15603,6557,497,1000},{15540,-2253,651,1000},{15523,-5604,450,2400}, + {15578,3391,590,850},{15555,-1987,613,975},{15575,654,607,455},{15592,2962,639,520},{15581,569,654,660},{15558,-4165,483,485},{15604,3372,550,485},{15586,-407,597,1200}, + {15580,-3016,627,750},{15777,8256,526,720},{15605,237,632,510},{15587,-3801,533,800},{15592,-4195,627,520},{15631,878,561,305},{15574,-6690,499,1400},{15648,3380,474,750}, + {15596,-5795,635,440},{15643,-706,557,425},{15632,-4247,618,520},{15652,-1684,381,440},{15625,-5858,595,720},{15687,4160,602,950},{15675,1936,615,500},{15702,4878,644,615}, + {15654,-4274,613,485},{15697,3671,421,510},{15691,976,604,1000},{15610,-7403,400,720},{15715,4521,635,500},{15679,-3107,425,440},{15618,-7397,619,950},{15748,6563,632,470}, + {15670,-5488,558,455},{15761,6426,612,825},{15718,-374,605,975},{15685,-5764,466,775},{15701,-4941,610,440},{15766,5598,484,630},{15753,3893,577,900},{15697,-5874,382,720}, + {15686,-6570,601,950},{15737,-274,521,1200},{15737,-2966,558,425},{15740,-3061,556,1200},{15757,-64,509,1800},{15722,-6417,529,350},{15901,8049,652,560},{15774,-764,620,425}, + {15775,-1359,558,1200},{15796,3238,590,1000},{15771,-2648,651,645},{15877,7571,484,520},{15805,1414,546,335},{15766,-6136,643,320},{15754,-6698,619,2200},{15795,-2824,605,645}, + {15843,5350,645,975},{15831,4043,475,825},{15820,931,385,2800},{15784,-5372,489,660},{15799,-4507,574,1800},{15799,-4507,609,1400},{15838,3499,558,950},{15758,-7199,474,975}, + {15814,-4462,591,540},{15801,-6169,332,2200},{15879,5708,516,735},{15776,-7322,493,440},{15824,-4700,502,440},{15820,-5867,600,950},{15850,-2375,508,440},{15870,695,508,520}, + {15836,-5819,614,1600},{15876,865,567,950},{15874,-2318,610,645},{15909,3633,628,705},{15855,-6099,623,470},{15891,-1857,649,825},{15880,-3956,538,246},{15879,-4366,608,520}, + {15907,-1058,657,750},{15907,-1634,603,335},{15902,-2668,629,675},{15914,-1223,570,645},{15890,-5756,445,455},{15930,-1175,652,750},{15796,-8192,707,2000},{15931,-2741,489,320}, + {15939,-1338,482,400},{15908,-5956,508,485},{15977,5367,552,455},{15968,3198,471,580},{15933,-4823,384,690},{15978,3791,585,615},{15936,-5873,545,1000},{15885,-7631,630,470}, + {15965,-1688,491,520},{15971,-1244,604,1000},{15951,-5726,591,2400},{15887,-7861,411,350},{15971,-4275,611,875},{16045,6844,575,455},{15969,-5918,466,440},{16049,6572,512,470}, + {15975,-5882,592,335},{15983,-5560,428,580},{16077,6908,500,455},{16035,-174,626,485},{16021,-3574,637,520},{16055,3170,602,290},{16005,-6510,552,1200},{15982,-7449,607,455}, + {16093,5720,580,1000},{16032,-5968,642,922},{16076,2633,551,900},{16063,-1398,624,470},{16089,4620,518,675},{16085,2319,508,925},{16084,475,579,455},{16068,-3272,564,1000}, + {16127,6741,600,400},{16056,-6447,482,2200},{16106,1976,627,875},{16067,-5922,538,2600},{16074,-6439,276,3000},{16088,-6057,457,440},{16129,249,628,440},{16159,5737,634,335}, + {16147,3068,524,975},{16096,-6425,577,2600},{16103,-6396,482,2000},{16046,-7978,597,2200},{16152,637,637,485},{16160,1889,549,455},{16161,1419,548,560},{16126,-5968,621,593}, + {16108,-7086,626,875},{16113,-7085,646,850},{16111,-7244,627,660},{16172,-1730,542,925},{16221,6513,639,975},{16169,-4942,269,550},{16157,-6060,625,1000},{16191,-1526,667,1000}, + {16157,-6452,534,1800},{16162,-6426,523,1600},{16174,-5676,523,1400},{16171,-6438,485,2400},{16224,2942,615,485},{16217,-196,593,320},{16206,-3169,588,975},{16191,-5747,636,335}, + {16231,1055,534,950},{16202,-5992,600,1000},{16232,-402,661,950},{16247,2797,604,705},{16132,-8047,547,500},{16241,-1619,311,470},{16243,-985,586,520},{16144,-8054,445,2600}, + {16280,5658,567,470},{16285,5932,558,470},{16258,-890,579,925},{16249,-3406,561,975},{16235,-5932,591,1200},{16277,-309,595,470},{16313,5257,665,520},{16313,5250,644,470}, + {16338,6985,593,510},{16306,103,638,925},{16315,-20,631,425},{16339,5458,510,455},{16333,3422,383,500},{16349,4319,471,975},{16335,-226,612,500},{16313,-5724,525,1200}, + {16337,-2014,524,660},{16339,-1545,638,455},{16343,-213,545,520},{16368,2549,620,800},{16357,-1376,566,560},{16374,3403,572,530},{16337,-5885,378,485},{16394,3351,503,485}, + {16390,2475,450,975},{16391,2475,630,950},{16380,-2066,644,500},{16342,-7072,599,1800},{16375,-4225,611,1000},{16406,4201,603,470},{16393,74,591,720},{16362,-6183,593,440}, + {16401,619,581,275},{16311,-7956,633,470},{16407,2235,614,470},{16414,2550,635,500},{16382,-6052,592,500},{16499,7777,620,510},{16418,-3714,460,485},{16428,-5076,591,900}, + {16482,5188,617,500},{16445,-5973,611,570},{16487,4043,505,590},{16489,3609,600,320},{16432,-7510,613,485},{16506,4553,547,425},{16492,1171,655,675},{16481,-3374,571,720}, + {16511,5150,643,510},{16488,-1635,589,320},{16509,4291,602,615},{16527,6342,639,950},{16494,-1830,408,500},{16521,3921,508,750},{16505,-1408,588,425},{16481,-6132,616,1400}, + {16514,362,484,485},{16500,-4381,581,1400},{16504,-4223,439,925},{16519,610,499,875},{16546,5638,237,975},{16504,-5182,615,925},{16524,-1579,634,500},{16517,-3184,607,630}, + {16546,-248,474,350},{16558,2018,442,975},{16593,6175,179,500},{16560,-2683,623,735},{16581,-75,614,750},{16480,-8156,671,645},{16581,-1130,550,530},{16590,0,595,455}, + {16582,-3196,646,335},{16590,-1343,634,550},{16613,3824,600,925},{16625,734,463,720},{16613,-4768,567,800},{16481,-8459,619,975},{16623,-3580,543,950},{16633,-2729,494,705}, + {16639,-1109,609,750},{16627,-4939,613,1200},{16649,-2729,571,1000},{16652,-5121,630,520},{16673,196,552,510},{16666,-2873,677,975},{16661,-5096,632,470},{16692,2332,646,875}, + {16662,-5868,602,470},{16678,-3259,659,735},{16664,-6242,461,520},{16660,-6484,641,925},{16682,-4264,515,875},{16698,-3017,654,580},{16671,-7088,557,2600},{16742,6721,606,750}, + {16707,-2997,649,975},{16720,2466,568,925},{16733,3631,574,305},{16718,-2808,544,975},{16742,4450,301,485},{16741,4321,589,320},{16715,-5898,391,560},{16714,-6195,513,1200}, + {16747,-3237,581,950},{16805,6827,640,925},{16793,1440,630,875},{16773,-5846,688,1600},{16791,-2283,448,950},{16819,5489,663,900},{16813,3581,641,600},{16806,-3243,638,335}, + {16813,-1850,613,1000},{16814,-2175,640,500},{16787,-7144,635,470},{16814,-4910,536,925},{16842,4109,633,470},{16815,-6032,460,850},{16824,-4974,611,520},{16831,-4437,580,425}, + {16819,-6417,523,1400},{16844,-7,542,1000},{16853,2052,256,900},{16851,806,579,455},{16856,1543,334,950},{16841,-5323,576,470},{16838,-5962,574,2800},{16880,2310,463,305}, + {16902,5277,650,660},{16872,-4373,621,440},{16897,1331,532,455},{16899,1284,667,775},{16917,4948,588,500},{16917,-365,447,825},{16924,-713,614,800},{16912,-4588,631,705}, + {16932,201,518,350},{16930,-3801,627,925},{16941,-3474,645,690},{16955,3153,487,600},{16955,3153,441,600},{16943,-3653,668,520},{16962,3309,348,455},{16959,1198,666,950}, + {16933,-6782,606,320},{16978,3819,478,975},{16973,165,591,500},{16984,-1478,356,520},{17022,6710,621,520},{16982,-6458,599,660},{16964,-7967,635,800},{17028,603,405,1200}, + {16990,-7514,627,1200},{17046,5708,643,825},{17002,-7199,641,2200},{17025,-5449,389,2000},{17071,6433,602,975},{17071,4348,499,530},{17060,-4465,612,520},{17082,13,605,455}, + {17084,-1878,509,675},{17080,-3616,500,410},{17049,-7761,643,875},{17078,-5678,579,900},{17098,1053,394,690},{17101,141,539,520},{17084,-6496,511,2000},{17115,-1086,483,425}, + {17109,-4267,612,2600},{17125,1143,580,440},{17122,-1768,408,875},{17105,-7226,559,2600},{17149,5585,575,500},{17140,1646,557,720},{17137,-3606,522,500},{17146,386,637,705}, + {17139,-3775,589,305},{17161,3345,632,645},{17143,-6397,517,645},{17169,301,650,500},{17165,-6112,530,2400},{17179,-1236,594,690},{17170,-5316,581,500},{17198,286,495,530}, + {17197,-170,625,470},{17199,-3533,645,425},{17227,6178,583,675},{17227,3934,531,950},{17215,-4267,508,1200},{17243,5674,628,900},{17208,-7247,609,2400},{17242,1541,574,440}, + {17255,5436,641,500},{17241,-2446,576,1000},{17296,8113,615,950},{17260,4666,635,470},{17263,4317,594,690},{17258,-300,477,455},{17262,1841,552,500},{17285,6933,384,350}, + {17272,4793,642,510},{17279,4879,656,510},{17294,1436,620,600},{17281,-6128,638,925},{17309,6108,548,630},{17295,-2078,624,645},{17294,-5944,513,570},{17295,-5952,515,950}, + {17307,-2926,581,630},{17307,-2926,564,630},{17310,-2675,616,335},{17320,-783,595,440},{17320,-4044,564,410},{17308,-6696,590,485},{17323,-3109,504,320},{17325,-3186,354,530}, + {17331,-1628,605,615},{17348,3682,640,500},{17341,-4059,539,950},{17354,1102,655,950},{17359,306,577,675},{17368,1680,595,2200},{17377,5479,563,500},{17362,-3283,598,500}, + {17377,2044,645,510},{17369,-5426,462,1200},{17401,6932,520,500},{17374,-4914,550,485},{17380,-4737,571,305},{17393,1091,656,900},{17407,2778,580,750},{17398,-4764,525,825}, + {17394,-6302,313,1200},{17417,-980,470,1200},{17415,-3357,574,500},{17417,-3724,631,975},{17424,-82,430,520},{17409,-6105,583,2600},{17426,-3299,629,660},{17447,5062,614,510}, + {17425,-6128,515,500},{17439,-4775,544,470},{17459,4363,559,720},{17454,888,617,485},{17432,-7590,565,735},{17464,4683,610,675},{17470,6435,646,875},{17463,3363,627,470}, + {17462,813,536,290},{17460,-244,622,510},{17445,-6762,596,500},{17469,4511,644,600},{17453,-6183,515,875},{17467,-1320,548,645},{17475,-2472,642,550},{17496,-1447,621,975}, + {17496,-1662,619,305},{17487,-6540,517,580},{17511,5797,637,1200},{17505,-3474,470,1200},{17520,2135,526,485},{17526,3420,533,520},{17518,-5397,596,335},{17528,-2920,644,600}, + {17522,-6209,494,570},{17543,5517,627,425},{17539,3175,573,690},{17533,-4310,555,1000},{17543,-3250,522,425},{17562,6674,530,455},{17552,2221,659,720},{17551,-2029,622,500}, + {17526,-8310,633,500},{17586,-3719,598,455},{17573,-7931,639,500},{17598,-668,607,520},{17588,-6249,503,600},{17605,2522,602,425},{17597,-6288,610,950},{17619,-1835,473,520}, + {17632,826,485,900},{17630,-4907,626,485},{17646,653,403,335},{17651,4778,371,500},{17643,-4569,529,1800},{17640,-6673,364,825},{17673,5563,527,470},{17663,-6118,411,550}, + {17663,-4050,491,570},{17678,-3591,617,520},{17682,-3029,648,550},{17683,-5770,541,425},{17700,2022,453,550},{17698,825,532,975},{17710,-1031,626,645},{17706,-6681,472,440}, + {17716,1428,588,750},{17719,-2675,511,290},{17726,-32,615,645},{17726,1457,214,925},{17731,1624,604,925},{17742,3493,570,675},{17742,-6379,432,2400},{17749,-7023,497,540}, + {17758,-1586,613,500},{17767,176,361,615},{17761,-6265,570,950},{17765,-2728,648,520},{17773,1228,635,750},{17776,-533,564,500},{17779,3338,627,875},{17779,-4517,446,455}, + {17784,-1219,635,735},{17792,-3083,585,645},{17796,-6521,490,2200},{17824,3772,645,520},{17804,-5699,557,950},{17823,-3391,428,1200},{17836,-3507,617,950},{17846,5369,244,1000}, + {17846,55,630,750},{17855,-5741,606,1000},{17861,-3775,646,645},{17868,-2571,530,560},{17876,844,558,500},{17877,4648,654,925},{17880,4647,703,975},{17881,3676,649,305}, + {17875,-6328,591,925},{17892,1565,553,925},{17892,-2848,593,425},{17899,5660,584,485},{17900,-1715,518,1000},{17898,-3969,613,500},{17922,6155,622,520},{17918,-4707,626,675}, + {17927,-3332,621,1000},{17931,4034,662,660},{17942,-6245,557,2400},{17953,3227,642,775},{17959,6146,676,500},{17956,-5632,544,1400},{17958,-4095,679,485},{17970,-6434,561,925}, + {17973,-2591,643,1000},{17976,53,617,470},{17982,3317,596,485},{17980,-5170,605,485},{17991,-7822,491,1200},{17999,3403,650,775},{17999,366,537,975},{18008,8085,617,320}, + {18019,-1045,555,520},{18018,-2184,628,500},{18022,661,466,875},{18021,-1966,526,2800},{18026,-177,631,520},{18037,-5750,616,1000},{18041,3604,559,500},{18053,4305,521,750}, + {18058,-8563,605,455},{18062,-7149,642,485},{18066,-6919,589,1200},{18072,-768,622,425},{18076,-6331,433,875},{18092,-4243,515,660},{18093,-7421,644,540},{18107,2146,587,750}, + {18108,-6317,472,2600},{18116,-6833,535,1000},{18119,-7652,504,440},{18112,8559,627,660},{18124,-6097,596,320},{18130,873,412,520},{18131,7691,580,510},{18142,6293,613,470}, + {18147,-6555,633,1800},{18149,-3569,623,1400},{18150,-313,637,520},{18158,-6865,623,570},{18160,-6571,606,520},{18172,-6461,415,720},{18196,-7537,518,470},{18202,-5066,447,1800}, + {18202,-5076,637,1200},{18206,-4869,534,1200},{18209,-5072,260,2600},{18210,-6085,622,455},{18210,-2473,402,720},{18222,-4433,575,950},{18223,-4123,548,2400},{18242,190,595,455}, + {18245,7466,635,660},{18251,-3471,617,1000},{18251,581,572,720},{18253,-2262,300,470},{18264,-3787,606,875},{18263,1681,639,900},{18269,2728,601,900},{18277,-6128,608,720}, + {18277,-2360,546,950},{18276,-4542,661,500},{18288,-5136,623,540},{18291,-5237,396,2400},{18275,8171,600,425},{18296,2587,566,440},{18294,5705,643,425},{18300,2854,649,675}, + {18304,2054,557,500},{18309,-6295,592,1400},{18320,-7015,617,660},{18305,7762,514,875},{18330,-3413,650,290},{18336,-3893,576,2200},{18348,-7857,635,470},{18336,1026,585,950}, + {18340,-3379,633,1000},{18351,-4572,531,455},{18357,-6441,622,2800},{18368,5343,616,500},{18375,-2084,583,530},{18379,-5875,280,2600},{18379,-1031,611,675},{18388,-4191,626,500}, + {18379,7020,571,470},{18386,5703,331,925},{18395,-2335,654,705},{18395,-1754,259,1400},{18400,1490,510,925},{18410,-7261,622,1000},{18392,7255,629,500},{18403,4066,566,335}, + {18409,2395,495,520},{18413,3306,500,500},{18428,-6569,606,1000},{18426,-1669,605,950},{18439,-6796,411,275},{18437,5319,581,410},{18438,2894,570,900},{18459,-7931,426,2000}, + {18445,-3609,615,1000},{18443,1514,634,440},{18454,-395,699,705},{18454,-395,654,720},{18461,-6400,404,2600},{18463,3025,623,775},{18467,-79,590,875},{18475,-5514,500,335}, + {18421,8644,633,690},{18476,2601,648,875},{18483,2303,627,875},{18383,8770,628,720},{18471,7516,538,975},{18487,2816,633,630},{18498,-67,389,950},{18495,4898,529,350}, + {18504,-2218,597,570},{18512,-6584,621,1200},{18508,2662,554,470},{18507,2600,615,750},{18509,331,496,500},{18514,-2222,521,1400},{18518,1779,474,520},{18517,2705,713,705}, + {18523,-1357,514,485},{18534,-6040,359,455},{18521,5786,555,425},{18549,-5637,592,440},{18553,-6752,515,875},{18555,-6831,574,500},{18554,2477,619,1000},{18571,-5768,539,1200}, + {18563,2585,481,600},{18563,531,640,630},{18641,-8615,633,500},{18581,-6763,636,520},{18584,-2484,568,485},{18590,-3541,532,1200},{18590,-3930,640,720},{18594,-3891,579,1200}, + {18595,4254,606,705},{18601,5156,480,540},{18608,2610,518,925},{18619,-4138,625,470},{18611,2558,642,825},{18632,-6577,630,520},{18629,-4251,611,550},{18630,-1161,595,975}, + {18633,-2775,609,485},{18634,-3519,573,1200},{18631,2393,603,500},{18626,5678,581,305},{18628,6380,632,520},{18646,3902,502,510},{18663,-6312,486,2200},{18665,-6310,133,3000}, + {18665,-6310,173,2800},{18663,-5145,482,2400},{18660,2727,495,750},{18670,-4891,626,570},{18672,-3283,555,1200},{18685,-6379,600,1400},{18673,2827,436,485},{18675,2683,500,900}, + {18687,-5899,550,290},{18660,7193,624,520},{18693,861,637,520},{18696,-1663,635,570},{18701,-5023,391,2600},{18708,-6434,604,1000},{18690,5571,570,320},{18696,-462,622,800}, + {18711,-6180,622,470},{18709,-3904,544,1400},{18714,-5641,615,500},{18716,2623,654,950},{18719,2590,665,950},{18723,2591,529,975},{18736,2411,548,675},{18747,-5652,580,335}, + {18749,-4174,602,320},{18743,2090,569,925},{18747,-1652,295,1200},{18752,-1339,635,600},{18757,-2370,563,350},{18749,5841,535,875},{18751,5154,621,660},{18752,5877,608,520}, + {18779,-5711,163,305},{18782,-5708,642,925},{18753,6920,495,305},{18775,2457,546,950},{18771,5308,621,630},{18792,-5942,548,645},{18804,-7300,588,485},{18784,760,605,425}, + {18798,-6351,595,800},{18791,-505,619,510},{18812,-7213,387,2000},{18802,-3253,646,925},{18802,-1620,431,720},{18815,-1386,574,720},{18826,1030,626,520},{18834,-1979,626,720}, + {18839,-1283,558,520},{18839,2428,629,900},{18845,-945,548,1000},{18850,-4991,638,705},{18841,3325,542,500},{18845,3338,624,500},{18844,4136,426,600},{18860,-2340,265,550}, + {18837,6979,387,1800},{18868,-4467,577,590},{18871,2263,481,1000},{18887,-6184,622,570},{18878,1838,656,775},{18878,1838,502,470},{18878,2188,585,485},{18894,-4102,513,825}, + {18868,7002,494,455},{18900,-3987,580,1000},{18899,-2053,620,750},{18930,-6914,269,2600},{18920,-583,587,925},{18910,5949,550,290},{18924,1709,568,425},{18943,-4854,386,950}, + {18943,-2714,545,735},{18981,-7537,649,1400},{18952,328,633,975},{18972,-6719,625,2800},{18959,185,571,305},{18962,699,708,290},{18969,-1825,600,850},{18976,-3042,589,470}, + {18969,4087,637,825},{18976,2266,638,485},{18981,-800,466,470},{18998,-6651,626,1400},{18978,2106,546,510},{18982,3595,645,1000},{18997,-3999,464,1400},{19035,-4615,584,440}, + {19038,-4896,217,975},{19052,-6941,633,645},{19032,-1301,608,705},{19032,-1302,598,675},{19049,-5969,493,1800},{19039,1043,619,825},{19042,-145,365,750},{19042,-145,368,750}, + {19046,-1976,603,705},{19047,1024,488,1000},{19049,681,559,950},{19071,-6306,531,2800},{19065,-4881,466,500},{19071,-5595,608,1200},{19039,6271,607,950},{19079,-5618,600,1600}, + {19087,-5890,640,500},{19086,-4018,644,800},{19091,-158,593,520},{19099,-3635,639,1000},{19100,-2832,548,440},{19077,6116,638,500},{19126,-6883,616,660},{19141,-6098,469,500}, + {19111,4410,633,660},{19157,-6811,305,2600},{19125,3928,595,600},{19128,4544,499,320},{19140,767,522,750},{19159,-5649,465,2400},{19159,954,567,485},{19169,-3332,586,455}, + {19166,1658,512,455},{19111,8062,640,875},{19193,-5969,125,3000},{19176,595,634,1000},{19181,1196,607,925},{19189,-630,626,645},{19197,-2485,644,1400},{19196,357,641,305}, + {19183,6278,589,775},{19211,-2760,566,550},{19206,1355,656,975},{19244,-7199,555,520},{19189,6679,543,425},{19220,2484,631,530},{19223,1412,570,975},{19217,4847,627,850}, + {19216,6032,585,675},{19255,-6040,675,925},{19232,2755,578,950},{19374,-8512,546,500},{19258,-4846,624,1000},{19274,-5279,573,875},{19257,2286,643,500},{19267,-3400,491,1000}, + {19254,3752,589,925},{19282,-6033,572,950},{19285,-1034,641,520},{19290,306,602,440},{19299,-3968,598,1400},{19302,-4809,633,1000},{19299,-2674,615,1000},{19310,-5383,624,520}, + {19292,2754,494,600},{19305,1707,632,350},{19327,-5495,593,470},{19312,1612,630,825},{19334,-6033,576,1200},{19328,-4894,433,455},{19336,-4018,427,825},{19345,-6038,590,2000}, + {19330,-355,611,675},{19228,8342,585,1000},{19231,8341,528,975},{19332,2124,490,520},{19359,-5843,658,645},{19341,-422,644,500},{19366,-5915,462,2200},{19365,-5718,403,2600}, + {19365,-5717,517,2000},{19346,1242,625,825},{19358,-1165,600,950},{19359,-954,479,305},{19374,-4415,589,590},{19355,3353,626,825},{19351,5596,177,1000},{19383,-4292,547,350}, + {19413,-7219,593,520},{19399,-5684,532,3300},{19374,4720,584,275},{19390,340,338,305},{19397,-1533,617,1000},{19413,-2646,662,705},{19427,-5120,516,1400},{19400,3831,560,750}, + {19401,3832,290,1000},{19387,6544,524,750},{19407,5410,582,875},{19439,-2275,631,550},{19428,4618,612,500},{19473,1741,478,335},{19491,-381,579,925},{19514,-3351,602,750}, + {19557,-7155,362,470},{19507,3078,490,510},{19515,-337,599,470},{19516,1837,620,660},{19470,7547,601,500},{19498,6660,532,470},{19529,1712,596,500},{19577,-7148,603,2800}, + {19518,5637,493,720},{19554,1096,283,520},{19589,-4953,485,1000},{19545,6361,600,660},{19594,-2058,558,645},{19567,5972,653,925},{19598,-366,659,750},{19620,-4120,626,305}, + {19638,-5212,643,335},{19657,-4846,471,2000},{19665,-4159,559,500},{19673,-4991,427,2800},{19644,3580,525,1200},{19685,-5986,599,630},{19647,4527,563,485},{19659,2115,599,690}, + {19673,-3586,654,1000},{19654,2903,654,925},{19659,2262,560,275},{19621,7303,631,750},{19691,-5346,571,1400},{19703,-6531,551,3000},{19659,6204,614,530},{19679,2762,480,425}, + {19697,-1074,519,470},{19697,2756,619,440},{19714,-898,555,455},{19726,-2312,495,485},{19731,-954,632,500},{19730,1002,578,500},{19744,-1033,594,425},{19745,1685,591,425}, + {19749,-554,438,975},{19741,3742,602,455},{19774,-5257,606,875},{19796,-6994,591,705},{19742,3853,628,1600},{19750,1753,522,675},{19750,1753,522,675},{19779,-4223,579,645}, + {19751,3850,591,775},{19797,-6330,633,800},{19785,-4337,525,485},{19746,6223,654,975},{19807,-5992,460,1400},{19857,-7845,585,630},{19820,-6623,590,1000},{19791,-2655,650,875}, + {19801,-3780,485,570},{19823,-5982,616,600},{19801,-1620,504,675},{19821,-4270,622,500},{19797,2788,426,615},{19803,2426,633,485},{19835,-5070,589,1200},{19814,1156,577,350}, + {19815,1875,653,520},{19855,-5868,589,500},{19856,-5910,492,645},{19836,-1883,628,1200},{19849,-4314,616,440},{19830,1873,611,520},{19881,-6789,480,1400},{19886,-6968,637,470}, + {19855,-1993,533,540},{19868,-4896,589,485},{19843,4015,492,520},{19863,1133,567,350},{19879,-3637,619,485},{19919,-6514,607,675},{19900,-1994,522,485},{19930,-6678,487,470}, + {19838,7280,659,950},{19888,4086,579,825},{19911,-139,668,735},{19922,-3151,510,485},{19906,1905,645,520},{19931,-4398,584,875},{19811,8047,625,500},{19913,1979,645,750}, + {19919,942,522,600},{19983,-7204,604,455},{19932,1368,533,455},{19937,-68,637,750},{19940,547,480,335},{19964,-5129,619,1000},{19939,4057,473,705},{19912,6841,620,1200}, + {19960,-1831,474,540},{19973,-2317,300,520},{19971,369,662,975},{19962,3410,582,440},{19956,4968,515,1000},{20015,-5977,618,705},{19977,3513,602,750},{20016,-5275,548,1800}, + {20020,-5580,602,3000},{20015,-3671,275,950},{20024,-4688,577,485},{20072,-7215,605,1800},{20017,294,626,925},{20008,4015,560,485},{20037,-1949,621,950},{20065,-6097,618,2600}, + {20066,-6099,453,2400},{20057,-5218,583,2600},{20042,209,569,950},{20072,-4794,616,900},{20076,-4856,638,1800},{20054,515,587,950},{20100,-6454,453,540},{20128,-7489,505,500}, + {20079,-3319,622,335},{20075,-1774,537,425},{20052,4390,635,825},{20097,-4982,648,520},{20083,-492,589,455},{20131,-6449,531,720},{20146,-7063,567,875},{20114,-516,575,705}, + {20183,-7469,663,1200},{20098,3703,607,305},{20113,1243,644,500},{20098,5493,227,975},{20098,5492,395,975},{20130,-1116,98,2800},{20128,2385,578,925},{20153,-3976,509,500}, + {20155,-119,597,825},{20173,-4150,569,455},{20178,-4914,631,1000},{20131,5499,401,875},{20184,-4938,628,2400},{20168,-1271,525,350},{20181,-4016,640,520},{20219,-6963,620,875}, + {20157,4603,588,500},{20186,-1597,476,500},{20228,-6468,611,875},{20150,6326,650,540},{20236,-5117,506,975},{20211,1378,498,560},{20153,7239,579,335},{20177,6474,666,750}, + {20179,6472,704,750},{20200,5275,634,750},{20211,4073,647,520},{20231,-136,643,500},{20205,5059,680,735},{20243,-2328,497,245},{20230,1082,565,500},{20331,-7757,648,660}, + {20219,5072,643,750},{20523,-8579,558,875},{20211,5995,540,975},{20250,718,617,425},{20249,601,651,500},{20261,-647,609,720},{20276,-3941,388,510},{20289,-2811,647,975}, + {20174,7864,577,580},{20302,-3840,616,485},{20340,-6563,637,1000},{20301,-1873,601,750},{20299,-626,469,320},{20282,4211,608,470},{20315,-2869,569,975},{20314,-2957,645,705}, + {20322,-1536,555,485},{20324,-1017,521,500},{20325,-720,668,245},{20320,2435,611,520},{20362,-4827,633,1400},{20368,-3331,644,470},{20353,366,494,975},{20367,-1321,591,950}, + {20367,-60,337,925},{20359,3879,637,750},{20353,5535,560,1000},{20370,3718,498,720},{20388,-540,573,540},{20361,4902,470,875},{20430,-6169,563,660},{20389,1020,649,485}, + {20480,-7568,634,1400},{20381,4420,684,825},{20421,-3447,650,500},{20428,-4414,598,520},{20469,-7045,610,455},{20420,-2650,578,825},{20435,-4643,590,1400},{20453,-5842,642,485}, + {20425,2461,574,320},{20470,-5762,601,550},{20500,-7079,659,500},{20417,4949,649,485},{20436,3629,482,825},{20468,-2956,583,675},{20453,1430,652,775},{20505,-6458,579,705}, + {20368,7655,657,425},{20497,-5347,230,2800},{20443,5071,648,320},{20500,-4995,600,275},{20492,-3975,627,290},{20495,-4005,560,470},{20476,1827,648,455},{20489,1075,557,750}, + {20430,7124,550,470},{20550,-5879,538,1200},{20544,-5456,501,1400},{20488,5292,546,925},{20506,3101,621,550},{20517,1996,575,975},{20516,2807,623,455},{20538,-2345,659,1000}, + {20544,-3360,605,500},{20510,5052,632,645},{20526,2250,562,510},{20540,-870,501,335},{20573,-5677,600,3000},{20573,-5079,641,350},{20509,5721,629,950},{20518,5468,466,320}, + {20573,-4140,598,500},{20555,839,616,705},{20592,-4207,598,1400},{20607,-5101,647,1000},{20577,354,536,470},{20562,4167,630,540},{20568,3499,598,550},{20537,6482,585,950}, + {20598,-550,651,975},{20594,2270,613,440},{20612,-1618,560,600},{20619,-2550,621,500},{20640,-2612,581,975},{20642,-3304,423,705},{20598,5206,602,975},{20640,-1577,619,950}, + {20679,-6259,651,500},{20666,-5143,465,520},{20648,-1243,551,520},{20674,-3625,515,1000},{20686,-5025,591,925},{20691,-5032,545,470},{20630,5588,650,645},{20681,-971,605,425}, + {20656,4109,587,875},{20658,3850,594,510},{20686,-1786,543,320},{20668,2570,595,550},{20674,635,633,600},{20566,7806,591,530},{20682,1746,450,660},{20675,3854,550,500}, + {20665,5443,570,1200},{20891,-8267,595,470},{20723,-3570,653,570},{20738,-4169,341,2600},{20688,4931,186,2400},{20736,-3445,419,290},{20740,-4247,304,2600},{20795,-6940,575,425}, + {20716,3119,562,500},{20747,-1813,497,500},{20753,-2908,618,1000},{20758,-3990,644,485},{20724,3954,740,260},{20737,1580,407,425},{20743,2126,491,440},{20764,-1990,653,550}, + {20560,8275,598,510},{20744,3663,638,825},{20760,550,601,500},{20795,-4690,577,2600},{20802,-5281,525,1200},{20790,-3643,635,660},{20783,-2439,645,550},{20796,-3299,456,2000}, + {20796,-3299,606,1400},{20800,-3162,612,645},{20744,6149,596,570},{20777,3477,665,875},{20779,3466,587,320},{20762,5854,646,925},{20843,-5337,589,2000},{20870,-6765,571,470}, + {20795,3444,474,425},{20808,1217,604,975},{20830,-3193,473,2200},{20839,-3566,554,690},{20849,-4713,610,2600},{20847,-3531,619,485},{20830,1793,570,550},{20786,6472,465,305}, + {20775,6832,640,470},{20857,-2857,604,470},{20829,2865,590,825},{20880,-5216,571,1200},{20888,-4729,255,2600},{20868,-150,515,470},{20874,-806,619,630},{20908,-5413,614,975}, + {20867,1840,268,600},{20914,-5470,600,540},{20894,-3128,651,520},{20846,5373,570,1000},{20908,-4659,583,485},{21014,-7859,609,1200},{20941,-6369,471,485},{20963,-6580,620,500}, + {20896,1406,616,660},{20912,105,591,800},{20904,3203,632,690},{20937,-2302,614,500},{20914,2749,501,455},{20957,-4210,383,2600},{20967,-4480,387,2600},{20963,-2497,515,1400}, + {20982,-5037,591,520},{21007,-6148,649,735},{21022,-6627,597,720},{20967,1465,600,425},{20966,2170,576,1000},{20938,6149,637,455},{21000,-2501,577,645},{20996,-355,640,630}, + {21033,-4022,613,485},{21043,-4560,434,660},{21133,-7680,550,260},{21034,889,599,950},{21029,2739,623,825},{21041,154,426,925},{21060,-2743,548,455},{21086,-5621,592,470}, + {21096,-6037,61,2800},{21076,-3168,618,630},{21086,-4142,611,875},{21063,969,620,630},{21055,4575,627,470},{21097,-2242,630,675},{21088,1079,630,520},{21090,755,626,510}, + {21098,490,624,690},{21106,-538,639,500},{21111,-1497,628,540},{21144,-5467,617,875},{21211,-7485,602,580},{21075,5097,615,950},{21160,-5972,642,3000},{21046,6868,634,425}, + {21116,230,628,500},{21131,-1634,656,900},{21151,-4118,436,2600},{21155,-4309,620,520},{21159,-2668,327,470},{21167,-3637,206,500},{21206,-6321,640,520},{21168,-931,546,720}, + {21110,6438,365,1000},{21223,-5928,634,2000},{21263,-7031,605,440},{21222,-4347,617,1200},{21276,-6972,606,850},{21240,-5150,600,1200},{21248,-5344,475,510},{21225,-1033,647,520}, + {21198,4385,527,290},{21207,4946,525,335},{21271,-1630,491,320},{21219,5934,646,500},{21456,-8101,491,950},{21260,2509,483,615},{21174,7459,645,825},{21423,-7766,647,470}, + {21288,136,643,645},{21332,-5367,556,425},{21310,-2436,634,500},{21281,3230,611,440},{21342,-5463,611,2000},{21319,-2726,508,455},{21307,241,501,1000},{21330,-2661,624,500}, + {21322,-1027,419,470},{21374,-5709,507,2200},{21342,-85,591,645},{21368,-4184,561,520},{21384,-5351,639,455},{21416,-6659,575,2800},{21221,7755,482,455},{21359,-595,636,615}, + {21352,1296,554,660},{21376,-2928,608,975},{21391,-4500,631,615},{21414,-5991,639,275},{21610,-8285,642,1400},{21337,5179,669,735},{21337,5179,454,800},{21371,1010,529,485}, + {21372,334,645,290},{21385,-1820,543,975},{21367,2187,639,825},{21302,6943,524,320},{21360,4152,624,455},{21559,-8011,506,2200},{21408,-3324,655,750},{21400,-600,408,660}, + {21673,-8367,432,470},{21392,1918,-4,485},{21409,-662,644,775},{21413,-320,615,975},{21402,1891,598,800},{21427,-1859,622,550},{21382,5254,658,900},{21414,2012,625,630}, + {21410,3974,638,720},{21460,-3322,654,750},{21496,-6127,523,875},{21404,5137,475,775},{21410,4609,418,1000},{21437,1526,580,305},{21450,-754,647,550},{21485,-4606,355,2600}, + {21466,-1872,590,1000},{21475,-2582,587,675},{21485,-3700,594,950},{21508,-5639,433,1800},{21478,-1337,452,950},{21434,5131,620,950},{21450,3551,481,500},{21504,-4306,556,520}, + {21446,4800,632,675},{21518,-4519,477,750},{21482,1300,541,675},{21489,-227,514,510},{21514,-3789,405,1000},{21492,38,619,825},{21473,3877,686,510},{21494,1631,486,455}, + {21565,-5846,492,520},{21473,5486,653,875},{21495,3879,633,950},{21504,3043,644,875},{21566,-4832,609,2800},{21558,-3479,556,1400},{21583,-5077,602,470},{21576,-3951,442,1600}, + {21628,-6820,561,925},{21595,-5318,600,485},{21577,-2775,477,440},{21641,-6617,636,925},{21586,-1171,621,530},{21581,124,627,590},{21584,844,686,750},{21584,845,512,1000}, + {21578,2534,622,675},{21600,824,595,925},{21740,-7673,607,500},{21620,-2481,532,520},{21678,-6582,585,455},{21605,582,510,875},{21617,-1167,649,690},{21608,809,619,440}, + {21653,-4522,456,2600},{21654,-4538,435,690},{21637,-1997,661,950},{21656,-4232,632,470},{21645,-2685,648,530},{21671,-3987,635,1600},{21680,-4613,583,975},{21637,3839,627,470}, + {21718,-5920,645,1000},{21630,5185,405,645},{21661,1923,539,750},{21685,-612,617,950},{21704,-2949,497,1400},{21782,-6772,583,485},{21705,-223,481,580},{21717,-690,542,425}, + {21686,4102,663,735},{21754,-4532,550,1400},{21759,-4952,537,975},{21713,2829,762,975},{21714,2829,712,975},{21707,3620,610,500},{21774,-4084,639,455},{21746,83,594,875}, + {21780,-3887,597,485},{21716,4984,559,570},{21814,-5689,693,675},{21746,3179,606,1000},{21740,4180,635,550},{21769,477,602,440},{21815,-5046,442,2600},{21839,-5500,587,660}, + {21837,-5268,587,500},{21829,-3071,609,500},{21796,3037,358,455},{21688,7570,425,440},{21853,-4210,660,950},{21882,-6002,640,470},{21808,2668,601,825},{21814,2226,592,720}, + {21802,3831,303,825},{21769,6319,609,690},{21793,6023,627,750},{21871,-2044,650,900},{21888,-4152,587,1600},{21888,-4216,231,2800},{21833,3696,643,630},{21813,5540,576,425}, + {21944,-6793,604,645},{21908,-4625,555,455},{21855,3253,633,675},{21910,-3960,613,485},{21867,2974,446,720},{21866,3663,603,425},{21918,-4021,574,1400},{21933,-4613,541,520}, + {21857,5707,648,660},{21866,4937,574,335},{21947,-4943,405,2000},{21903,2325,638,500},{21925,-1231,620,675},{21958,-3879,602,455},{21980,-4658,607,645},{21985,-4906,639,705}, + {21990,-6084,-1,580},{21990,-6084,133,485},{22014,-5644,630,500},{21956,1830,591,470},{22063,-6498,319,875},{21955,4364,570,470},{22048,-5862,622,690},{22026,-3614,567,1000}, + {21956,5402,585,975},{21971,4440,539,975},{22048,-4739,230,2800},{22196,-7904,383,455},{22049,-3779,400,2400},{22009,2198,610,600},{22018,1353,591,750},{22046,-3093,637,875}, + {22018,1642,494,1200},{22018,1642,588,850},{22029,1373,483,950},{22029,1373,443,950},{21841,7966,626,455},{22041,816,486,530},{22043,1166,556,520},{22132,-6288,536,775}, + {22048,2112,638,520},{22081,-2500,573,1200},{22091,-3517,405,455},{22123,-5848,611,500},{22076,-566,388,720},{22130,-5560,610,2600},{22125,-3519,492,1000},{22086,2653,481,305}, + {23208,-8813,648,875},{22051,6126,625,720},{22094,4046,573,440},{22162,-4744,574,975},{22176,-5238,521,520},{22130,-142,607,335},{22150,-2544,494,720},{22180,-5221,607,975}, + {22153,-2315,581,550},{22219,-6659,591,2600},{22138,72,569,1200},{22131,1696,460,520},{22149,-1546,633,485},{22155,-2118,640,645},{22125,2708,512,950},{22125,2707,270,500}, + {22134,1888,613,500},{22177,-3829,594,455},{22188,-4356,630,520},{22131,3279,628,335},{22156,189,372,1000},{22152,1513,563,275},{22181,-2132,606,425},{22184,-2562,563,1000}, + {22228,-5667,623,485},{22194,-2609,524,550},{22199,-2665,577,1200},{22198,-1284,635,500},{22216,-3663,604,305},{22331,-7319,560,530},{22233,-2425,568,485},{22223,-85,614,1200}, + {22233,-1415,531,975},{22210,2437,614,720},{22546,-8323,565,520},{22257,-2796,441,440},{22315,-6381,587,570},{22291,-4358,432,2000},{22228,3781,616,720},{22267,-1600,515,690}, + {22272,-1604,275,925},{22249,2862,580,900},{22233,4612,574,645},{22257,2391,585,600},{22275,-230,494,520},{22275,-26,618,455},{22238,5137,651,675},{22242,4872,569,660}, + {22368,-6599,609,2400},{22447,-7666,534,440},{22262,3727,548,500},{22314,-3058,629,590},{22321,-3780,503,1600},{22285,1910,455,520},{22619,-8304,565,520},{22389,-6011,520,470}, + {22505,-7716,593,520},{22358,-2464,530,470},{22366,-3330,582,470},{22335,1570,640,615},{22418,-6278,511,2200},{22286,5929,546,440},{22335,1915,601,470},{22360,-1190,580,530}, + {22498,-7503,620,1200},{22407,-5281,538,950},{22778,-8479,591,1200},{22394,-3386,532,1000},{22413,-4788,564,1200},{22426,-5145,664,320},{22415,-3942,636,1000}, + {22413,-3264,606,455},{22268,7416,208,440},{22419,-1141,546,440},{22431,-2916,629,1200},{22454,-4886,635,570},{22405,1445,577,1000},{22437,-2142,574,440},{22399,3230,612,950}, + {22430,-435,449,750},{22463,-4313,268,2600},{22465,-3991,615,470},{22439,-17,553,485},{22426,2156,649,975},{22430,1639,571,550},{22479,-4210,313,2600},{22466,-2766,565,900}, + {22456,-1116,660,975},{22481,-3788,647,1400},{22486,-4316,610,675},{22410,4963,563,645},{22472,-1114,587,455},{22472,-499,609,630},{22485,457,593,320},{22530,-3806,589,470}, + {22524,-852,492,1200},{22549,-3436,622,800},{22490,3927,564,750},{22440,6593,460,290},{22533,-276,552,350},{22553,-2806,585,900},{22522,2205,638,500},{22620,-6403,517,520}, + {22545,-14,571,350},{22575,-3264,544,2400},{22258,8251,564,615},{22516,4728,637,1200},{22679,-7191,652,440},{22569,-303,661,875},{22553,2501,481,440},{22572,209,440,500}, + {22549,4039,350,520},{22602,-2528,329,305},{22618,-4086,641,260},{22628,-4705,472,2000},{22628,-4705,482,2000},{22633,-4107,515,520},{22536,6020,593,900},{22578,3521,551,520}, + {22603,549,650,750},{22699,-6528,617,455},{22578,4464,665,660},{22590,3457,659,520},{22645,-2579,667,1400},{22656,-3626,627,425},{22611,2695,454,470},{22686,-4909,577,520}, + {22595,4765,476,615},{22664,-3092,596,1000},{22661,-2203,617,485},{22737,-6708,576,660},{22666,-1626,520,425},{22736,-6364,628,1000},{22705,-4058,579,1400},{22716,-4287,585,1600}, + {22721,-4528,405,2400},{22636,4815,557,975},{22814,-7277,601,1400},{22599,6592,613,950},{22665,3646,635,630},{22692,550,616,500},{22769,-6142,630,580},{22684,1844,602,925}, + {22683,2487,493,675},{22657,5456,525,530},{22753,-3879,598,520},{22782,-5535,554,580},{22710,2630,567,470},{22722,1324,610,540},{22715,2511,581,485},{22758,-2633,576,500}, + {22790,-4528,644,485},{22788,-4528,739,500},{22858,-7008,581,400},{22825,-6174,632,440},{22798,-4874,387,1200},{22799,-4874,569,925},{22708,5006,639,500},{22807,-5210,341,520}, + {22813,-4822,633,470},{22821,-4450,482,2400},{22806,-1979,454,1000},{22828,-3609,610,1400},{22802,1898,589,290},{22832,-2401,647,1200},{22833,-1965,608,925},{22837,-2531,645,560}, + {22847,-2619,584,550},{22818,1929,668,550},{22866,-3152,491,735},{22915,-6090,573,3000},{22864,-1777,617,1200},{22897,-4807,595,925},{22924,-6096,509,3600},{22838,2298,630,950}, + {22941,-6361,486,470},{22902,-4149,516,550},{22904,-4348,604,2400},{22871,-550,628,470},{22938,-5880,407,925},{22973,-6868,289,975},{22768,6778,617,800},{22840,3826,620,470}, + {22853,3179,599,425},{22880,494,533,500},{22862,2916,526,950},{22854,4217,613,320},{22910,-2240,550,425},{22895,37,563,900},{22970,-6050,546,3900},{22888,3331,347,520}, + {22954,-4106,628,875},{22963,-4788,427,1400},{23017,-6748,628,2400},{22925,-938,261,1400},{22946,-3015,434,510},{22974,-4079,559,1600},{22967,-3121,618,540},{22988,-3710,620,540}, + {22961,-46,589,425},{22866,6735,513,615},{22960,2057,570,520},{22872,6895,651,975},{22983,177,506,630},{23034,-4065,322,2800},{23040,-4075,620,975},{23038,-3822,648,1000}, + {23053,-4793,500,630},{23045,-4832,565,570},{23079,-6066,567,645},{23022,-1816,617,520},{22988,3252,632,950},{23026,-1555,630,750},{23084,-5932,451,2000},{23045,-3626,356,425}, + {23020,-241,635,500},{23028,-582,554,485},{23067,-4469,337,2600},{23004,2962,551,500},{23026,72,535,455},{23028,2496,639,500},{23079,-3686,454,2200},{23156,-6831,589,520}, + {22927,7182,502,440},{23002,5196,566,900},{23017,4443,619,705},{23060,1257,628,1000},{23045,3293,537,1200},{23119,-3971,537,1000},{23097,-1237,572,540},{23093,-102,612,750}, + {23128,-3817,703,950},{23105,-1032,494,675},{23133,-3873,460,1000},{23189,-6453,571,425},{23066,3958,550,440},{23080,3029,558,600},{23080,3029,608,570},{23582,-8447,557,950}, + {23288,-7339,549,2400},{23066,6205,598,1000},{23102,4527,601,470},{23112,3738,431,720},{23113,3735,650,590},{23018,7183,305,925},{23183,-3677,545,2200},{23066,6334,579,440}, + {23211,-5160,610,580},{23145,1543,517,335},{23147,1948,627,600},{23157,3434,546,440},{23235,-4673,524,440},{23206,-1672,564,425},{23123,5897,329,470},{23191,2510,602,335}, + {23216,184,517,800},{23196,2911,368,750},{23164,5402,645,950},{23265,-2073,622,875},{23267,-1661,582,540},{23302,-3862,625,825},{23219,4720,615,925},{23296,-3288,646,1600}, + {23192,6228,650,875},{23196,6067,590,425},{23293,-2016,622,800},{23483,-7792,618,470},{23273,858,657,690},{23224,5519,643,925},{23259,3129,646,900},{23262,3680,637,500}, + {23315,-1967,552,950},{23273,4083,502,440},{23323,-1685,550,2600},{23329,-2449,700,925},{23407,-6561,651,705},{23351,-4007,582,320},{23234,6210,638,425},{23284,3662,638,660}, + {23304,1606,622,1200},{23418,-6632,411,485},{23324,-119,551,500},{23359,-3935,636,1200},{23295,4090,502,875},{23366,-2805,515,440},{23379,-4117,278,2600},{23354,-1006,462,485}, + {23323,3136,414,1800},{23359,-570,651,645},{23361,-918,517,1800},{23397,-4496,454,2400},{23509,-7345,565,1600},{23347,1714,645,705},{23405,-4440,543,440},{23273,6421,579,500}, + {23548,-7608,595,1200},{23388,-1479,391,520},{23370,1054,380,750},{23370,1054,380,750},{23405,-3309,624,1400},{23377,167,656,750},{23505,-7023,644,975},{23367,2671,223,1000}, + {23426,-2814,358,455},{23389,1766,612,520},{23397,1127,607,500},{23451,-4257,433,440},{23471,-5237,544,1000},{23414,-56,651,520},{23381,3901,511,335},{23437,-2628,619,1600}, + {23412,1001,526,500},{23499,-5991,595,660},{23412,1612,593,705},{23464,-3916,657,1200},{23445,-2314,578,500},{23468,-3913,604,925},{23396,3837,642,470},{23457,-2821,632,485}, + {23457,-2102,584,510},{23382,5392,597,440},{23466,-2978,366,2600},{23422,2999,652,675},{23473,-1930,538,520},{23467,-879,650,660},{23467,-879,648,660},{23402,5207,674,690}, + {23399,5463,574,950},{23484,-2315,634,1000},{23494,-3441,467,520},{23524,-4774,623,425},{23506,-3121,634,470},{23446,4035,524,530},{23507,-2382,496,455},{23530,-4466,464,675}, + {23285,7735,496,425},{23470,3467,611,500},{23438,5451,587,485},{23230,8045,658,600},{23457,4680,575,720},{23504,1205,625,530},{23566,-4949,604,470},{23484,3664,600,1200}, + {23484,3664,507,1600},{23464,5042,584,520},{23598,-6029,648,600},{23566,-3742,524,520},{23549,-1968,474,350},{23567,-3471,475,2000},{23525,1602,601,520},{23479,5792,645,485}, + {23539,1967,452,975},{23545,1285,533,1000},{23441,6928,562,425},{23548,1846,581,950},{23609,-4182,594,1000},{23585,-1504,631,500},{23602,-1567,541,750},{23569,2630,384,1200}, + {23579,1367,648,550},{23697,-6544,618,875},{23697,-6544,639,750},{23601,252,588,580},{23607,643,265,470},{23600,3252,556,510},{23655,-2806,651,720},{23571,5236,551,1200}, + {23618,1726,614,1000},{23635,545,558,1000},{23668,-3468,561,1600},{23642,89,633,500},{23686,-4019,642,550},{23652,-180,540,1400},{23687,-3792,601,540},{23721,-5244,607,470}, + {23669,-612,624,500},{23655,1542,367,950},{23661,735,443,600},{23753,-5321,577,1200},{23682,1412,571,925},{23749,-4891,584,975},{23757,-4540,612,775},{23778,-5506,573,2600}, + {23706,1379,600,470},{23724,-382,553,875},{23824,-6515,654,875},{23701,3174,644,425},{23664,5547,592,455},{23719,1814,409,350},{23714,2816,585,600},{23740,-343,353,1000}, + {23788,-4706,601,470},{23774,-3363,395,1200},{23834,-6261,619,455},{23774,-2575,464,2400},{23667,6260,519,950},{23691,5538,586,925},{23757,220,523,520},{23740,2607,463,570}, + {23821,-5062,660,455},{23887,-6860,509,550},{23770,448,371,950},{23805,-2989,640,485},{23767,1513,520,245},{23782,-309,511,925},{23791,-1413,619,520},{23879,-6343,285,720}, + {23872,-6074,615,1200},{23782,2098,476,425},{23888,-6018,577,550},{23781,3566,482,485},{23833,-2017,503,2600},{23601,7779,432,925},{23840,-2533,459,2600},{23902,-6048,576,925}, + {23847,-2453,539,1800},{23848,-2398,542,2600},{23846,-1673,415,520},{23823,1740,636,500},{23862,-2734,614,1800},{23830,1320,610,600},{23866,-2524,587,1400},{23840,1608,609,675}, + {23817,4245,462,630},{23875,-1938,594,2000},{23888,-3108,621,440},{23888,-2627,562,925},{23903,-3986,603,1800},{23867,858,629,825},{23974,-6504,575,1600},{23927,-4816,631,735}, + {23807,5583,581,520},{23906,-3179,629,660},{23864,2031,544,350},{23922,-3397,512,925},{23923,-3396,562,1200},{23906,-1440,637,645},{23922,-2921,388,2600},{23934,-3618,580,500}, + {23914,-1483,613,950},{23892,1862,626,1400},{23866,4314,537,305},{23911,1566,385,660},{23942,-2098,585,2400},{23963,-3750,631,500},{23895,3795,545,750},{23998,-5402,610,2000}, + {23888,4257,575,1200},{24029,-6378,641,630},{23931,1441,554,485},{23955,-1428,488,2000},{23964,-2483,543,2000},{23988,-4174,499,500},{23971,-2611,289,2800},{23999,-4065,649,485}, + {24028,-5458,613,875},{23940,2688,415,470},{24003,-3840,341,2600},{23896,5891,631,1000},{23937,3970,631,500},{24072,-6254,625,1200},{24022,-4044,621,1000},{24008,-2262,232,3000}, + {24008,-1653,547,630},{24148,-7240,570,485},{24033,-3189,633,440},{23974,3664,562,425},{23988,2592,200,2400},{24020,-841,555,1000},{23945,5475,495,750},{24088,-5778,463,825}, + {24080,-4923,465,520},{24021,443,583,500},{23977,4988,605,750},{24066,-2914,603,455},{24031,1782,512,520},{24085,-3860,489,1800},{24026,3330,541,600},{24084,-2587,500,425}, + {24089,-3200,601,690},{24036,2985,499,1000},{24057,2280,483,925},{24098,-2473,621,500},{24107,-3321,610,775},{24115,-3786,590,705},{24094,499,608,510},{24109,-1137,507,675}, + {24109,-1137,477,675},{24185,-5619,616,720},{24162,-4517,472,875},{24052,5292,593,425},{24070,4604,476,1200},{24083,3663,583,950},{24136,-1981,262,2800},{24136,-1980,492,2600}, + {24047,5857,401,630},{24165,-3680,423,2600},{24153,-2361,592,1400},{24144,-629,653,720},{24150,-614,641,500},{24182,-3676,573,735},{24141,810,629,925},{24170,-2067,396,2800}, + {24233,-5793,557,1200},{24079,5941,619,335},{24176,-1407,632,560},{24185,-2087,432,570},{24197,-2446,633,1600},{24214,-3911,705,825},{24214,-3909,665,1000},{24203,-2633,538,320}, + {24190,-1275,578,1200},{24218,-2369,588,1200},{24191,989,563,825},{24184,2182,614,440},{24238,-3265,619,500},{24247,-3355,554,1400},{24202,1705,500,520},{24202,1705,625,485}, + {24212,853,573,305},{24225,345,591,425},{24248,-1834,647,690},{24219,1721,614,1000},{24230,638,597,485},{24282,-4112,586,750},{24246,-347,537,440},{24276,-2942,513,455}, + {24224,3649,476,485},{24334,-5554,581,720},{24509,-7870,468,275},{24511,-7867,527,455},{24332,-5367,583,350},{24219,4493,426,1200},{24337,-5463,494,520},{24158,6781,544,1000}, + {24299,-1945,630,1400},{24300,-1946,401,2400},{24308,-2793,459,2600},{24307,-2842,567,1200},{24386,-6369,385,580},{24300,-1006,494,925},{24287,971,653,750},{24303,-855,543,900}, + {24088,7679,556,925},{24287,1667,608,1000},{24226,5794,633,975},{24427,-6794,575,900},{24236,5583,649,500},{24291,2349,570,290},{24396,-5791,563,925},{24324,-422,625,975}, + {24344,-2442,641,2000},{24292,3334,629,470},{24359,-3301,592,500},{24381,-4737,514,1400},{24295,3642,563,455},{24331,502,548,425},{24346,-1184,522,455},{24385,-4290,614,470}, + {24295,4237,587,440},{24362,-2111,641,1000},{24319,2667,650,705},{24366,-1854,632,470},{24372,-2548,605,1600},{24418,-5381,544,320},{24359,-369,274,350},{24356,590,631,470}, + {24425,-5007,499,615},{24434,-5309,633,800},{24391,-837,550,580},{24396,-1485,609,1000},{24472,-5790,649,630},{24367,3386,564,600},{24367,3386,666,590},{24387,1881,569,455}, + {24424,-2130,661,1200},{24423,-395,618,775},{24395,2742,614,470},{24310,6714,621,520},{24457,-2861,478,1000},{24482,-4267,545,925},{24496,-5016,402,520},{24510,-5514,577,500}, + {24419,2915,578,925},{24458,-469,324,510},{24478,-2022,629,425},{24489,-3091,549,660},{24475,-1487,594,440},{24271,7588,548,1400},{24514,-3943,612,1200},{24516,-2417,455,875}, + {24313,7521,639,470},{24562,-4957,533,1800},{24530,-2559,289,2800},{24562,-4391,588,570},{24431,5976,540,290},{24502,2113,605,520},{24364,7340,598,925},{24634,-6312,615,975}, + {24480,4904,591,410},{24498,3971,546,705},{24494,4631,389,2000},{24552,103,482,750},{24601,-3919,540,550},{24548,1915,375,775},{24566,-208,623,1200},{24599,-3320,647,1000}, + {24712,-7008,491,615},{24623,-4535,633,950},{24613,-3757,542,1400},{24454,6855,641,500},{24836,-7890,389,520},{24552,3089,485,500},{24603,-2004,450,500},{24616,-2970,663,600}, + {24617,-2970,584,600},{24559,3380,520,320},{24562,3370,539,425},{24699,-6406,527,690},{24574,3233,640,900},{24604,695,585,950},{24640,-2345,502,2600},{24640,-2345,592,2600}, + {24706,-5860,569,1200},{24680,-4756,447,2200},{24438,7576,495,675},{24635,1403,457,1200},{24676,-1846,442,2600},{24645,1889,670,245},{24744,-5776,606,520},{24655,1141,611,520}, + {24706,-3718,579,485},{24635,3739,554,875},{24671,235,607,520},{24771,-6163,520,500},{24545,6911,525,470},{24611,5521,574,950},{24693,-760,523,305},{24695,-837,463,925}, + {24595,6170,567,520},{24743,-4624,535,2800},{24600,6151,274,520},{25900,-8757,657,550},{24735,-2643,96,335},{24858,-7099,550,485},{24714,66,539,440},{24720,-813,648,720}, + {25147,-8324,657,440},{25524,-8636,604,950},{24745,-1455,568,580},{24755,-2512,479,2600},{24792,-4182,533,2800},{24785,-3470,423,2600},{24762,-752,650,825},{24784,-2654,610,485}, + {24716,4188,504,260},{24778,-1661,428,520},{24756,2149,277,530},{24773,198,382,1000},{24718,5141,629,510},{24894,-6550,552,520},{24764,2048,525,520},{24803,-2147,445,825}, + {24781,2220,576,425},{24852,-4405,494,3000},{24752,4896,645,975},{24776,3522,625,425},{24815,552,563,1200},{24815,1149,484,395},{24878,-4524,646,470},{24700,6877,500,1000}, + {24795,4560,565,900},{25077,-7752,424,500},{24909,-4286,547,3300},{24897,-2822,282,3000},{24909,-3526,416,410},{24972,-6099,618,1600},{24853,4244,420,1200},{24886,1706,641,950}, + {24811,6082,594,950},{24909,-232,575,470},{25035,-6830,591,1600},{24643,7896,556,750},{24961,-4340,583,2600},{24929,-1057,256,3300},{24918,1550,630,1200},{25021,-6045,618,720}, + {24977,-3722,591,1200},{24951,-654,609,1000},{24787,7261,630,470},{24945,1369,631,675},{25084,-6743,603,1000},{24905,4661,579,520},{24905,5290,553,1200},{24906,5292,508,1200}, + {24906,5292,653,975},{25033,-4876,565,4500},{25042,-4965,565,2800},{24991,-955,635,705},{25014,-2041,626,510},{24766,7745,634,485},{25044,-3315,587,570},{25040,-2447,609,750}, + {25015,421,693,925},{25016,422,577,975},{25039,-1774,496,530},{25076,-4607,623,675},{24923,6307,616,425},{24950,5602,529,485},{24969,4893,490,320},{25030,-100,624,825}, + {25047,-1992,557,690},{25021,1240,608,925},{25167,-6711,513,875},{25043,118,574,720},{25094,-4112,620,900},{25117,-5315,596,520},{25025,2486,606,470},{25097,-4111,612,1800}, + {25095,-3816,605,1200},{25091,-3211,646,1400},{25032,3160,281,600},{25040,2692,592,720},{25118,-4084,571,2400},{25159,-5850,574,2600},{25107,-2746,658,950},{25217,-6903,192,470}, + {25125,-2851,602,925},{25183,-5834,558,3000},{25072,3892,353,530},{25170,-3938,548,500},{25097,3404,599,720},{25023,6459,483,500},{25137,102,603,1200},{25171,-2553,671,540}, + {25074,5569,616,950},{25134,1575,556,305},{25146,858,515,425},{25245,-5904,376,425},{25130,4322,605,440},{25307,-6768,632,470},{25179,206,610,925},{25297,-6538,613,1400}, + {25194,525,524,1200},{25211,-1491,603,1000},{25265,-5005,647,875},{25132,5678,485,720},{24947,7892,632,510},{25204,1359,635,550},{25237,-1567,610,875},{25254,-3429,229,470}, + {25183,4224,587,290},{25246,-1078,465,645},{25275,-3751,611,1200},{25289,-4123,522,3600},{25239,1326,591,975},{25297,-3805,308,2800},{25259,-265,632,735},{25308,-4185,649,794}, + {25258,725,549,925},{25350,-5791,594,335},{25308,-3802,357,2600},{25385,-6327,602,1000},{25231,4598,482,950},{25285,122,551,950},{25242,4343,613,500},{25343,-4305,596,305}, + {25266,2981,572,335},{25268,3255,613,500},{25351,-4181,545,3000},{25349,-4199,632,2800},{25350,-4236,473,2800},{25355,-4185,645,3300},{25265,4190,629,455},{25358,-4182,659,809}, + {25361,-4248,588,675},{25077,7751,598,690},{25302,1497,652,1200},{25336,-2042,588,570},{25294,2466,504,500},{25365,-4236,362,440},{25374,-4115,577,3600},{25365,-3059,635,825}, + {25404,-5068,633,2400},{25412,-5228,594,1200},{25489,-6927,579,875},{25354,-161,625,750},{25367,-1179,657,950},{25324,3170,532,750},{25365,-615,525,470},{25350,1017,438,1400}, + {25399,-3351,637,425},{25415,-4082,615,3000},{25401,-1681,637,500},{25465,-5599,313,455},{25332,4742,600,470},{25373,2096,541,520},{25430,-3326,548,425},{25457,-5064,555,1200}, + {25382,1362,634,705},{25420,-2315,558,1000},{25376,2573,608,550},{25384,1843,535,440},{25427,-1954,627,1800},{25490,-5316,406,440},{25436,-1096,619,520},{25503,-5460,565,875}, + {25472,-3762,609,925},{25442,938,320,470},{25511,-4865,600,520},{25438,1388,637,500},{25467,-1487,659,705},{25513,-4545,665,440},{25545,-5896,611,2600},{25438,2535,628,550}, + {25403,5004,656,320},{25443,2438,632,500},{25499,-2509,586,305},{25504,-2499,575,690},{25515,-3593,597,470},{25544,-5113,645,825},{25446,4251,634,455},{25579,-5324,529,660}, + {25401,6513,489,660},{25547,-3214,503,1400},{25512,658,659,825},{25526,-422,482,440},{25410,6504,641,735},{25610,-5771,573,2000},{25546,-1889,626,500},{25149,8204,423,550}, + {25592,-4716,606,950},{25507,3093,392,1000},{25524,2263,565,455},{25539,1495,631,1200},{25596,-3815,591,690},{25529,2720,655,675},{25550,845,633,900},{25484,5669,603,485}, + {25627,-4550,628,950},{25540,3357,525,925},{25558,2551,575,530},{25621,-3412,487,2800},{25407,7313,630,750},{25557,3188,636,500},{25578,1409,498,305},{25645,-4410,619,900}, + {25579,1451,652,550},{25619,-2049,630,1800},{25591,1361,593,975},{25599,1357,608,485},{25597,1969,635,440},{25658,-3723,598,950},{25476,6919,640,500},{25588,3541,669,290}, + {25662,-3545,613,3300},{25532,6065,613,485},{25632,70,601,630},{25655,-2156,630,925},{25597,3479,604,875},{25617,1960,617,1200},{25638,-89,564,2800},{25672,-2651,629,1000}, + {25634,1274,491,900},{25753,-6168,639,1400},{25808,-7072,622,485},{25654,973,637,425},{25655,1045,637,520},{25556,6460,610,550},{25672,-166,638,775},{25627,4381,643,925}, + {25621,4880,609,485},{25658,2208,556,455},{25706,-1761,599,500},{25720,-3040,597,875},{25706,-108,606,975},{25832,-6720,589,500},{25633,5447,583,645},{25633,5447,580,645}, + {25768,-4456,508,520},{25723,-388,636,550},{25915,-7453,625,1000},{25791,-4887,584,335},{25745,-1052,556,675},{25694,4052,634,950},{25701,3594,539,875},{25759,-1572,243,950}, + {25542,7530,621,750},{25804,-4324,333,705},{25807,-3951,567,975},{25807,-3882,630,485},{25707,5084,646,975},{25856,-5689,609,335},{25769,1247,657,975},{25806,-2525,654,1000}, + {25810,-2776,614,1200},{25739,4078,508,455},{25824,-3244,601,2800},{25794,789,633,500},{25776,2424,619,800},{25862,-3977,660,550},{25812,1059,533,320},{25823,35,665,660}, + {25763,5241,629,1200},{25720,6571,317,1800},{25883,-3355,553,3900},{25890,-3859,596,645},{25792,4975,604,875},{26005,-7005,653,600},{25884,-2660,511,485},{25884,-2660,507,500}, + {25896,-3021,621,1200},{25883,-1458,599,485},{25909,-3575,612,600},{25866,1439,348,275},{25866,1439,539,550},{25980,-5969,591,470},{25927,-3266,555,660},{25876,2484,314,925}, + {26052,-7012,541,1200},{25906,219,617,950},{25918,-624,609,500},{25913,121,588,2000},{25915,-45,473,470},{25976,-4663,548,520},{26050,-6777,478,470},{25876,3681,316,455}, + {25892,2374,596,470},{25970,-4413,576,1200},{25814,6287,556,750},{25959,-3255,636,2000},{25988,-5006,627,630},{25950,-2429,520,500},{25950,-2428,680,660},{25974,-3499,591,455}, + {25985,-4422,665,1200},{25958,-1631,643,320},{26286,-8086,588,305},{25940,2309,645,470},{25933,3310,482,2800},{25952,1732,600,975},{25965,1086,503,440},{25972,609,651,750}, + {25997,-1776,602,950},{25942,3729,465,950},{25920,4969,748,470},{26073,-5801,588,520},{26000,-592,632,510},{26100,-6286,570,2600},{26014,-1933,652,570},{26078,-5653,580,520}, + {25970,2882,565,500},{25960,3881,594,530},{26025,-2111,439,735},{26021,-1285,433,950},{26108,-6067,577,1400},{25912,6067,632,485},{26022,-1070,646,735},{26066,-3780,641,2200}, + {26082,-4747,525,2600},{26008,1806,500,320},{26050,-2500,327,2600},{26066,-3591,647,645},{26004,2554,538,925},{26073,-3722,593,485},{26023,2450,512,950},{26016,3247,539,600}, + {26084,-2814,535,425},{26105,-4416,512,1600},{26132,-5553,285,455},{26135,-5638,334,2800},{26039,1673,635,320},{26009,4624,559,350},{26071,-239,629,550},{26038,2876,635,600}, + {26014,4819,643,690},{26150,-5063,523,520},{26043,3997,551,615},{26126,-3470,616,1000},{26203,-6304,624,1600},{26117,-2144,585,520},{26115,-1845,621,1200},{26128,-2424,619,485}, + {26173,-5195,619,1400},{26099,885,577,485},{26171,-4584,529,1600},{26180,-5063,592,1200},{26044,5342,567,440},{26103,2296,574,750},{26113,1630,571,925},{26114,1561,635,1200}, + {26199,-5230,575,470},{26092,3715,547,1000},{26092,3715,452,1200},{26159,-2418,417,925},{26216,-5517,594,520},{26101,3858,649,630},{26149,-165,644,705},{26173,-2594,644,1000}, + {26111,3695,628,550},{26184,-2987,429,675},{26166,-509,454,705},{26191,-2972,600,1200},{26148,1692,598,290},{26176,-1251,621,645},{26158,760,606,1200},{26163,414,434,470}, + {26150,2688,641,875},{26277,-6068,362,1400},{26223,-3678,602,500},{26170,2008,554,2000},{26236,-3852,639,950},{26201,-821,637,675},{26285,-5692,595,1600},{26169,3470,594,1000}, + {26221,33,544,800},{26269,-3730,269,2600},{26168,4826,585,900},{26296,-4988,295,2600},{26142,6005,565,975},{26245,-592,637,500},{26295,-4604,603,630},{26152,5865,651,900}, + {26260,-106,531,510},{26295,-3370,644,470},{26125,6731,643,500},{26285,-2396,481,1200},{26294,-2627,605,1200},{26259,1192,639,975},{26310,-3428,617,720},{26328,-4117,584,1200}, + {26284,272,559,520},{26390,-5985,628,2000},{26268,2611,441,455},{26340,-3710,163,2600},{26273,3116,561,520},{25990,8014,572,425},{26383,-5335,610,1200},{26267,3888,643,660}, + {26306,1193,642,1200},{26296,2841,562,975},{26337,-574,562,875},{26368,-3258,570,3900},{26261,5230,279,580},{26391,-4651,459,1000},{26300,3427,656,550},{26393,-3744,648,1000}, + {26268,5788,640,470},{26335,1926,564,660},{26341,1632,569,500},{26343,1484,648,290},{26369,-1124,555,1400},{26383,-2204,657,1400},{26414,-3864,429,500},{26436,-5006,593,500}, + {26365,959,581,950},{26452,-5450,525,875},{26328,4124,574,485},{26361,1650,640,800},{27039,-8521,645,675},{26433,-4300,187,735},{26304,5518,488,850},{26307,5517,487,900}, + {26373,1256,208,875},{26436,-3807,626,580},{26454,-4288,610,1400},{26400,2100,610,825},{26338,5756,617,550},{26440,-1540,354,750},{26440,-1557,594,750},{26393,3730,610,510}, + {26403,2818,638,425},{26608,-7222,649,645},{26299,6814,505,500},{26446,-812,462,1400},{26454,-1093,575,500},{26510,-4942,477,705},{26415,3079,602,875},{26438,2431,577,950}, + {26532,-4692,579,1000},{26375,6188,523,600},{26416,4859,537,485},{26479,203,626,500},{26552,-5051,624,305},{26474,1333,612,660},{26505,-215,619,470},{26471,3274,637,550}, + {26562,-3903,241,2800},{26535,-1288,426,950},{26643,-6472,362,470},{26571,-3695,554,425},{26499,3120,603,320},{26604,-5183,515,570},{26623,-5755,601,520},{26578,-3305,640,335}, + {26487,4601,380,2400},{26530,1518,634,975},{26538,631,595,530},{26517,3129,628,500},{26527,2451,636,485},{26582,-2788,636,775},{26549,1595,552,690},{26586,-2168,487,660}, + {26424,6876,480,675},{26618,-4273,587,975},{26417,6957,642,615},{26516,4347,659,470},{26595,-1351,639,750},{26595,-708,630,2800},{26562,2456,552,440},{26587,457,277,470}, + {26584,1430,624,675},{26515,5731,677,500},{26429,7246,586,510},{26534,5182,599,500},{26584,2433,571,580},{26614,258,617,975},{26615,258,656,1000},{26607,1441,619,925}, + {26577,4408,634,440},{26678,-3811,643,1200},{26716,-5540,611,775},{26690,-4013,303,720},{26689,-2783,454,645},{26694,-2248,618,540},{26600,5380,575,950},{26642,3150,623,1000}, + {26690,-1473,594,1200},{26712,-2698,635,2200},{26762,-5361,592,2600},{26661,2772,342,550},{26790,-6016,578,500},{26647,3888,652,500},{26649,3932,668,455},{26678,1770,572,975}, + {26729,-3170,483,1400},{26697,271,375,1000},{26746,-3704,321,470},{26755,-4009,481,950},{26780,-5313,609,1200},{26708,380,622,1200},{26833,-6549,649,500},{26942,-7618,607,470}, + {26548,7215,458,675},{26549,7216,579,600},{26710,2057,569,550},{26733,196,647,500},{26794,-4560,611,540},{26678,4761,643,950},{26720,1926,612,975},{26789,-4077,596,320}, + {26720,2562,512,470},{26780,-3056,666,1000},{27136,-8149,635,455},{26806,-3480,590,1800},{26808,-3442,584,520},{26822,-4200,620,630},{26768,1195,617,485},{26820,-3411,606,485}, + {26823,-3502,645,1200},{26824,-3562,603,720},{26760,2932,550,485},{26770,2232,598,775},{26727,5078,502,950},{26833,-3473,617,1400},{26835,-3490,560,455},{26800,-124,635,500}, + {26844,-3479,638,1200},{26816,-614,621,630},{26848,-3475,596,1200},{26849,-3483,642,875},{26751,4839,668,2000},{26799,1533,646,500},{26826,-1090,618,485},{26815,130,595,425}, + {26861,-3447,596,1200},{26781,4007,646,600},{26831,610,577,705},{26878,-3648,606,1200},{26872,-2489,620,3900},{26802,3998,604,440},{26800,4664,638,500},{26920,-4434,486,470}, + {26856,1113,638,675},{26833,4001,516,485},{26923,-4031,643,455},{26898,-1880,652,900},{26917,-2807,580,825},{26908,-1581,589,975},{26945,-4172,488,350},{26949,-3914,629,1000}, + {26908,67,582,2600},{26886,2605,546,720},{26920,-408,547,510},{26896,2246,558,455},{26838,5687,375,470},{26927,7,597,925},{26923,649,629,1200},{26973,-3686,574,520}, + {26966,-2876,601,2400},{26977,-3025,516,335},{26977,-3025,704,520},{26906,3725,386,485},{26936,1104,636,975},{26931,2400,630,580},{26976,-977,334,500},{26885,5597,610,750}, + {26995,-2382,476,1200},{26736,7696,504,660},{26920,4535,602,275},{26944,2925,370,520},{27000,-2034,621,500},{26915,5149,223,425},{26990,-482,587,425},{26963,3019,441,720}, + {27045,-3638,630,1600},{27006,63,637,925},{27012,-369,462,720},{26968,3629,600,550},{27007,437,464,2600},{27001,1675,467,500},{27016,293,397,2000},{27035,-1716,628,455}, + {27048,-2278,577,3000},{26754,7831,624,425},{26972,4548,648,1200},{27022,627,634,2600},{27012,1951,650,1400},{28370,-8761,528,455},{27024,1509,626,550},{27044,131,445,950}, + {27071,-2428,534,705},{26880,7200,545,720},{27015,3321,599,410},{27076,-2272,674,3000},{26998,4550,567,350},{27037,2160,518,520},{27038,2160,496,875},{27282,-7589,586,470}, + {27069,-536,676,1200},{27077,-818,594,675},{27077,-818,524,720},{26861,7517,636,500},{27097,-2436,597,4800},{27040,3331,615,425},{27060,2083,528,2400},{27121,-3590,600,470}, + {27195,-6455,641,470},{27063,2292,621,2400},{27126,-2958,469,690},{27166,-5009,366,2600},{27081,1961,650,1000},{27214,-6367,435,825},{27145,-3042,299,500},{27116,192,614,3000}, + {27160,-3602,595,550},{27171,-4342,577,875},{27171,-4342,577,875},{27314,-7367,585,675},{27136,250,403,500},{27079,4846,621,950},{27117,2394,634,750},{27153,-832,585,1400}, + {27156,-475,577,485},{27153,-45,634,520},{27143,1200,704,925},{27213,-4577,615,1600},{27249,-5904,638,440},{27261,-6200,549,600},{27180,-2144,628,3000},{27138,2165,615,455}, + {27118,4008,652,645},{27151,2222,506,305},{27202,-2846,457,530},{27125,4195,634,750},{27146,3223,571,500},{27195,-1715,552,485},{27183,873,464,520},{27184,956,373,900}, + {27234,-3667,658,2800},{27223,-2547,661,2400},{27360,-7075,673,1600},{27176,3056,504,645},{27195,1307,663,950},{27250,-3272,643,500},{27277,-4751,607,485},{27189,2876,383,1200}, + {27252,-3073,553,485},{27196,2610,586,925},{27196,2610,590,925},{27281,-4595,453,500},{27214,1428,637,875},{27243,-1393,639,500},{27277,-4136,586,720},{27219,2081,436,2600}, + {27273,-3380,616,2800},{26305,8659,436,975},{27172,5082,629,500},{27187,4346,500,520},{27178,4971,632,950},{27201,3640,548,470},{27222,2005,510,800},{27239,399,573,720}, + {27357,-6369,647,440},{27247,312,569,675},{27281,-1984,636,900},{27229,3047,638,485},{27267,332,551,470},{27293,-2370,498,500},{27299,-2890,651,1200},{27254,1648,609,1200}, + {27330,-4134,547,2600},{27392,-6306,560,500},{27241,3846,640,470},{27250,3647,558,520},{27450,-6823,633,1200},{27001,8000,604,645},{27004,8000,568,645},{26270,8697,579,950}, + {27344,-2106,386,1400},{27329,-401,659,950},{27294,3345,588,925},{27298,3141,497,305},{27357,-2171,544,455},{27263,5429,595,485},{27397,-4421,546,520},{27428,-5602,533,2400}, + {27332,2188,612,440},{27425,-5107,606,1200},{27380,-2073,538,3000},{27380,-2039,595,3300},{27318,4115,636,500},{27388,-1866,607,1000},{27327,3877,604,1200},{27278,6041,649,975}, + {27492,-6389,618,615},{27590,-7504,547,1000},{27399,-362,636,900},{27368,2921,656,630},{27441,-3676,311,305},{27440,-3411,616,2400},{27402,238,601,290},{27435,-2865,619,875}, + {27435,-2829,640,600},{27733,-8023,595,470},{27430,-1737,575,440},{27467,-4229,630,3000},{27422,-301,600,570},{27437,-1846,654,3900},{27451,-2704,465,455},{27435,-976,631,800}, + {27427,101,663,720},{27391,4216,559,1600},{27467,-2560,651,485},{27389,4521,629,580},{27468,-1862,684,3000},{27367,5659,637,735},{27347,6440,503,675},{27451,1378,630,2000}, + {27453,1813,599,1200},{27428,4094,611,520},{27453,2330,663,425},{27581,-6149,436,440},{27523,-3749,645,470},{27479,726,539,470},{27504,-1583,539,440},{27525,-2983,270,455}, + {27479,2445,527,455},{27538,-2492,625,275},{27558,-3866,510,440},{27535,-1886,575,630},{27550,-2843,616,925},{27382,6876,595,485},{27522,338,486,520},{27497,2967,599,440}, + {27507,2196,495,335},{27533,-290,326,500},{27572,-3667,534,875},{27638,-6302,614,925},{27497,3606,433,470},{27537,544,613,2400},{27587,-3624,555,500},{27608,-4411,525,2600}, + {27524,2986,563,875},{27525,2887,512,825},{27576,-1022,633,1200},{27604,-3438,185,1200},{27498,5135,630,485},{27580,-1201,573,1400},{27554,2329,541,350},{27565,1203,589,950}, + {27591,-893,468,510},{27570,1783,525,455},{27528,4973,640,485},{27576,1669,622,500},{27626,-3076,560,500},{27634,-3599,615,500},{27601,-358,638,660},{27539,4912,505,320}, + {27618,-708,631,500},{27648,-3395,630,2000},{27672,-4812,546,500},{27592,2177,384,470},{27634,-2054,481,470},{27674,-4597,351,2400},{27624,-158,615,660},{27823,-7397,589,500}, + {27629,508,674,1200},{27599,3874,636,440},{27641,803,565,520},{27606,3951,512,925},{27624,2740,627,1000},{27721,-4907,413,520},{27648,1497,637,1200},{27696,-2982,592,615}, + {27749,-5752,576,500},{27693,-2663,631,800},{27711,-3900,564,950},{27595,5330,632,925},{28056,-8181,627,1400},{27699,-2542,281,485},{27703,-2676,627,925},{27730,-4385,636,520}, + {27784,-6228,464,1600},{27650,2983,583,950},{27680,20,521,600},{27699,-1780,620,1400},{27519,7134,422,1000},{27732,-3885,663,1400},{27748,-4722,570,470},{27598,5880,498,975}, + {27667,2645,653,2400},{27696,375,607,455},{27724,-2658,650,925},{27526,7273,357,645},{27699,619,573,1400},{27734,-2526,659,2600},{27730,-1457,470,925},{27776,-4191,604,925}, + {27745,-1458,596,1200},{27755,-1873,566,500},{27794,-4592,496,1800},{27742,-199,539,500},{27777,-3299,534,925},{27798,-4351,572,470},{27801,-4576,507,2400},{27837,-5871,644,550}, + {27756,-572,628,520},{27752,407,669,2600},{27809,-3970,516,925},{27740,2387,590,1800},{27786,-1840,514,1000},{27650,6556,482,485},{27786,-1080,572,2600},{27797,-1912,668,590}, + {27825,-3989,622,675},{27693,5955,643,500},{27767,2082,650,520},{27838,-4231,464,520},{27835,-3872,632,1000},{27835,-3873,565,1200},{27863,-5289,622,470},{27777,1693,577,950}, + {27809,-1464,637,900},{27799,-100,594,900},{27803,366,643,1000},{27818,-1487,550,455},{27849,-3302,528,2600},{27847,-2403,549,440},{27841,-1485,576,950},{27835,-591,636,1000}, + {28299,-8332,716,485},{27864,-2422,651,705},{27819,2362,584,425},{27835,827,642,1400},{27821,3055,548,1400},{27884,-2084,648,750},{27876,-1098,514,520},{27835,3089,659,2200}, + {27900,-2970,637,485},{27880,-824,385,455},{27805,5212,656,1200},{27858,2047,657,925},{27870,1089,640,975},{27880,1820,578,1000},{27814,5705,477,645},{27781,6544,659,800}, + {27888,2361,561,510},{27897,1698,621,580},{28076,-7143,401,500},{27849,5235,536,500},{27881,3446,610,2000},{27912,912,539,675},{27981,-4791,586,825},{27916,667,545,705}, + {27948,-2140,594,800},{27952,-1400,647,1200},{27963,-2350,581,1400},{27990,-4319,537,320},{27930,1142,642,1200},{27940,-31,575,975},{28196,-7787,639,630},{27929,1620,629,520}, + {28059,-6464,637,875},{27915,3347,542,1400},{27972,-2105,586,560},{27960,-319,649,615},{27958,-111,666,735},{27923,3878,3,1000},{27959,883,640,260},{27919,4322,620,750}, + {28090,-6455,578,500},{28038,-4810,649,470},{27744,7755,564,440},{28000,-779,584,440},{27990,526,638,630},{27953,3967,604,290},{27996,736,628,520},{28046,-2383,623,875}, + {28136,-6487,479,875},{27906,6549,606,750},{28043,-1456,642,720},{28130,-6110,604,455},{28001,3085,636,500},{27989,4093,625,1200},{27939,6253,574,1000},{28005,3837,645,850}, + {28057,-905,472,720},{28095,-3832,513,950},{28135,-5688,622,455},{28073,-1928,635,290},{28065,-707,615,500},{27604,8318,617,950},{28103,-3672,632,500},{28243,-7299,606,1200}, + {27997,5220,600,1000},{28108,-3564,487,2600},{28042,3162,641,1400},{28124,-3969,543,455},{28088,-828,490,520},{28053,3475,647,2000},{28096,-682,631,645},{28121,-2501,583,2000}, + {28216,-6508,573,800},{28175,-5009,654,750},{28133,-2100,636,675},{28141,-2699,317,1400},{28121,206,502,1200},{28082,3930,645,425},{28023,6275,609,500},{28090,3656,601,520}, + {28096,3193,570,735},{28150,-1961,642,290},{28159,-2239,537,440},{28117,2359,631,660},{28137,550,583,975},{28066,5554,504,1200},{28194,-4041,524,550},{28109,3967,506,900}, + {28108,3967,602,735},{28110,3961,514,800},{28110,3961,537,750},{28168,-1012,571,720},{28119,3761,436,900},{28120,3759,573,750},{28140,2198,651,1200},{28162,-96,590,950}, + {28087,5387,611,950},{28142,2055,419,660},{28221,-4368,549,925},{28179,-475,422,560},{28152,2666,483,455},{28236,-4581,581,520},{28187,-570,520,500},{28167,1871,617,425}, + {28240,-4343,561,1200},{28176,1818,436,875},{28232,-3475,662,485},{28123,5490,623,550},{28219,-1860,647,550},{28155,4144,607,1800},{28305,-6219,422,2600},{28108,6105,599,530}, + {28201,424,621,425},{28240,-1914,675,975},{28242,-2032,524,440},{28207,2351,615,630},{28175,4632,652,950},{28199,3176,606,2400},{28079,7079,644,470},{28242,-591,599,470}, + {28168,5299,588,2600},{28240,84,625,975},{28222,1933,588,975},{28317,-5211,517,520},{28271,-2216,661,825},{28258,-791,680,400},{28192,4908,640,975},{28231,2505,659,975}, + {28311,-4660,554,350},{28330,-5193,631,2200},{28274,-977,583,720},{28326,-4836,619,950},{28207,4877,612,825},{28325,-4659,619,975},{28243,3163,664,1000},{28269,1098,655,425}, + {28244,3281,591,2400},{28284,-332,610,720},{28247,3255,525,925},{28312,-2665,629,520},{28315,-2938,613,485},{28317,-3073,663,1400},{28252,3336,345,1400},{28424,-6723,444,675}, + {28363,-4988,660,950},{28301,1397,614,1400},{28326,-958,634,825},{28423,-6280,648,455},{28290,2878,618,485},{28307,2143,548,1200},{28350,-2136,569,485},{28290,3654,609,1800}, + {28354,-2274,483,470},{28144,7409,527,500},{28303,4138,628,1200},{28368,-1560,510,2000},{28378,-2267,499,455},{28382,-2630,202,2600},{28407,-4271,536,500},{28290,5298,551,510}, + {28159,7543,535,975},{28280,5939,466,510},{28388,-1638,579,690},{28465,-6020,514,470},{28400,-2317,593,1400},{28417,-3734,538,1400},{28501,-6665,601,500},{28343,3697,558,2600}, + {28356,2791,562,440},{28369,2264,459,560},{28462,-5294,487,1000},{28386,662,557,500},{28439,-3982,631,925},{28331,5071,492,530},{28356,4123,730,675},{28363,3690,430,290}, + {28372,3397,602,520},{28406,420,462,875},{28406,420,498,875},{28409,-180,622,1200},{28411,247,615,520},{28434,-2066,508,1000},{28372,4160,544,500},{28402,1800,663,1200}, + {28403,1811,569,485},{28427,-585,483,470},{28443,-2111,351,485},{28459,-3104,612,455},{28468,-3711,487,720},{28344,5749,622,425},{28370,4886,577,705},{28459,-2488,662,1000}, + {28480,-3953,649,1000},{28383,4395,404,275},{28432,254,557,1200},{28460,-2253,614,950},{28141,7994,639,800},{28587,-6876,588,630},{28426,3290,522,615},{28460,624,621,675}, + {28486,-1857,637,570},{28456,1736,538,630},{28485,-1284,553,2000},{28470,1391,589,750},{28510,-2494,636,500},{28526,-3706,669,1200},{28527,-3706,640,1400},{28469,1979,650,1600}, + {28477,1362,523,630},{28482,1014,675,2600},{28451,3827,589,1600},{28419,5782,566,485},{28491,1507,402,485},{28554,-4191,623,1000},{28474,3269,324,1200},{28469,4068,622,2400}, + {28360,7130,482,500},{28494,2623,527,470},{28541,-2270,624,425},{28499,2281,629,305},{28437,5823,646,950},{28480,3922,641,2000},{28539,-1528,632,540},{28411,6526,563,520}, + {28578,-4210,475,1200},{28599,-5102,593,500},{28432,6240,645,550},{28500,3215,493,470},{28542,-574,402,485},{28565,-2988,260,950},{28562,-2485,565,455},{28475,5081,630,520}, + {28582,-3825,574,750},{28527,1931,639,485},{28339,7579,622,975},{28534,2083,669,2600},{28508,4068,665,290},{28532,2629,569,2000},{28577,-1925,605,540},{28523,3380,601,500}, + {28578,-1910,637,675},{28540,2503,672,470},{28546,2226,640,900},{28559,837,630,470},{28573,-370,542,975},{28506,5053,538,2600},{28610,-3105,550,1000},{28545,3362,639,2000}, + {28658,-5234,516,645},{28588,182,583,900},{28536,4693,501,825},{28572,1966,609,485},{28617,-2174,377,510},{28518,5566,548,550},{28604,333,673,875},{28610,-568,690,400}, + {28747,-6842,533,520},{28593,2127,652,720},{28673,-4830,597,1000},{28472,6953,652,1000},{28624,-403,542,485},{28660,-3706,493,630},{28660,-3706,499,630},{31719,-8896,547,750}, + {28553,5226,631,520},{28642,-1566,597,1200},{28633,-151,653,735},{28672,-3781,616,550},{28722,-5572,649,485},{28674,-2767,332,485},{28635,1386,299,1000},{28656,-488,344,1200}, + {28624,3174,556,350},{28624,3073,606,320},{28672,-1623,603,1400},{28688,-2864,604,455},{28679,-1874,629,1400},{28709,-4050,459,485},{28659,823,609,245},{28645,2992,631,350}, + {28679,64,656,1200},{28706,-2466,630,1200},{28449,7705,654,720},{28674,1107,509,1400},{28707,-1929,554,2600},{28666,2425,577,900},{28623,5340,538,1000},{28629,4992,643,455}, + {28666,2863,555,750},{28737,-3790,411,950},{28742,-3983,646,500},{28740,-3616,656,1000},{28749,-4189,588,1800},{28657,4141,649,2400},{28751,-3934,411,500},{28699,1685,607,550}, + {28686,3250,523,750},{28683,3610,528,1800},{28702,2170,623,705},{28744,-2102,289,720},{28745,-1980,613,485},{28725,607,522,750},{28717,1685,648,675},{28776,-3901,636,1600}, + {28746,-43,634,2000},{28783,-2950,630,1200},{28819,-5049,613,520},{28727,3460,674,590},{28804,-3758,657,580},{28912,-6919,627,850},{28711,5243,581,485},{28812,-2166,641,520}, + {28831,-2591,580,485},{28930,-6666,553,875},{28817,-794,534,2400},{28788,2674,636,675},{28867,-4519,592,520},{28831,-1228,551,440},{28794,3128,598,1200},{28785,4043,618,925}, + {28814,1685,673,1200},{28815,2155,593,950},{28843,229,515,1400},{28843,552,649,950},{28909,-4547,540,455},{28744,6598,625,1000},{28889,-2418,625,615},{28888,-2526,485,520}, + {28802,4985,675,560},{28802,4986,657,560},{28792,5686,512,520},{28914,-3352,625,600},{28954,-5339,638,645},{28844,3915,439,2600},{28876,2020,600,520},{28883,1508,557,520}, + {28882,2123,564,900},{28885,3053,585,350},{28913,483,559,925},{28941,-1895,496,520},{28899,2746,654,1400},{28905,2139,477,2200},{28911,1454,563,1200},{28900,2793,616,660}, + {28848,5771,499,470},{28814,6766,307,510},{28883,5007,627,520},{28729,7656,513,775},{28945,203,619,975},{28909,3813,436,500},{28945,1160,528,750},{28992,-3542,559,2400}, + {28975,-1554,606,455},{28943,2303,543,3000},{28964,109,510,470},{29102,-6837,634,470},{28971,34,641,500},{28921,4700,600,660},{29011,-3182,658,350},{28950,3102,668,925}, + {28972,962,632,510},{28970,1961,658,1400},{29016,-2240,558,750},{28928,5337,377,510},{29071,-5442,505,1000},{29037,-3498,648,550},{28991,1237,553,750},{28997,1154,602,950}, + {29014,-542,501,520},{29054,-4202,634,485},{28977,3339,660,2600},{29015,-89,549,1200},{29066,-4446,401,1200},{28976,3745,622,1000},{29040,-1923,626,2000},{29042,-1785,393,750}, + {28966,4957,631,485},{29043,-1596,461,2600},{29080,-4480,429,720},{29046,-1831,587,510},{28991,3733,631,520},{29014,3519,631,1200},{29059,-820,631,2400},{29097,-4062,397,1400}, + {29059,-25,583,520},{29109,-4372,617,335},{28990,5438,626,975},{28889,7336,445,470},{29077,-740,632,500},{29070,991,635,660},{29113,-2787,604,2600},{29007,5765,591,335}, + {29078,1492,664,735},{29071,2626,518,1800},{29064,3352,606,500},{29127,-2931,593,455},{28994,6439,652,1200},{29132,-2451,503,875},{29137,-2396,543,440},{29095,2026,640,1200}, + {29036,5777,643,470},{29126,-488,652,775},{29134,-1390,569,455},{29089,3322,637,520},{29109,1694,625,950},{29195,-5433,569,440},{29017,6571,459,950},{29103,2962,497,2400}, + {29124,1194,516,520},{29109,2809,653,2000},{29158,-2178,559,455},{29103,3645,636,485},{29137,311,336,705},{29155,-1505,572,1400},{29160,-1455,670,975},{29174,-2974,567,1200}, + {29085,5027,651,1200},{29099,4339,584,530},{29280,-6843,596,440},{29134,2027,631,1000},{29137,1980,516,500},{29136,2491,619,645},{29163,34,466,720},{29247,-5544,613,500}, + {29160,1302,574,660},{29156,2010,563,1000},{29162,1989,581,350},{29235,-4345,571,875},{29264,-5511,630,675},{25924,8904,638,335},{29154,3632,515,1200},{29189,1428,632,2200}, + {29209,293,585,1800},{29247,-2699,552,455},{29256,-3209,660,705},{29225,195,580,1000},{29144,5803,660,1400},{29234,-704,661,645},{29190,3794,634,2400},{29232,25,625,470}, + {29218,2466,444,350},{29224,2477,581,500},{29234,1460,556,500},{29186,5232,575,975},{29234,2028,633,2000},{29273,-2131,613,950},{29322,-5319,575,675},{29254,290,609,425}, + {29161,6256,638,425},{29267,-279,503,335},{29264,344,605,1000},{29334,-4527,561,875},{29268,2796,308,455},{29269,2797,511,1400},{29270,3623,625,1200},{29243,5173,379,875}, + {29284,2662,587,500},{29354,-4003,589,925},{29042,7960,605,925},{29380,-4810,490,500},{28878,8346,653,925},{29294,3445,474,2400},{29283,5031,553,485},{29281,5573,637,470}, + {29352,738,445,455},{29378,-1056,512,510},{29401,-2472,565,825},{29387,-746,634,500},{29389,-1225,627,500},{29461,-5798,618,500},{29497,-6669,639,975},{29340,3876,661,925}, + {29365,1977,500,1400},{29386,291,638,660},{29411,-1885,611,750},{29418,-2488,460,1200},{29371,2946,538,630},{29342,4926,596,290},{29426,-1823,564,455},{29367,4241,535,950}, + {29403,1115,668,925},{29422,-703,495,3000},{29418,-129,436,2000},{29329,6016,629,440},{29407,1439,638,500},{29275,7099,607,470},{29358,5124,573,645},{29403,2259,632,1200}, + {29367,4816,667,775},{29439,-1430,547,675},{29541,-6585,609,350},{29422,1127,598,600},{29395,3694,605,1400},{29416,2033,714,720},{29508,-5442,626,500},{29445,-465,546,705}, + {29492,-4528,625,720},{29309,6966,468,500},{29432,1646,566,520},{29498,-3943,661,950},{29398,5024,652,510},{29429,2933,643,2000},{29424,3838,650,2000},{29416,4469,517,510}, + {29411,5022,448,690},{29496,-2343,634,1200},{29470,338,635,2400},{29457,2078,648,500},{29503,-2343,597,485},{29480,540,517,2400},{29486,1657,638,440},{29518,-1629,620,470}, + {29449,4928,647,540},{29484,3015,469,520},{29502,1801,437,590},{29518,-62,567,925},{29494,3398,610,925},{29512,2048,650,3000},{29486,4282,540,1200},{29467,5497,582,660}, + {29517,2372,664,1800},{29527,1382,601,2000},{29489,4596,620,520},{29526,1748,437,520},{29563,-1612,506,735},{29531,2245,636,500},{29591,-3754,616,1400},{29517,4308,616,320}, + {29555,1219,634,1200},{29901,-8135,639,455},{29521,4552,506,675},{29589,-1547,549,675},{29564,1183,527,600},{29736,-7250,541,900},{29549,4025,623,850},{29505,6051,651,875}, + {29570,2933,649,735},{29569,3243,594,875},{29545,5053,596,590},{29547,5052,620,580},{29579,3068,605,975},{29593,2577,549,520},{29650,-3191,552,1400},{29598,2714,628,1600}, + {29552,5546,648,275},{29700,-5636,535,825},{29614,1330,626,1200},{29596,3416,605,1400},{29747,-6681,645,440},{29594,4177,584,350},{29659,-1976,486,485},{29647,-288,648,2400}, + {29607,3735,489,530},{29620,2926,682,720},{29642,761,591,925},{29616,3441,657,335},{29723,-5289,625,485},{29581,5802,622,600},{29620,4072,634,290},{29776,-6560,605,720}, + {29656,1061,272,455},{29591,5704,627,470},{29759,-6106,621,2000},{29624,4513,287,1200},{29641,3609,643,1200},{29646,3501,609,500},{29769,-5919,542,1000},{29701,-1370,611,850}, + {29666,2513,662,825},{29661,3373,499,645},{29665,3289,618,455},{29685,1853,382,320},{29756,-4756,594,335},{29730,-2879,605,720},{29695,2538,595,500},{29726,-1087,604,425}, + {29713,1069,644,630},{29687,3841,577,1400},{29718,1182,572,580},{29608,6934,592,950},{29724,1914,500,925},{29686,4791,612,320},{29816,-5497,574,520},{29816,-5498,650,950}, + {29718,3531,653,690},{29721,3344,644,3000},{29796,-3987,533,1000},{29770,-1076,539,750},{29757,790,651,2600},{29736,3871,611,550},{29748,2844,638,2000},{29770,887,77,825}, + {29867,-6117,624,900},{29780,-246,613,425},{29776,1042,511,630},{29805,-1905,592,550},{29782,963,625,975},{29669,6844,634,750},{29764,3291,423,260},{29777,2261,495,2600}, + {29764,3872,512,320},{29766,4060,569,2800},{29770,3783,606,290},{29801,1163,613,600},{29812,101,390,660},{29828,-1460,648,1000},{29807,1035,654,1600},{29801,2499,557,975}, + {29809,1867,623,3900},{29833,-311,565,875},{29766,5299,503,455},{29783,4738,620,720},{29857,-2394,618,470},{29967,-6916,575,875},{29834,440,653,1200},{29882,-4187,413,500}, + {29704,7027,383,530},{29807,3643,610,440},{29853,-857,579,425},{29877,-3305,646,470},{29924,-5793,653,440},{29928,-5890,526,1200},{29972,-6876,639,1000},{29800,4703,562,3300}, + {30015,-7291,396,1000},{29803,4793,591,2600},{29837,2408,458,1200},{29866,-823,571,1600},{29866,-824,649,1400},{29856,846,471,510},{29869,27,561,1000},{29896,-2630,470,550}, + {29867,714,615,950},{29883,-673,651,720},{29826,4781,629,3000},{29863,2432,552,1000},{29883,641,371,520},{30010,-6695,576,500},{29924,-2717,452,470},{29942,-3806,655,690}, + {29870,3700,576,590},{29878,3020,657,1800},{29832,5752,514,2000},{29901,1663,536,550},{29906,1142,528,975},{29840,5971,606,925},{30047,-6694,531,470},{29897,3849,494,2000}, + {29949,-1549,502,925},{29908,3508,389,500},{29925,2100,648,950},{29974,-3054,628,485},{29974,-2620,483,540},{29891,5244,492,900},{29918,3625,602,2000},{30011,-4935,617,500}, + {29944,1679,553,1200},{29993,-3528,437,2400},{29996,-3470,530,900},{30044,-5938,513,260},{29884,5825,609,510},{30011,-4304,614,350},{29931,4037,545,2000},{30007,-3770,595,520}, + {30020,-4511,581,800},{30008,-3370,566,645},{29919,5090,643,975},{29898,5885,496,425},{29908,5669,612,900},{29969,1949,347,350},{29984,138,617,520},{29995,-996,588,630}, + {29948,4226,643,925},{30036,-4081,629,950},{29966,3098,549,1200},{29979,2310,567,750},{29964,3811,632,2000},{30035,-2274,601,530},{30139,-6732,607,570},{30001,1752,537,290}, + {29984,4577,592,875},{29998,3704,519,2400},{30025,856,591,425},{30049,-1364,571,925},{30066,-2771,458,290},{29981,5206,615,2000},{30089,-3794,477,425},{30028,2775,464,900}, + {29962,6353,596,925},{30031,3710,620,500},{30044,2480,588,2200},{30051,2494,522,720},{30093,-2260,645,660},{30108,-3206,499,485},{30034,5010,505,485},{30100,-747,672,690}, + {30082,1850,596,455},{30209,-6635,645,425},{30088,1603,567,1200},{30218,-6618,356,540},{29967,7037,633,520},{30110,-71,568,440},{30138,-3300,653,1400},{30103,728,552,500}, + {30091,2990,571,540},{30127,-1160,634,735},{30103,1707,580,590},{30185,-5288,494,335},{30190,-5502,626,630},{30136,-1267,655,975},{30037,6482,527,335},{30124,2321,645,875}, + {30115,3222,564,2800},{30129,1999,510,470},{30136,1550,634,320},{30155,-408,647,500},{30058,6463,657,530},{30129,3848,619,550},{30112,4823,616,950},{30070,6787,451,455}, + {29990,7648,620,305},{30128,5184,614,335},{30172,2361,507,2400},{30159,3597,536,500},{30201,-68,599,485},{30278,-5752,637,1600},{30134,5634,621,690},{30196,940,643,705}, + {30213,-1006,618,1200},{30119,6389,626,950},{30203,1666,642,305},{30156,5317,585,675},{30623,-8331,617,875},{30192,3442,611,2000},{30216,1073,631,2400},{30139,6200,539,455}, + {30180,5023,654,925},{30280,-3610,532,455},{30122,6803,628,305},{30249,2092,648,675},{30310,-4278,622,485},{30361,-6342,609,750},{30236,3684,493,2600},{30279,-884,649,2800}, + {30283,-82,323,1200},{30264,2690,552,925},{30299,-1239,634,500},{30276,2113,622,500},{30358,-5245,565,440},{30311,-1262,585,645},{30284,2188,626,2800},{30315,87,627,1200}, + {30295,2681,549,455},{30300,2648,592,1600},{30331,-101,547,425},{30302,4774,692,1600},{30382,-2703,573,500},{30342,2424,656,975},{30357,1520,495,950},{30396,-3001,630,440}, + {30313,5146,601,470},{30290,6208,575,675},{30410,-3645,639,290},{30411,-3520,653,720},{30332,4682,483,875},{30356,2869,518,825},{30476,-6323,627,500},{30343,4338,614,440}, + {30352,3661,645,1000},{30341,4674,379,470},{30363,3681,497,950},{30359,4210,671,1200},{30409,-1234,632,1200},{30382,2559,478,2400},{30335,5657,430,925},{30388,2351,515,570}, + {30337,6064,579,425},{30385,3373,566,500},{30394,2781,452,455},{30473,-4771,631,335},{30408,2160,613,485},{30441,-1251,424,570},{30451,-2181,587,500},{30482,-4758,613,675}, + {30222,7771,439,1200},{30387,4771,398,455},{30401,3890,627,975},{30420,2467,532,520},{30451,-1254,357,520},{30393,5023,631,950},{30400,4558,591,675},{30412,3706,648,1800}, + {30513,-5505,627,350},{30423,4036,524,455},{30438,2915,622,510},{30485,-1912,528,455},{30437,4272,629,440},{30445,3803,481,2600},{30512,-2920,630,900},{30522,-3567,646,455}, + {30542,-5000,627,600},{30453,4073,584,3300},{30493,-108,606,500},{30462,3700,558,950},{30466,3498,517,675},{30487,1322,621,335},{30511,-636,663,425},{30517,-1276,476,1200}, + {30500,1355,595,875},{30519,-1478,610,1000},{30525,-1478,308,630},{30471,4632,645,2600},{30509,1457,613,520},{30561,-4205,559,1000},{30509,1779,580,425},{30460,5540,576,950}, + {30495,3713,657,1000},{30438,6685,593,570},{30506,3940,623,975},{30832,-8096,577,540},{30498,4684,650,1400},{30597,-4242,564,875},{30575,-965,630,550},{30551,2445,554,1400}, + {30641,-5674,194,2600},{30513,5360,618,440},{30490,6226,572,1200},{30572,1455,617,630},{30579,534,531,520},{30551,4113,639,550},{30556,4026,220,630},{30566,3126,609,470}, + {30552,4579,558,500},{30645,-4080,609,455},{30569,4103,593,395},{30636,-2866,585,520},{30573,4298,620,500},{30616,107,615,1200},{30502,6888,555,290},{30530,6398,569,425}, + {30597,3219,443,470},{30594,3748,590,2600},{30672,-3740,625,470},{30643,-280,611,485},{30643,1006,633,425},{30642,2141,566,540},{30958,-8129,591,425},{30650,1987,641,500}, + {30683,-1821,525,1400},{30614,5355,651,1200},{30660,1732,622,500},{30719,-3560,610,1400},{30627,5960,644,950},{30718,-1574,641,500},{30703,844,625,510},{30710,-336,613,1200}, + {30721,-1781,478,720},{30678,3433,639,735},{30707,294,621,520},{30738,-2239,616,335},{30689,3844,562,925},{30660,5664,636,1200},{30676,4938,569,750},{30747,-1859,674,825}, + {30747,-1858,594,925},{30741,-289,491,470},{30774,-2911,639,875},{30734,2009,655,925},{30735,3037,401,675},{30734,3645,588,975},{30758,1090,608,975},{30777,-1506,612,550}, + {30897,-6961,611,470},{30774,2061,618,975},{30780,1126,711,1400},{30750,4593,641,470},{30822,-2494,636,1000},{30736,5607,591,1200},{30751,4895,495,2600},{30810,-985,565,580}, + {30848,-4452,511,500},{30775,3694,619,675},{30890,-6058,476,735},{30799,2580,634,875},{30740,6299,422,825},{30783,4922,544,320},{30830,1130,403,1800},{30873,-3809,644,800}, + {30784,5231,618,500},{30855,-1372,613,660},{30870,-3047,640,1400},{30847,1006,656,950},{30849,1303,538,925},{30940,-6153,488,675},{30794,5678,614,425},{30822,4319,660,2200}, + {30854,2099,648,2400},{31051,-7618,600,690},{31043,-7535,655,550},{30888,-1653,619,825},{30848,3525,461,470},{30845,4177,649,500},{30750,7253,627,440},{30939,-4729,311,500}, + {30848,4669,578,1200},{30883,1467,468,925},{30996,-6291,622,485},{30918,-255,489,425},{30903,2588,641,875},{31001,-6055,512,630},{30871,5185,611,775},{30947,-2511,636,750}, + {30933,10,622,1400},{30788,7495,520,1000},{30927,2646,559,1200},{31049,-6676,515,1200},{30939,1460,363,675},{30945,1138,543,950},{30958,-111,432,530},{30938,3157,632,1400}, + {30948,1827,625,260},{30939,3152,649,750},{30935,3833,620,500},{30982,-1495,522,1800},{30980,-241,622,1600},{30963,2120,482,1000},{30968,1332,572,455},{31008,-3343,547,485}, + {30963,2412,504,2000},{30965,2368,591,500},{30978,1009,505,580},{30985,49,516,485},{31005,-2377,637,520},{30977,1584,597,2400},{31001,-1814,510,320},{30706,8142,546,510}, + {31015,-2664,651,645},{30979,2182,608,1000},{30975,3033,568,470},{31014,-1612,580,530},{30991,1591,377,1200},{30997,1125,642,630},{30736,8109,596,500},{31035,-3160,576,350}, + {31035,-2600,628,470},{30989,4058,606,1800},{30985,4567,658,2000},{31124,-6620,342,825},{31019,1994,645,550},{31072,-3956,629,485},{30975,5600,648,720},{31015,2981,608,950}, + {31032,1458,599,440},{31001,4346,595,470},{31101,-5192,451,825},{31026,3231,551,520},{31025,3908,651,1800},{31049,1752,622,520},{31036,4528,125,950},{31007,6051,601,660}, + {31049,4172,567,1400},{31059,3546,666,2600},{31086,1507,443,825},{31055,5034,539,2600},{30726,8363,619,925},{31130,-2725,650,520},{31085,3559,647,645},{31158,-3920,550,1400}, + {31233,-6878,541,500},{31158,-3612,649,705},{31152,-2527,414,690},{31154,-2151,593,1000},{31067,6060,615,950},{31122,2527,491,470},{31081,5711,632,2600},{31137,1809,638,275}, + {31142,3072,422,510},{31212,-4399,511,735},{31109,5649,578,305},{31080,6666,558,800},{31177,-249,627,470},{31166,1612,514,645},{31166,1612,427,485},{31155,3397,246,500}, + {31192,-950,377,975},{31193,-503,442,305},{31237,-4623,489,425},{31195,601,558,1000},{31195,331,640,1000},{31134,5758,451,630},{31180,3437,492,455},{31132,6184,343,500}, + {31166,4653,630,925},{31291,-6243,628,950},{31291,-6243,659,950},{31232,-2578,586,1600},{31159,5300,633,500},{31185,3649,453,2000},{31234,-1804,621,455},{31249,-3378,490,530}, + {31184,4558,640,455},{31114,6975,641,520},{31288,-5161,505,485},{31196,4783,557,425},{31270,-3205,636,425},{31275,-3791,552,455},{31197,5241,627,530},{31241,1255,598,675}, + {31245,786,633,975},{31250,554,621,500},{31267,-1254,588,485},{31223,4611,484,2400},{31218,5191,629,1400},{31300,-3318,604,950},{31296,-2692,411,350},{31265,1805,652,975}, + {31286,-563,599,675},{31248,4666,633,2200},{31252,4406,504,900},{31304,-551,555,1200},{31370,-5845,365,485},{31342,-3981,535,470},{31287,2825,577,675},{31326,-2378,633,500}, + {31316,-898,473,925},{31335,-3072,635,485},{31365,-5073,624,2000},{31232,6404,645,3000},{31327,-1157,638,590},{31303,2710,459,530},{31300,3285,644,2400},{31353,-2793,641,290}, + {31349,-689,644,735},{31328,2965,634,470},{31370,-1792,578,500},{31331,4439,478,2000},{31065,8253,575,1000},{31333,4518,545,500},{31336,4241,666,975},{31347,3344,547,425}, + {31378,-137,655,775},{31359,2852,656,2600},{31364,2806,501,440},{31359,4070,670,1400},{31392,453,605,540},{31390,1372,517,500},{31391,1257,558,900},{31420,-2630,570,645}, + {31408,-356,657,1400},{31423,-970,551,425},{31189,8055,539,485},{31429,46,605,470},{31442,-1603,587,950},{31537,-6821,637,950},{31396,4742,567,1400},{31618,-7702,515,825}, + {31405,5108,663,2000},{31414,4492,596,4200},{31467,-1448,601,750},{31411,5073,581,750},{31411,4920,590,520},{31509,-5127,576,675},{31429,4117,394,975},{31407,5689,623,2600}, + {31461,1084,548,540},{31500,-3613,611,675},{31457,2233,531,455},{31490,-1904,625,1200},{31477,429,523,660},{31458,4447,555,500},{31463,4194,616,1600},{31496,1682,666,690}, + {31502,752,599,735},{31532,-3226,467,540},{31463,5046,561,2000},{31514,-473,621,590},{31555,-4300,664,570},{31368,7593,605,550},{31512,1933,565,305},{31544,-2688,605,825}, + {31561,-3853,594,500},{31496,4752,474,2800},{31574,-3863,530,705},{31486,5944,551,440},{31579,-2773,625,500},{31530,3603,597,550},{31720,-7621,658,485},{31530,4616,537,2800}, + {31575,-92,650,1800},{31631,-5473,516,470},{31576,153,625,675},{31576,1473,631,335},{31602,-582,731,925},{31602,-582,589,560},{31610,-1986,484,875},{31734,-7317,568,630}, + {31560,4479,619,290},{31577,3866,607,520},{31570,4585,648,2400},{31554,5667,583,1400},{31614,550,561,425},{31617,294,642,500},{31619,227,633,550},{31661,-4139,553,455}, + {31651,-3012,568,500},{31597,4163,633,705},{31586,5035,637,500},{31714,-6393,576,500},{31593,4686,632,750},{31649,-1723,407,975},{31660,-3234,518,470},{31636,596,594,630}, + {31595,5329,590,500},{31623,4393,372,440},{31678,-2501,450,350},{31784,-7254,620,485},{31660,2692,612,500},{31694,-1746,617,1000},{31663,3118,582,645},{31673,3875,521,425}, + {31673,3874,603,395},{31714,-2119,530,1000},{31689,1566,634,470},{31665,4765,455,440},{31712,699,615,425},{31739,-2056,625,735},{31834,-7013,502,335},{31740,-1137,451,520}, + {31716,3021,559,1200},{31749,294,645,675},{31770,-935,627,500},{31759,1013,469,750},{31763,1005,607,950},{31660,7143,587,705},{31806,-4027,583,675},{31763,2245,668,975}, + {31792,-1447,648,750},{31749,4550,663,2200},{31826,-3943,526,675},{31777,3630,654,2800},{31757,5356,573,1200},{31763,4769,646,1800},{31833,-3642,596,500},{31737,6330,654,1600}, + {31832,-2762,542,425},{31957,-7535,663,975},{31637,7813,591,1400},{31738,6849,733,245},{31894,-5326,575,850},{31823,3023,320,520},{31837,1598,627,825},{31881,-4051,621,470}, + {31857,-1061,677,1200},{31795,5999,564,3000},{31836,3663,605,750},{31865,9,638,335},{31878,-1734,604,550},{31862,1001,449,675},{31895,-3621,612,455},{31950,-6468,631,1400}, + {31854,2990,617,540},{31891,-2065,524,510},{31894,-1517,528,305},{32302,-8481,645,455},{31870,3805,372,720},{31896,525,392,600},{31916,-161,648,500},{31843,6440,639,580}, + {31931,-1328,640,950},{31948,-3217,471,975},{31890,4797,646,2200},{31949,-1799,543,1400},{31912,4225,643,470},{31957,-1746,705,720},{31997,-5345,439,875},{31955,-452,582,2000}, + {31973,-2877,640,520},{31935,3939,423,1200},{31935,4268,619,1600},{32004,-4502,600,400},{31948,3490,443,2600},{31926,5400,613,975},{31994,-2635,656,550},{31972,1120,596,425}, + {31931,5580,598,455},{32019,-4081,482,875},{32032,-4994,638,455},{31933,5861,642,2600},{31961,4395,500,3900},{31973,4104,615,875},{32107,-6973,641,400},{31984,3824,583,735}, + {32006,2203,629,1800},{32133,-7180,609,470},{32027,-456,587,530},{31987,4951,576,1800},{31965,6259,244,825},{32027,735,582,320},{31982,5862,566,335},{32027,2386,557,485}, + {32021,3245,568,520},{32056,-1683,428,530},{31893,7701,595,425},{32034,3261,604,975},{32026,4035,640,630},{31984,6487,518,2400},{32075,-2267,560,335},{32052,1980,408,485}, + {31834,8123,615,925},{32073,-932,599,350},{32348,-8268,638,2400},{32109,-4662,631,825},{32072,681,516,925},{32014,6076,611,550},{32110,-4101,577,1000},{32161,-6537,422,660}, + {32067,3031,605,485},{32104,-2085,541,500},{32103,-2275,638,500},{32050,4939,569,500},{32070,3863,663,975},{32105,-1288,549,735},{32156,-5466,612,735},{32085,3741,658,660}, + {32099,2427,571,735},{32110,1017,635,675},{32122,-340,636,750},{32095,3735,647,425},{32103,2531,615,950},{32130,-975,570,750},{32219,-6951,534,275},{32110,2453,632,500}, + {32114,2617,568,800},{32132,-356,549,440},{32160,-3783,563,470},{32334,-8004,647,1000},{32176,-4255,551,875},{32146,53,646,975},{32167,-2241,374,560},{32162,110,613,675}, + {32123,4932,658,1200},{32181,-2120,578,425},{32133,4671,560,750},{32145,3667,594,3000},{32161,1938,607,800},{32225,-5371,639,470},{32206,-1157,661,705},{32218,-2181,451,550}, + {32227,-3124,650,1000},{32184,3712,531,2400},{32171,4884,531,850},{32192,2761,541,975},{32169,5290,603,1800},{32210,820,640,335},{32203,3223,580,750},{32225,1791,644,425}, + {32250,-1915,657,750},{32225,2218,593,290},{32186,5975,610,305},{32249,2364,457,335},{32222,5542,612,1400},{32194,6681,544,1600},{32236,4654,524,500},{32302,-4118,529,500}, + {32306,-3394,597,950},{32279,1214,608,1200},{32289,-557,291,600},{32332,-5274,641,440},{32473,-7944,618,690},{32314,-2459,643,825},{32335,-4485,557,485},{32258,5296,602,950}, + {32216,7056,323,2800},{32121,8052,597,540},{32311,2339,670,1200},{32357,-4292,632,485},{32286,5262,616,580},{32275,6046,553,2800},{32372,-2970,641,1400},{32371,-2008,569,735}, + {32324,4998,575,1000},{32382,-2345,640,530},{32332,4585,625,485},{32451,-6482,620,1000},{32364,2275,647,645},{32382,-398,577,510},{32350,4559,402,520},{32405,-2617,573,825}, + {32537,-7739,376,500},{32369,3853,490,500},{32420,-3305,611,825},{32383,2820,631,750},{32386,2445,611,900},{32361,5170,615,1200},{32427,-1947,468,2600},{32406,3006,636,520}, + {32401,4537,553,275},{32439,-39,625,950},{32444,-785,469,825},{32443,662,618,950},{32424,4041,501,875},{32444,1932,545,735},{32478,-3368,628,520},{32500,-5236,621,645}, + {32463,577,567,775},{32514,-5574,633,500},{32437,4470,620,825},{32487,-1058,608,500},{32469,2550,616,510},{32441,5404,615,485},{32475,2027,585,720},{32489,224,510,500}, + {32502,-1666,368,750},{32448,6208,473,2600},{32773,-8272,529,520},{32474,5749,562,4200},{32544,-2510,649,500},{32539,-1405,518,590},{32505,4327,511,335},{32550,-2326,524,510}, + {32637,-7101,601,1400},{32554,129,567,470},{32566,-1887,473,520},{32556,568,530,320},{32518,5487,620,500},{32539,4081,611,950},{32564,1082,609,2000},{32581,-1962,622,925}, + {34137,-8882,657,750},{32577,-1440,588,750},{32550,3551,607,360},{32553,4577,617,290},{32612,-3855,630,550},{32560,4108,569,1000},{32552,5119,467,2400},{32600,-1475,599,750}, + {32633,-4950,645,485},{32566,4960,609,520},{32624,-3303,434,1000},{32578,4116,549,320},{32586,3828,565,1000},{32605,988,239,470},{32604,2874,473,660},{32603,2874,608,580}, + {32625,-908,509,520},{32569,5927,608,485},{32613,1735,434,550},{32613,1477,594,600},{32616,2564,413,675},{32588,5878,408,320},{32548,7131,456,485},{32657,-928,600,305}, + {32663,-1137,558,975},{32644,3586,640,520},{32652,2295,529,500},{32676,-1613,287,875},{32707,-4730,558,600},{32577,7232,517,500},{32660,2556,628,455},{32693,-3090,501,950}, + {32622,6246,595,3300},{32681,269,564,975},{32657,4306,654,600},{32677,1719,621,705},{32750,-6471,562,500},{32691,-592,617,825},{32770,-6963,553,470},{32636,6112,429,950}, + {32670,4931,423,2400},{32703,3658,647,425},{32742,-1272,631,925},{32712,3865,612,1400},{32686,6069,552,335},{32755,-1684,638,500},{32736,2046,629,2600},{32675,7015,629,1000}, + {32746,3017,504,975},{32754,1729,529,720},{32742,4115,648,950},{32792,-1862,616,750},{32733,6127,617,335},{32789,1983,577,1400},{32777,3954,617,1200},{32808,2127,689,290}, + {32832,-1355,508,735},{32880,-6189,590,735},{32848,-3736,301,1400},{32812,2879,553,660},{32840,-1031,659,1200},{32827,2592,508,2400},{32800,5580,571,1800},{32841,1967,568,1000}, + {32849,686,615,600},{32854,-428,571,470},{32791,6575,637,975},{32906,-5790,619,875},{32865,-330,620,675},{32857,1972,639,500},{32898,-3061,641,520},{32910,-3725,546,950}, + {32926,-3775,618,735},{32948,-5499,440,750},{32963,-5901,612,705},{33047,-7766,641,875},{32872,5661,580,1400},{32910,2124,640,425},{32924,1208,554,950},{32884,6154,613,520}, + {32888,6532,586,2600},{32956,-542,633,675},{32926,4867,642,1400},{32968,-2118,612,290},{32982,-3840,550,500},{33077,-7612,595,705},{33010,-5588,601,1200},{32973,-437,622,470}, + {32916,6363,491,320},{32930,6616,643,2600},{33003,672,600,2400},{33021,-2845,542,1400},{33084,-5679,469,440},{32972,6270,593,305},{32985,5766,659,1000},{33027,60,558,440}, + {33011,3301,646,675},{33027,1312,560,690},{33029,826,565,425},{33055,-1790,628,530},{32946,7500,635,425},{33061,-1696,637,950},{33051,1097,637,2000},{33082,-2990,710,750}, + {33016,6249,666,3000},{32981,7318,503,675},{33082,-652,554,500},{33083,-216,469,1600},{33046,5288,578,2000},{33083,1139,580,1200},{33110,-2992,647,425},{33052,5800,556,3300}, + {33074,4465,560,1000},{33115,-2682,596,2200},{33146,-5964,562,440},{33120,-91,530,875},{33153,-3954,446,455},{33114,3294,638,550},{33142,506,484,440},{33145,-32,296,580}, + {33130,2667,578,470},{33097,6312,529,275},{33095,6463,429,925},{33161,-1387,427,1200},{33139,2896,570,1200},{33268,-7588,655,470},{33132,4674,613,290},{33146,4511,644,1000}, + {32955,8287,698,660},{33151,4501,514,425},{33206,-4696,174,1600},{33125,6279,527,440},{33146,4823,627,2600},{33129,6228,511,3300},{33155,4525,619,925},{33175,2534,376,675}, + {33210,-3299,450,950},{33298,-7612,615,500},{33211,-3404,499,335},{33156,5634,639,1000},{33187,1948,575,720},{33188,1800,635,335},{33218,-3313,637,950},{33196,2170,578,1600}, + {33225,-1852,581,2600},{33249,-4811,643,455},{33207,2554,611,735},{33179,5884,632,540},{33186,5331,614,925},{33248,-3401,537,875},{33207,4980,642,425},{33250,-2829,644,875}, + {33254,-3255,492,660},{33217,4574,611,580},{33231,3317,558,520},{33255,620,353,950},{33259,-389,627,975},{33266,-1157,546,1600},{33264,-427,601,500},{33250,3318,429,675}, + {33258,1962,618,600},{33259,1463,633,500},{33276,-2123,609,660},{33266,1162,578,335},{33263,2098,646,925},{33272,3055,632,800},{33296,1604,595,485},{33311,-1419,603,720}, + {33279,5082,540,875},{33324,-2633,617,925},{33271,5820,335,485},{33303,2495,592,500},{33318,-472,639,645},{33245,7234,479,530},{33288,5941,504,4200},{33344,-2518,558,660}, + {33446,-7751,551,705},{33295,5684,524,630},{33256,7211,637,1200},{33266,7013,550,720},{33320,3460,533,470},{33299,5908,630,500},{33366,-4138,623,550},{33358,-2777,543,1400}, + {33301,6076,535,485},{33358,-2107,532,500},{33501,-8044,510,275},{33341,2861,589,470},{33309,6329,579,305},{33390,-4445,610,520},{33347,3972,449,455},{33390,-4135,479,520}, + {33346,4544,553,1000},{33411,-4163,510,520},{33368,4295,571,950},{33346,6316,611,1400},{33400,855,621,975},{33416,-2590,615,485},{33322,7331,608,500},{33376,5704,419,750}, + {33414,-160,615,875},{33420,-1283,534,500},{33425,-2314,617,550},{33399,3775,413,455},{33421,-778,416,520},{33422,-904,579,455},{33457,-5363,537,570},{33463,-6026,286,455}, + {33412,2780,637,510},{33428,-539,575,540},{33791,-8597,577,500},{33411,5722,588,520},{33452,-24,639,705},{33475,-1331,595,530},{33515,-5751,634,455},{33473,3777,617,775}, + {33455,6280,575,455},{33505,-782,537,1400},{33511,579,537,2000},{33523,819,617,660},{33615,-7226,529,925},{33540,-2160,513,470},{33525,2694,647,290},{33541,-139,384,1000}, + {33516,5098,642,470},{33538,1221,501,2600},{33568,-4595,662,275},{33533,2833,481,1200},{33526,4654,457,1800},{33578,-4593,562,705},{33458,7649,666,975},{33646,-7502,604,570}, + {33629,-7043,578,705},{33546,4208,641,2000},{33588,-2476,553,510},{33588,-719,593,540},{33624,-5780,532,570},{33592,2085,604,645},{33603,-484,578,1000},{33611,-1353,576,540}, + {33575,5728,616,1400},{33598,3857,622,675},{33575,6242,604,975},{33589,5223,443,520},{33632,138,466,2800},{33683,-6497,448,1200},{33613,4948,457,1200},{33654,-2368,629,1000}, + {33642,1844,626,500},{33664,-1674,657,580},{33664,-1674,635,590},{33329,8611,527,1200},{33716,-6749,555,900},{33666,439,575,645},{33669,3744,646,2600},{33592,7824,676,1200}, + {33696,470,479,500},{33716,-3913,547,510},{33686,3981,614,2600},{33675,5643,657,2000},{33694,3184,598,470},{33732,-4350,397,540},{33650,7077,547,470},{33721,-2,459,660}, + {33721,-2,442,705},{33744,-4375,411,290},{33677,6513,546,3000},{33728,913,558,425},{33744,-2711,595,735},{33729,2676,579,470},{33751,-1292,640,705},{33749,443,548,720}, + {33757,-1459,637,1400},{33708,6409,629,500},{33743,3573,656,950},{33772,-2607,643,485},{33729,5842,375,675},{33738,4771,436,350},{33766,-1068,482,1000},{33751,3257,565,1200}, + {33753,4936,640,470},{33788,-3235,429,1000},{33886,-7877,615,485},{33668,7879,583,950},{33762,4312,451,2600},{33783,-291,616,500},{33783,-656,614,645},{33825,-6198,481,290}, + {33792,-1091,638,800},{33789,2954,635,875},{33782,5028,377,975},{33815,2023,642,735},{33820,1586,632,500},{33811,3978,588,850},{33808,5404,635,520},{33851,-157,589,540}, + {33747,7882,550,925},{33867,-2071,520,690},{33897,-5788,623,875},{33842,5662,571,520},{33826,6991,660,775},{33890,-2399,597,500},{33884,-12,402,1200},{33832,7037,634,1000}, + {33807,7623,568,950},{33912,-4058,628,925},{33915,-3166,582,485},{33924,-4059,586,975},{33897,3963,573,2600},{33903,3558,610,500},{33915,1170,640,975},{33897,5007,629,2400}, + {33897,5607,638,925},{33927,1258,630,425},{33920,3565,630,425},{33944,-423,503,470},{33978,-5269,665,735},{33959,-790,623,600},{33934,5155,463,800},{33969,-2875,647,485}, + {33894,7364,508,705},{33971,-3308,566,950},{33957,4518,640,580},{33972,1952,582,520},{33993,-2833,631,500},{34020,-5742,597,440},{33966,5680,521,290},{33982,3905,488,3300}, + {34009,-3066,587,455},{33995,1968,621,950},{33930,7537,579,335},{33989,3759,603,570},{33966,6358,519,925},{34016,-2704,417,1400},{34020,-355,631,660},{34151,-8138,415,775}, + {34022,1455,571,560},{34013,4428,446,470},{34008,5385,593,500},{34037,1083,340,1400},{34065,-4721,598,600},{34067,-4688,210,275},{34059,-2936,617,275},{34038,3097,634,425}, + {34068,-4425,607,500},{34037,4023,525,2600},{34044,2931,479,975},{34049,1452,590,500},{34040,4155,594,510},{34087,-4141,485,500},{34076,-831,645,875},{34107,-6050,630,705}, + {34081,-696,641,1000},{34059,5391,612,470},{34090,-1883,469,440},{34075,3022,294,580},{34073,3780,643,2800},{34077,4717,639,975},{34093,1094,651,520},{34102,3947,595,425}, + {34141,-5350,485,470},{34102,4182,508,500},{34142,-4655,551,470},{34153,-4898,662,615},{34162,-4969,648,485},{34137,1937,625,540},{34120,5252,655,470},{34168,-4694,656,775}, + {34260,-8012,535,1800},{34242,-7705,673,900},{34167,1217,419,660},{34154,4455,576,750},{34163,2357,395,520},{34183,-3416,628,485},{34209,-6168,637,500},{34189,-1961,526,530}, + {34195,-3822,671,690},{34232,-7035,634,950},{34193,-1406,566,1000},{34198,-2591,630,550},{34214,-5132,349,925},{34213,-1056,619,775},{34185,5848,636,1200},{34205,3742,590,520}, + {34240,-1359,401,350},{34250,-3281,633,750},{34238,1048,654,750},{34220,5442,612,2000},{34218,6294,606,500},{34250,2460,348,520},{34276,-3916,542,350},{34294,-5988,646,485}, + {34225,6857,619,675},{34244,5590,543,485},{34304,-6319,612,500},{34259,4195,592,2400},{34266,1914,640,500},{34254,5068,621,560},{34284,-2954,597,520},{34242,6620,352,500}, + {34313,-3288,446,1000},{34284,6170,560,520},{34310,984,516,645},{34315,-758,374,320},{34301,4331,494,350},{34341,-4860,604,600},{34366,-7007,605,580},{34187,8315,474,455}, + {34326,1684,564,485},{34337,-1162,580,1200},{34322,5041,646,1400},{34330,4017,634,1600},{34327,6010,601,470},{34342,4475,581,925},{34366,-1582,327,925},{34364,-720,619,455}, + {34369,-1627,556,470},{34353,4038,581,500},{34381,-3639,640,470},{34365,1694,612,290},{34375,106,611,825},{34380,-499,572,500},{34381,882,490,975},{34376,3708,591,675}, + {34396,-3163,610,455},{34399,-3254,421,520},{34410,-3157,648,440},{34420,-4797,570,875},{34394,3635,574,1600},{34421,1185,651,925},{34410,4160,559,2600},{34411,4973,495,425}, + {34432,-481,631,510},{34441,-2962,116,925},{34437,2077,549,580},{34439,381,628,485},{34427,4868,543,2200},{34465,-3552,613,630},{34442,3931,618,2600},{34456,-240,616,520}, + {34460,-141,637,750},{34276,8537,590,425},{34465,936,643,580},{34468,734,633,975},{34480,1173,575,800},{34490,-2946,551,875},{34490,-1307,607,425},{34486,96,543,485}, + {34502,-2516,565,500},{34479,5265,629,470},{34479,5981,643,1200},{34510,-2563,629,485},{34522,-5275,412,520},{34360,8435,471,440},{34528,-5095,568,455},{34516,19,621,560}, + {34518,301,583,440},{34502,5695,500,560},{34518,3108,660,1200},{34533,-2885,555,455},{34514,4538,650,925},{34535,-2279,628,875},{34535,-706,621,425},{34523,3871,654,2400}, + {34538,-471,594,500},{34564,-3642,647,485},{34538,5711,620,485},{34548,4233,362,1800},{34564,-657,615,320},{34568,-2087,597,500},{34564,3178,657,720},{34565,4276,510,925}, + {34587,-3475,511,750},{34569,4406,639,2600},{34622,-6882,552,690},{34568,5524,650,1200},{34600,-4148,579,500},{34599,-480,668,630},{34597,382,453,1800},{34616,-5396,537,425}, + {34594,2808,242,320},{34600,662,641,720},{34585,6045,674,2600},{34584,5856,643,520},{34589,6721,524,455},{34605,5005,465,500},{34619,1521,249,1200},{34629,-769,543,720}, + {34630,-1708,614,500},{34628,1656,644,520},{34632,131,639,510},{34710,-7948,612,925},{34672,-4352,428,675},{34658,1852,613,660},{34667,-2374,447,510},{34672,-3889,561,975}, + {34679,-4961,633,440},{34663,1991,630,645},{34681,-5069,583,660},{34715,-7359,615,455},{34675,941,452,335},{34678,2547,476,520},{34665,5942,485,3000},{34687,3283,602,900}, + {34687,2113,599,875},{34683,4607,666,2600},{34679,5282,611,500},{34709,-2882,560,510},{34679,5973,640,2600},{34691,4639,533,425},{34694,4930,570,675},{34705,4456,656,950}, + {34717,213,540,520},{34695,6363,626,2400},{34755,-6686,647,500},{34805,-8091,641,440},{34699,6422,621,500},{34736,-2117,366,485},{34744,-2809,587,470},{34749,-4286,581,690}, + {34738,868,512,290},{34746,-1451,642,1000},{34748,-2246,469,580},{34754,-4059,583,290},{34697,7539,441,580},{34759,-4525,390,485},{34751,982,539,1200},{34743,5933,570,875}, + {34769,-2952,651,735},{34768,1759,571,440},{34761,4354,594,675},{34793,872,516,875},{34796,2685,617,520},{34831,-4962,680,520},{34853,-6270,612,520},{34814,4941,452,750}, + {34827,2944,635,520},{34832,5717,556,455},{34836,1107,582,520},{34858,-605,422,335},{34874,-4111,577,470},{34867,-1069,612,425},{34856,5062,631,1000},{34859,2977,641,660}, + {34865,2410,636,470},{34889,-350,555,925},{34897,-909,421,500},{34894,2825,649,550},{34924,-6200,566,645},{34866,7423,584,1000},{34899,2477,660,675},{34917,-4449,592,485}, + {34921,-4119,647,500},{34936,-5824,399,735},{34978,-7947,633,500},{34921,-773,506,305},{34891,7089,556,750},{34929,328,369,510},{34918,5321,554,645},{34911,6196,653,3000}, + {34958,-6747,613,470},{34942,-1171,634,975},{34932,4516,643,500},{34948,-918,439,2000},{34954,-4082,553,675},{34944,4902,485,320},{34948,4549,648,1200},{35202,-8748,549,470}, + {34971,-3253,441,485},{34960,4177,602,825},{34974,-961,498,1000},{34978,-1346,508,550},{34933,7530,638,950},{34985,-512,555,705},{34985,-1808,593,455},{34976,4514,650,875}, + {34993,-3371,637,470},{34966,6811,475,500},{34986,3479,632,1400},{34987,4863,544,500},{34992,4838,632,485},{34997,4208,579,350},{35021,-5031,605,675},{35009,538,505,470}, + {35017,-591,617,500},{35016,2374,460,875},{35006,6197,645,485},{35021,3042,559,350},{35031,-2699,564,560},{35018,4412,613,875},{35022,3818,577,675},{35014,6221,639,470}, + {35048,3181,532,1800},{35049,2661,662,675},{35074,-6006,609,305},{35066,-1504,520,925},{35067,2083,629,1200},{35074,-2010,397,500},{35077,1231,508,455},{35064,6013,556,455}, + {35098,-5381,615,900},{35094,-4312,610,455},{35088,29,631,470},{35105,-5189,575,350},{35095,3253,669,690},{35103,-1869,619,520},{35133,-5685,559,500},{35115,4111,672,1400}, + {35121,3238,557,1200},{35121,6228,498,335},{35134,2340,440,630},{35151,-2064,439,425},{35165,-5272,552,690},{35178,-6658,645,440},{35181,-5848,563,520},{35179,-5016,620,1200}, + {35173,126,494,1000},{35181,112,625,530},{35178,4291,575,1200},{35200,-3554,632,470},{35192,2517,598,1000},{35199,638,428,485},{35202,-1145,637,600},{35182,7036,560,850}, + {35225,-6311,568,875},{35225,-4450,643,470},{35225,-927,618,500},{35227,2305,635,510},{35229,1276,455,530},{35238,-453,625,455},{35253,4913,617,455},{35251,5855,491,2400}, + {35267,3866,605,510},{35275,-629,639,500},{35286,-4484,602,485},{35282,3924,522,500},{35288,-409,649,630},{35293,-2137,629,775},{35293,2840,641,975},{35299,-109,638,485}, + {35333,-7739,581,580},{35310,3495,665,1400},{35324,-3782,437,1200},{35175,8731,558,825},{35332,-2091,471,975},{35337,2250,532,275},{35343,4506,624,520},{35348,2084,606,305}, + {35349,3133,498,440},{35354,-125,587,950},{35380,-6469,740,720},{35371,-1525,596,500},{35366,4024,559,975},{35366,3350,563,500},{35377,-4262,471,950},{35369,3802,618,395}, + {35388,-746,639,550},{35375,7164,584,510},{35398,2456,645,335},{35410,210,568,660},{35413,3290,635,660},{35427,-3187,652,485},{35460,-7687,600,500},{35442,-1306,565,510}, + {35446,-4549,474,950},{35442,1683,626,975},{35439,4646,382,520},{35438,4443,580,1400},{35449,1840,553,975},{35449,4620,658,550},{35453,4327,429,1400},{35487,-4664,609,800}, + {35478,5047,530,1200},{35495,-1422,500,750},{35499,563,413,645},{35498,968,597,950},{35479,7529,595,925},{35484,7400,598,520},{35501,3765,653,720},{35484,7763,321,485}, + {35516,-3207,531,485},{35510,4433,414,1200},{35517,3672,623,735},{35528,-2416,660,335},{35529,-1168,589,510},{35539,-1803,534,425},{35536,4951,626,900},{35544,-1782,482,600}, + {35549,725,589,925},{35551,178,450,825},{35548,5726,624,520},{35556,4499,657,425},{35562,-1545,528,440},{35568,-1454,449,1200},{35559,6452,656,320},{35563,6168,640,470}, + {35584,1033,506,320},{35596,-1528,636,245},{35601,-4508,609,485},{35611,-7049,607,520},{35617,-7879,575,485},{35605,-6440,572,470},{35600,2936,493,500},{35605,-1828,524,1200}, + {35612,-2625,617,690},{35620,5580,651,560},{35650,-4018,631,825},{35650,-1868,529,720},{35651,4642,495,550},{35660,349,504,400},{35665,6678,595,2600},{35682,-5023,518,2400}, + {35685,-6839,689,720},{35676,5865,487,485},{35682,-1191,573,485},{35676,5745,551,455},{35689,4683,607,2400},{35699,-276,549,520},{35698,6781,504,975},{35714,-638,607,440}, + {35721,221,646,675},{35723,-2813,457,1000},{35716,6488,641,1000},{35721,6221,543,925},{35722,5998,634,1000},{35730,5896,633,720},{35738,-1586,624,470},{35736,108,577,875}, + {35744,-6284,659,425},{35742,3643,590,590},{35741,2884,597,950},{35746,-2533,642,925},{35756,-997,594,500},{35759,5162,644,705},{35764,-1440,572,440},{35784,931,579,305}, + {35784,-1891,518,1200},{35803,-8202,511,530},{35799,293,555,440},{35799,7760,655,675},{35810,2167,611,320},{35812,1912,508,320},{35812,-1425,587,455},{35810,7554,639,455}, + {35815,1095,530,900},{35821,-900,575,500},{35823,-316,593,510},{35827,209,628,975},{35834,-2423,624,925},{35859,-2704,635,950},{35860,5750,454,580},{35866,-4030,603,630}, + {35869,11,561,275},{35878,707,621,1200},{35882,-3192,610,1800},{35882,-3188,683,1400},{35885,2596,654,425},{35889,5741,600,3000},{35889,4736,600,520},{35912,-2474,631,550}, + {35917,2265,615,320},{35912,8319,659,925},{35926,4266,597,630},{35928,-2662,626,455},{35929,5571,555,735},{35933,-6296,597,975},{35939,-8217,573,500},{35940,-6430,500,975}, + {35939,6002,647,1200},{35944,2514,466,305},{35959,-1585,626,500},{35960,5139,480,245},{35967,-356,486,510},{35970,3238,652,1800},{35973,-5275,513,500},{35969,4641,654,2200}, + {35975,5576,488,2800},{35983,686,401,690},{35987,-2948,562,425},{35987,3372,658,600},{35987,3372,658,600},{35998,-6558,450,1200},{8,-4429,629,570},{10,2692,646,645}, + {13,5956,619,510},{18,4525,638,1200},{27,-4881,571,520},{33,-5034,553,320},{33,4998,622,530},{40,-7707,478,455},{40,6122,555,750},{43,4237,625,1200},{46,-303,510,1600}, + {54,2708,575,550},{49,-601,441,305},{53,-1468,710,245},{58,-2972,501,2200},{60,896,632,850},{62,849,563,750},{65,6610,586,520},{74,-2005,625,660},{78,-2415,644,550}, + {86,6364,624,2400},{94,-1734,455,1200},{97,6671,629,290},{106,6229,588,975},{108,-1653,578,470},{108,-2927,640,1000},{113,-1051,494,455},{118,6717,567,485},{115,4209,601,1200}, + {113,-7290,731,720},{122,3466,612,580},{117,-7144,559,1400},{123,2665,625,440},{128,6131,580,1400}, +}; + +} // namespace + +namespace star_catalogue +{ + +catalogue_star const *stars() +{ + return STARS; +} + +std::size_t size() +{ + return std::size(STARS); +} + +} // namespace star_catalogue diff --git a/environment/starcatalog.h b/environment/starcatalog.h new file mode 100644 index 00000000..97761fd4 --- /dev/null +++ b/environment/starcatalog.h @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include +#include + +// the stars of the night sky: the Yale Bright Star Catalogue, 5th revised edition (Hoffleit & Warren, 1991), every star with a +// position and a visual magnitude, about down to magnitude 6.5. positions for the equinox J2000 +struct catalogue_star +{ + std::uint16_t right_ascension; // hundredths of a degree, 0-36000 + std::int16_t declination; // hundredths of a degree, -9000-9000 + std::int16_t magnitude; // visual magnitude, hundredths + std::int16_t temperature; // colour temperature, tens of kelvins +}; + +namespace star_catalogue +{ + +catalogue_star const *stars(); +std::size_t size(); + +} // namespace star_catalogue diff --git a/environment/stars.cpp b/environment/stars.cpp index 5fd112c5..405cf6b6 100644 --- a/environment/stars.cpp +++ b/environment/stars.cpp @@ -9,5 +9,7 @@ void cStars::init() { - m_stars = TModelsManager::GetModel( "skydome_stars.t3d", false ); + // the stars are drawn from the catalogue built into the renderer (environment/starcatalog.h), the model of the starry + // dome is no longer loaded + m_stars = nullptr; } diff --git a/environment/sun.h b/environment/sun.h index 7fdea413..e7e083fb 100644 --- a/environment/sun.h +++ b/environment/sun.h @@ -25,6 +25,9 @@ public: float getAngle() const; // return current hour angle double getHourAngle() const; + // local mean sidereal time, degrees, and the latitude of the observer, degrees north: what turns the stars + double getSiderealTime() const { return m_observer.lmst; } + double getLatitude() const { return m_observer.latitude; } // returns current intensity of the sun float getIntensity(); // sets current time, overriding one acquired from the system clock diff --git a/environment/weather.cpp b/environment/weather.cpp new file mode 100644 index 00000000..df83e220 --- /dev/null +++ b/environment/weather.cpp @@ -0,0 +1,968 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "environment/weather.h" + +#include "utilities/Globals.h" +#include "utilities/Logs.h" +#include "utilities/parser.h" +#include "simulation/simulationenvironment.h" + +namespace +{ + +// name, abbreviation, base, thickness, extinction, cell, spread, bottom, top, top variation, anvil, erosion, stretch, opacity, flat. +// the extinction of the volumetric genera is about what makes the visibility inside a cloud a few tens of metres (cumulus) to +// a few hundred (altostratus); of the flat ones it is the optical depth of the sheet +cloud_genus const GENERA[static_cast(cloud_type::count)]{ + {"cumulus", "Cu", 1100.f, 1200.f, 0.040f, 1400.f, 5000.f, 0.06f, 0.45f, 0.65f, 0.0f, 0.35f, 1.0f, 0.80f, false}, + {"towering_cumulus", "TCu", 900.f, 4000.f, 0.050f, 2500.f, 9000.f, 0.05f, 0.35f, 0.60f, 0.0f, 0.30f, 1.0f, 0.90f, false}, + {"cumulonimbus", "Cb", 800.f, 9000.f, 0.060f, 6000.f, 25000.f, 0.04f, 0.15f, 0.30f, 1.0f, 0.20f, 1.0f, 1.00f, false}, + {"stratocumulus", "Sc", 900.f, 600.f, 0.030f, 900.f, 3500.f, 0.15f, 0.50f, 0.30f, 0.0f, 0.30f, 1.0f, 0.85f, false}, + {"stratus", "St", 300.f, 450.f, 0.020f, 3000.f, 9000.f, 0.25f, 0.30f, 0.10f, 0.0f, 0.10f, 1.5f, 1.00f, false}, + {"nimbostratus", "Ns", 500.f, 3000.f, 0.030f, 4000.f, 15000.f, 0.10f, 0.30f, 0.15f, 0.0f, 0.10f, 1.0f, 1.00f, false}, + {"altocumulus", "Ac", 3500.f, 400.f, 0.020f, 450.f, 3000.f, 0.20f, 0.50f, 0.30f, 0.0f, 0.45f, 1.0f, 0.50f, false}, + {"altostratus", "As", 4000.f, 1500.f, 0.006f, 5000.f, 15000.f, 0.20f, 0.40f, 0.10f, 0.0f, 0.10f, 1.5f, 0.60f, false}, + {"cirrus", "Ci", 8000.f, 800.f, 0.50f, 3000.f, 12000.f, 0.0f, 0.0f, 0.0f, 0.0f, 0.50f, 5.0f, 0.15f, true}, + {"cirrostratus", "Cs", 8500.f, 700.f, 0.30f, 8000.f, 20000.f, 0.0f, 0.0f, 0.0f, 0.0f, 0.20f, 2.0f, 0.25f, true}, + {"cirrocumulus", "Cc", 7500.f, 300.f, 0.25f, 300.f, 3000.f, 0.0f, 0.0f, 0.0f, 0.0f, 0.60f, 1.0f, 0.20f, true}, +}; + +// the old fog (exp2 of the squared distance over the fog range) thinned out a dark object to 5% at 1.7 ranges and to a half at +// 0.83. the visibility is put where an exponential fog crosses the old one around a third +float const LEGACY_VISIBILITY_SCALE{2.5f}; +float const FEET{0.3048f}; +float const KNOTS{0.514444f}; + +std::string upper(std::string_view Text) +{ + std::string result{Text}; + for (auto &c : result) + c = static_cast(std::toupper(static_cast(c))); + return result; +} + +std::string lower(std::string_view Text) +{ + std::string result{Text}; + for (auto &c : result) + c = static_cast(std::tolower(static_cast(c))); + return result; +} + +bool all_digits(std::string_view Text) +{ + return !Text.empty() && std::all_of(Text.begin(), Text.end(), [](char c) { return std::isdigit(static_cast(c)) != 0; }); +} + +// a number in the C locale, the whole token +std::optional number(std::string_view Text) +{ + if (Text.empty()) + return std::nullopt; + std::istringstream stream{std::string{Text}}; + stream.imbue(std::locale::classic()); + float value; + stream >> value; + if (stream.fail()) + return std::nullopt; + char rest; + if (stream >> rest) + return std::nullopt; + return value; +} + +std::vector split(std::string_view Text) +{ + std::vector tokens; + std::string token; + for (auto const c : Text) + { + // commas and semicolons separate as the spaces do: the text of a memory cell set by an event can't hold spaces + if (std::isspace(static_cast(c)) || c == ';' || c == '=' || c == ',') + { + if (!token.empty()) + tokens.push_back(token); + token.clear(); + } + else + token += c; + } + if (!token.empty()) + tokens.push_back(token); + return tokens; +} + +std::string strip_quotes(std::string Text) +{ + if (!Text.empty() && Text.front() == '\"') + Text.erase(0, 1); + if (!Text.empty() && Text.back() == '\"') + Text.pop_back(); + return Text; +} + +// the genus of a layer of a report, which names only the convective ones: by the height, the amount and the precipitation +cloud_type report_genus(float const Base, float const Coverage, bool const Precipitation, bool const Showers) +{ + if (Base < 2000.f) + { + if (Coverage >= 0.99f) + return Precipitation ? cloud_type::nimbostratus : (Base < 600.f ? cloud_type::stratus : cloud_type::stratocumulus); + if (Coverage > 0.5f) + return (Showers ? cloud_type::cumulus : (Base < 400.f ? cloud_type::stratus : cloud_type::stratocumulus)); + return cloud_type::cumulus; + } + if (Base < 6000.f) + return (Coverage > 0.5f ? cloud_type::altostratus : cloud_type::altocumulus); + return (Coverage > 0.5f ? cloud_type::cirrostratus : cloud_type::cirrus); +} + +float coverage_from_amount(std::string_view Amount) +{ + if (Amount == "FEW") + return 1.5f / 8.f; + if (Amount == "SCT") + return 3.5f / 8.f; + if (Amount == "BKN") + return 6.f / 8.f; + if (Amount == "OVC") + return 1.f; + return -1.f; +} + +// one layer of a typical depth of its genus, below the next height it may not reach +cloud_layer typical_layer(cloud_type const Type, float const Coverage, float const Base) +{ + auto const &genus{cloud_genus_info(Type)}; + cloud_layer layer; + layer.type = Type; + layer.coverage = Coverage; + layer.base = Base; + layer.top = Base + genus.thickness; + return layer; +} + +// reads the body of a weather section (or the text the scripting interface gives) into Conditions +void parse_section(std::vector const &Tokens, weather_conditions &Conditions, std::optional *Elevation, float *Transition) +{ + std::size_t i{0}; + auto const next_number = [&](float &Value) { + if (i < Tokens.size()) + { + if (auto const value = number(Tokens[i])) + { + Value = *value; + ++i; + return true; + } + } + return false; + }; + while (i < Tokens.size()) + { + auto const keyword{lower(Tokens[i++])}; + if (keyword == "metar") + { + // the report is a single quoted token, or the rest of the tokens up to the next keyword of the section + if (i < Tokens.size()) + parse_metar(strip_quotes(Tokens[i++]), Conditions); + } + else if (keyword == "visibility") + next_number(Conditions.visibility); + else if (keyword == "haze") + next_number(Conditions.haze_height); + else if (keyword == "groundfog") + { + next_number(Conditions.groundfog_thickness); + next_number(Conditions.groundfog_visibility); + } + else if (keyword == "fognoise") + next_number(Conditions.fog_noise); + else if (keyword == "noclouds" || keyword == "clear") + Conditions.clouds.clear(); + else if (keyword == "clouds") + { + if (i >= Tokens.size()) + break; + auto const type{cloud_type_from_name(Tokens[i++])}; + if (!type) + { + WriteLog("Weather: unknown cloud genus \"" + Tokens[i - 1] + "\"", logtype::file); + continue; + } + auto const &genus{cloud_genus_info(*type)}; + float coverage{0.f}; + if (i < Tokens.size()) + { + auto const amount{coverage_from_amount(upper(Tokens[i]))}; + if (amount >= 0.f) + { + coverage = amount; + ++i; + } + else if (next_number(coverage)) + coverage /= 8.f; // oktas + } + auto layer{typical_layer(*type, coverage, genus.base)}; + if (next_number(layer.base)) + { + layer.top = layer.base + genus.thickness; + next_number(layer.top); + next_number(layer.density); + } + Conditions.clouds.push_back(layer); + } + else if (keyword == "precipitation") + { + if (i < Tokens.size()) + { + auto const kind{lower(Tokens[i])}; + auto const known{kind == "none" || kind == "rain" || kind == "snow" || kind == "auto" || kind == "automatic"}; + if (known) + { + ++i; + Conditions.precipitation = (kind == "none") ? precipitation_type::none : (kind == "rain") ? precipitation_type::rain : (kind == "snow") ? precipitation_type::snow : precipitation_type::automatic; + } + Conditions.precipitation_intensity = (Conditions.precipitation == precipitation_type::none) ? 0.f : 0.5f; + next_number(Conditions.precipitation_intensity); + } + } + else if (keyword == "cloudwind") + { + next_number(Conditions.cloud_wind_direction); + next_number(Conditions.cloud_wind_speed); + } + else if (keyword == "temperature") + { + float temperature{0.f}; + if (next_number(temperature)) + Conditions.temperature = temperature; + } + else if (keyword == "elevation") + { + float elevation{0.f}; + if (next_number(elevation) && (Elevation != nullptr)) + *Elevation = elevation; + } + else if (keyword == "seed") + { + float seed{0.f}; + if (next_number(seed)) + Conditions.seed = static_cast(std::max(seed, 0.f)); + } + else if (keyword == "transition") + { + float transition{0.f}; + if (next_number(transition) && (Transition != nullptr)) + *Transition = std::max(transition, 0.f); + } + else + WriteLog("Weather: unknown entry \"" + Tokens[i - 1] + "\"", logtype::file); + } + Conditions.normalize(); +} + +bool is_section_keyword(std::string const &Token) +{ + static std::array const keywords{"metar", "visibility", "haze", "groundfog", "fognoise", "noclouds", "clear", "clouds", "precipitation", "cloudwind", "temperature", "elevation", "seed", "transition"}; + auto const token{lower(Token)}; + return std::any_of(keywords.begin(), keywords.end(), [&](char const *Keyword) { return token == Keyword; }); +} + +} // namespace + +cloud_genus const &cloud_genus_info(cloud_type const Type) +{ + auto const index{std::clamp(static_cast(Type), 0, static_cast(cloud_type::count) - 1)}; + return GENERA[index]; +} + +std::optional cloud_type_from_name(std::string_view const Name) +{ + auto const name{lower(Name)}; + for (int i = 0; i < static_cast(cloud_type::count); ++i) + { + if (name == GENERA[i].name || name == lower(GENERA[i].abbreviation)) + return static_cast(i); + } + if (name == "toweringcumulus") + return cloud_type::towering_cumulus; + return std::nullopt; +} + +float weather_conditions::overcast() const +{ + float clear{1.f}; + for (auto const &layer : clouds) + { + auto const &genus{cloud_genus_info(layer.type)}; + clear *= 1.f - std::clamp(layer.coverage * genus.opacity * std::min(layer.density, 1.5f), 0.f, 1.f); + } + if ((precipitation != precipitation_type::none) && (precipitation_intensity > 0.f)) + return 1.f + std::clamp(precipitation_intensity, 0.f, 1.f); + return std::clamp(1.f - clear, 0.f, 1.f); +} + +void weather_conditions::normalize() +{ + for (auto &layer : clouds) + { + layer.coverage = std::clamp(layer.coverage, 0.f, 1.f); + layer.base = std::clamp(layer.base, 0.f, 15000.f); + layer.top = std::clamp(layer.top, layer.base + 50.f, 20000.f); + layer.density = std::clamp(layer.density, 0.f, 4.f); + } + clouds.erase(std::remove_if(clouds.begin(), clouds.end(), [](cloud_layer const &Layer) { return Layer.coverage <= 0.f; }), clouds.end()); + if (clouds.size() > max_cloud_layers) + { + // the ones which cover the most are kept + std::stable_sort(clouds.begin(), clouds.end(), [](cloud_layer const &Left, cloud_layer const &Right) { return Left.coverage > Right.coverage; }); + clouds.resize(max_cloud_layers); + } + std::stable_sort(clouds.begin(), clouds.end(), [](cloud_layer const &Left, cloud_layer const &Right) { return Left.base < Right.base; }); + visibility = std::clamp(visibility, 10.f, 200000.f); + haze_height = std::clamp(haze_height, 10.f, 10000.f); + groundfog_thickness = std::clamp(groundfog_thickness, 0.f, 2000.f); + groundfog_visibility = std::clamp(groundfog_visibility, 10.f, 100000.f); + fog_noise = std::clamp(fog_noise, 0.f, 1.f); + precipitation_intensity = std::clamp(precipitation_intensity, 0.f, 1.f); + cloud_wind_direction = std::fmod(std::fmod(cloud_wind_direction, 360.f) + 360.f, 360.f); + cloud_wind_speed = std::clamp(cloud_wind_speed, 0.f, 100.f); +} + +bool parse_metar(std::string_view const Report, weather_conditions &Conditions) +{ + // underscores may stand for the spaces too, as in the text of a memory cell + auto report{upper(Report)}; + std::replace(report.begin(), report.end(), '_', ' '); + auto const tokens{split(report)}; + bool understood{false}; + bool cloudsgiven{false}; + std::vector clouds; + struct report_layer + { + float coverage; + float base; + std::string kind; + }; + std::vector layers; + std::optional visibility; + bool fog{false}, shallowfog{false}, patchyfog{false}, mist{false}, haze{false}, showers{false}, thunder{false}; + std::optional verticalvisibility; + precipitation_type precipitation{precipitation_type::none}; + float intensity{0.f}; + + for (std::size_t i = 0; i < tokens.size(); ++i) + { + auto token{tokens[i]}; + if (token == "TEMPO" || token == "BECMG" || token == "RMK" || token == "NOSIG" || token == "TREND") + break; // forecasts and remarks + if (token == "METAR" || token == "SPECI" || token == "AUTO" || token == "COR" || token == "NIL") + continue; + // wind: dddssKT, dddssGggKT, VRBssKT, MPS or KMH + { + auto const unit{token.ends_with("KT") ? 2 : (token.ends_with("MPS") || token.ends_with("KMH")) ? 3 : 0}; + if (unit > 0 && token.size() >= 5 + static_cast(unit)) + { + auto const body{token.substr(0, token.size() - unit)}; + auto const direction{body.substr(0, 3)}; + auto speedtext{body.substr(3)}; + if (auto const gust = speedtext.find('G'); gust != std::string::npos) + speedtext = speedtext.substr(0, gust); + if ((all_digits(direction) || direction == "VRB") && all_digits(speedtext)) + { + auto speed{static_cast(std::stoi(speedtext))}; + speed *= token.ends_with("KT") ? KNOTS : token.ends_with("KMH") ? (1.f / 3.6f) : 1.f; + if (direction != "VRB") + Conditions.cloud_wind_direction = static_cast(std::stoi(direction)); + // the wind at the clouds is stronger than at the ground + Conditions.cloud_wind_speed = std::max(speed * 1.5f, 2.f); + understood = true; + continue; + } + } + } + // variable wind direction range, runway visual range, pressure + if ((token.size() == 7 && token[3] == 'V' && all_digits(token.substr(0, 3)) && all_digits(token.substr(4))) || (token.size() > 1 && token[0] == 'R' && token.find('/') != std::string::npos) || + (token.size() == 5 && (token[0] == 'Q' || token[0] == 'A') && all_digits(token.substr(1)))) + continue; + // station and time + if ((token.size() == 4 && std::all_of(token.begin(), token.end(), [](char c) { return std::isalpha(static_cast(c)) != 0; }) && i == 0) || + (token.size() == 7 && token.back() == 'Z' && all_digits(token.substr(0, 6)))) + continue; + if (token == "CAVOK") + { + visibility = 15000.f; + cloudsgiven = true; + understood = true; + continue; + } + // visibility in metres, 9999 being 10 km or more + if (token.size() == 4 && all_digits(token)) + { + auto const value{static_cast(std::stoi(token))}; + visibility = (value >= 9999.f) ? 15000.f : std::max(value, 50.f); + understood = true; + continue; + } + if (token.size() > 4 && all_digits(token.substr(0, 4)) && !all_digits(token.substr(4)) && token.substr(4).find_first_not_of("NESW") == std::string::npos) + continue; // lowest visibility in a direction + // visibility in statute miles: P6SM, 10SM, 1/2SM, 1 1/2SM + if (token.ends_with("SM")) + { + auto body{token.substr(0, token.size() - 2)}; + float miles{-1.f}; + if (!body.empty() && (body[0] == 'P' || body[0] == 'M')) + body.erase(0, 1); + if (auto const slash = body.find('/'); slash != std::string::npos) + { + auto const numerator{body.substr(0, slash)}, denominator{body.substr(slash + 1)}; + if (all_digits(numerator) && all_digits(denominator) && std::stoi(denominator) > 0) + miles = static_cast(std::stoi(numerator)) / static_cast(std::stoi(denominator)); + // a whole number before the fraction + if (i > 0 && all_digits(tokens[i - 1]) && tokens[i - 1].size() <= 2 && visibility) + miles += *visibility / 1609.34f; + } + else if (all_digits(body)) + miles = static_cast(std::stoi(body)); + if (miles >= 0.f) + { + visibility = std::max(miles * 1609.34f, 50.f); + understood = true; + } + continue; + } + if (all_digits(token) && token.size() <= 2 && i + 1 < tokens.size() && tokens[i + 1].ends_with("SM")) + { + // whole miles before a fraction, added by the fraction + visibility = static_cast(std::stoi(token)) * 1609.34f; + continue; + } + // clouds + if (token == "SKC" || token == "CLR" || token == "NSC" || token == "NCD") + { + cloudsgiven = true; + understood = true; + continue; + } + if (token.size() >= 6 && coverage_from_amount(token.substr(0, 3)) >= 0.f) + { + auto const height{token.substr(3, 3)}; + auto const kind{token.substr(6)}; + cloudsgiven = true; + understood = true; + if (all_digits(height)) + layers.push_back({coverage_from_amount(token.substr(0, 3)), static_cast(std::stoi(height)) * 100.f * FEET, kind}); + continue; + } + if (token.size() == 5 && token.starts_with("VV")) + { + if (all_digits(token.substr(2))) + verticalvisibility = static_cast(std::stoi(token.substr(2))) * 100.f * FEET; + understood = true; + continue; + } + // temperature and dew point + if (auto const slash = token.find('/'); slash != std::string::npos && slash > 0) + { + auto temperature{token.substr(0, slash)}; + auto const negative{temperature[0] == 'M'}; + if (negative) + temperature.erase(0, 1); + if (all_digits(temperature) && temperature.size() <= 2) + { + Conditions.temperature = static_cast(std::stoi(temperature)) * (negative ? -1.f : 1.f); + understood = true; + continue; + } + } + // present weather: intensity, descriptor, phenomena + { + std::string_view body{token}; + float strength{0.6f}; + if (body.starts_with('-')) + { + strength = 0.3f; + body.remove_prefix(1); + } + else if (body.starts_with('+')) + { + strength = 1.f; + body.remove_prefix(1); + } + else if (body.starts_with("VC")) + { + // in the vicinity only + continue; + } + bool recognized{false}; + bool shallow{false}, patches{false}, freezing{false}; + while (body.size() >= 2) + { + auto const code{body.substr(0, 2)}; + body.remove_prefix(2); + if (code == "MI") { shallow = true; recognized = true; } + else if (code == "BC" || code == "PR") { patches = true; recognized = true; } + else if (code == "SH") { showers = true; recognized = true; } + else if (code == "TS") { thunder = true; showers = true; recognized = true; } + else if (code == "FZ") { freezing = true; recognized = true; } + else if (code == "DR" || code == "BL") { recognized = true; } + else if (code == "RA" || code == "DZ" || code == "UP") + { + if (precipitation != precipitation_type::snow) + precipitation = precipitation_type::rain; + intensity = std::max(intensity, strength * (code == "DZ" ? 0.4f : 1.f)); + recognized = true; + } + else if (code == "SN" || code == "SG" || code == "PL" || code == "IC" || code == "GS" || code == "GR") + { + precipitation = precipitation_type::snow; + intensity = std::max(intensity, strength); + recognized = true; + } + else if (code == "FG") { fog = true; shallowfog = shallowfog || shallow; patchyfog = patchyfog || patches; recognized = true; } + else if (code == "BR") { mist = true; recognized = true; } + else if (code == "HZ" || code == "FU" || code == "DU" || code == "SA" || code == "VA") { haze = true; recognized = true; } + else + { + recognized = false; + break; + } + } + (void)freezing; + if (recognized) + { + understood = true; + continue; + } + } + } + if (!understood) + return false; + + if (visibility) + Conditions.visibility = *visibility; + Conditions.groundfog_thickness = 0.f; + if (fog || verticalvisibility) + { + // the reported visibility is the one in the fog; above it the air is clearer + auto const infog{visibility.value_or(500.f)}; + Conditions.groundfog_visibility = infog; + Conditions.groundfog_thickness = verticalvisibility ? std::max(*verticalvisibility, 30.f) : shallowfog ? 8.f : 120.f; + Conditions.visibility = std::max(infog * 6.f, 4000.f); + Conditions.fog_noise = patchyfog ? 0.8f : shallowfog ? 0.5f : 0.3f; + } + else if (mist || haze) + { + Conditions.haze_height = mist ? 300.f : 1200.f; + Conditions.fog_noise = mist ? 0.4f : 0.2f; + } + else + Conditions.haze_height = 800.f; + if (precipitation != precipitation_type::none) + { + Conditions.precipitation = precipitation; + Conditions.precipitation_intensity = std::clamp(intensity, 0.1f, 1.f); + } + else + { + Conditions.precipitation = precipitation_type::none; + Conditions.precipitation_intensity = 0.f; + } + if (cloudsgiven || verticalvisibility) + { + auto const precipitating{precipitation != precipitation_type::none}; + for (std::size_t l = 0; l < layers.size(); ++l) + { + auto const &layer{layers[l]}; + cloud_type type; + if (layer.kind == "CB") + type = cloud_type::cumulonimbus; + else if (layer.kind == "TCU") + type = cloud_type::towering_cumulus; + else + type = report_genus(layer.base, layer.coverage, precipitating && l == 0, showers); + auto cloud{typical_layer(type, layer.coverage, layer.base)}; + // a layer doesn't reach into the next one reported above it, apart from the towers + if ((l + 1 < layers.size()) && (type != cloud_type::cumulonimbus) && (type != cloud_type::towering_cumulus)) + cloud.top = std::max(cloud.base + 100.f, std::min(cloud.top, layers[l + 1].base - 100.f)); + clouds.push_back(cloud); + } + if (thunder && std::none_of(clouds.begin(), clouds.end(), [](cloud_layer const &Layer) { return Layer.type == cloud_type::cumulonimbus; })) + clouds.push_back(typical_layer(cloud_type::cumulonimbus, 0.3f, 900.f)); + Conditions.clouds = clouds; + } + Conditions.normalize(); + return true; +} + +std::string to_metar(weather_conditions const &Conditions) +{ + std::ostringstream report; + report.imbue(std::locale::classic()); + auto const knots{static_cast(std::round(Conditions.cloud_wind_speed / 1.5f / KNOTS))}; + report << std::setfill('0') << std::setw(3) << static_cast(std::round(Conditions.cloud_wind_direction / 10.f) * 10.f) % 360 << std::setw(2) << std::min(knots, 99) << "KT "; + auto const fogged{Conditions.groundfog_thickness > 0.f}; + auto const visibility{fogged ? Conditions.groundfog_visibility : Conditions.visibility}; + if (visibility >= 10000.f) + report << "9999"; + else + report << std::setw(4) << static_cast(std::round(visibility / 50.f) * 50.f); + if (Conditions.precipitation_intensity > 0.f && Conditions.precipitation != precipitation_type::none) + { + report << ' ' << (Conditions.precipitation_intensity < 0.4f ? "-" : Conditions.precipitation_intensity > 0.8f ? "+" : ""); + auto const snow{(Conditions.precipitation == precipitation_type::snow) || ((Conditions.precipitation == precipitation_type::automatic) && (Global.AirTemperature <= 1.f))}; + report << (snow ? "SN" : "RA"); + } + if (fogged) + report << ' ' << (Conditions.groundfog_thickness < 30.f ? "MIFG" : Conditions.groundfog_visibility < 1000.f ? "FG" : "BR"); + else if (Conditions.visibility < 5000.f) + report << " BR"; + bool any{false}; + for (auto const &layer : Conditions.clouds) + { + if (layer.coverage < 0.03f) + continue; + auto const amount{layer.coverage <= 0.25f ? "FEW" : layer.coverage <= 0.5f ? "SCT" : layer.coverage < 0.95f ? "BKN" : "OVC"}; + report << ' ' << amount << std::setw(3) << std::clamp(static_cast(std::round(layer.base / FEET / 100.f)), 0, 999); + if (layer.type == cloud_type::cumulonimbus) + report << "CB"; + else if (layer.type == cloud_type::towering_cumulus) + report << "TCU"; + any = true; + } + if (!any) + report << " NSC"; + if (Conditions.temperature) + { + auto const temperature{static_cast(std::round(*Conditions.temperature))}; + report << ' ' << (temperature < 0 ? "M" : "") << std::setw(2) << std::abs(temperature) << '/'; + } + return report.str(); +} + +std::vector clouds_from_overcast(float const Overcast) +{ + auto const overcast{std::clamp(Overcast, 0.f, 2.f)}; + auto const sky{std::min(overcast, 1.f)}; + std::vector clouds; + auto const add = [&](cloud_type const Type, float const Coverage, float const Base, float const Top, float const Density = 1.f) { + clouds.push_back(cloud_layer{Type, Coverage, Base, Top, Density}); + }; + // the high veil is there on most days, hidden once the low clouds close up + if (sky < 0.95f) + add(cloud_type::cirrus, 0.15f + 0.3f * sky, 8200.f, 9000.f); + if (sky <= 0.5f) + // fair weather cumulus, growing taller with the cover + add(cloud_type::cumulus, std::max(0.05f, sky * 0.9f), 1200.f, 2000.f + 1600.f * sky); + else if (sky < 0.9f) + { + add(cloud_type::stratocumulus, 0.45f + (sky - 0.5f) * 1.2f, 1000.f, 1700.f); + add(cloud_type::altocumulus, 0.3f, 3600.f, 4000.f); + } + else if (overcast <= 1.f) + { + add(cloud_type::stratocumulus, 0.95f + (sky - 0.9f) * 0.5f, 700.f, 1500.f); + add(cloud_type::altostratus, 0.6f, 3500.f, 5000.f); + } + else + { + // rain or snow: a thick, low sheet, lower and denser the heavier it falls, with storms in the heaviest + auto const precipitation{overcast - 1.f}; + add(cloud_type::nimbostratus, 1.f, 600.f - 300.f * precipitation, 3500.f, 1.f + 0.5f * precipitation); + if (precipitation > 0.6f) + add(cloud_type::cumulonimbus, 0.3f * (precipitation - 0.6f) / 0.4f, 600.f, 9000.f); + } + return clouds; +} + +float visibility_from_fog_range(float const Fogend, float const Overcast) +{ + auto const range{Fogend / std::max(1.f, Overcast * 2.f)}; + return std::clamp(range * LEGACY_VISIBILITY_SCALE, 10.f, 200000.f); +} + +float fog_range_from_visibility(float const Visibility, float const Overcast) +{ + return Visibility / LEGACY_VISIBILITY_SCALE * std::max(1.f, Overcast * 2.f); +} + +void world_weather::reset() +{ + m_scenery.reset(); + m_elevation.reset(); + m_described_clouds = false; + m_described_fog = false; + m_known_overcast = -1.f; + m_known_fogend = -1.f; + m_transition = 0.0; + m_transition_time = 0.0; + m_ground_set = false; + m_ground = 0.f; + m_cloud_offset = glm::dvec2{0.0}; + m_time = 0.0; +} + +void world_weather::deserialize(cParser &Input) +{ + std::vector tokens; + auto token{Input.getToken(false)}; + while (!token.empty() && lower(token) != "endweather") + { + tokens.push_back(token); + token = Input.getToken(false); + } + // a later section adds to an earlier one + auto conditions{m_scenery.value_or(weather_conditions{})}; + parse_section(tokens, conditions, &m_elevation, nullptr); + m_scenery = conditions; +} + +void world_weather::init() +{ + m_transition = 0.0; + m_transition_time = 0.0; + if (m_scenery) + { + // the description wins over the atmo section, wherever it was in the scenery + m_target = *m_scenery; + if (m_target.temperature) + Global.AirTemperature = *m_target.temperature; + m_described_clouds = true; + m_described_fog = true; + m_current = m_target; + write_globals(); + } + else + { + m_target = weather_conditions{}; + follow_overcast(); + follow_fog_range(); + m_current = m_target; + } + m_from = m_current; + if (m_elevation) + { + m_ground = *m_elevation; + m_ground_set = true; + } + WriteLog("Weather: " + to_metar(m_current) + (m_scenery ? " (described)" : " (from the overcast and the fog range)")); +} + +void world_weather::follow_overcast() +{ + m_target.clouds = clouds_from_overcast(Global.Overcast); + m_target.precipitation = precipitation_type::automatic; + m_target.precipitation_intensity = std::clamp(Global.Overcast - 1.f, 0.f, 1.f); + m_target.normalize(); + m_known_overcast = Global.Overcast; + m_described_clouds = false; +} + +void world_weather::follow_fog_range() +{ + m_target.visibility = visibility_from_fog_range(Global.fFogEnd, Global.Overcast); + m_target.groundfog_thickness = 0.f; + m_target.haze_height = 800.f; + m_target.normalize(); + m_known_fogend = Global.fFogEnd; + m_described_fog = false; +} + +void world_weather::write_globals() +{ + auto changed{false}; + if (m_described_clouds) + { + auto const overcast{m_current.overcast()}; + if (overcast != Global.Overcast) + { + Global.Overcast = overcast; + changed = true; + } + m_known_overcast = Global.Overcast; + } + if (m_described_fog) + { + // the fog the old renderers draw, and the scripting interface reads + auto const fogend{std::clamp(fog_range_from_visibility(std::min(m_current.visibility, m_current.groundfog_thickness > 0.f ? m_current.groundfog_visibility : m_current.visibility), Global.Overcast), 10.f, 50000.f)}; + Global.fFogEnd = fogend; + m_known_fogend = Global.fFogEnd; + } + if (changed) + { + // the weather name, the turbidity and the precipitation follow the overcast. the name drives the precipitation renderer, + // which picks it up by the change of the overcast + simulation::Environment.compute_weather(); + } +} + +void world_weather::update(double const Deltatime, glm::dvec3 const &Camera) +{ + if (!m_ground_set) + { + // the ground below the place the simulation starts at, for want of anything better: the camera is in the cab + m_ground = static_cast(Camera.y) - 3.f; + m_ground_set = true; + } + // something set the old overcast or fog range: the scenery atmo, the sliders, a script, the network. tiny changes are the way + // the precipitation is made to update after a change of the season, which keeps the description + auto const overcastchanged{std::abs(Global.Overcast - m_known_overcast) > 1e-3f}; + if (overcastchanged) + { + follow_overcast(); + m_current.clouds = m_target.clouds; + m_current.precipitation = m_target.precipitation; + m_current.precipitation_intensity = m_target.precipitation_intensity; + m_from = m_current; + } + else + m_known_overcast = Global.Overcast; + // the old fog thickened with the overcast, so it is followed with every change of either + if ((std::abs(Global.fFogEnd - m_known_fogend) > 0.5f) || (false == m_described_fog && overcastchanged)) + { + follow_fog_range(); + m_current.visibility = m_target.visibility; + m_current.groundfog_thickness = m_target.groundfog_thickness; + m_current.haze_height = m_target.haze_height; + m_from = m_current; + } + + // a change in progress + if (m_transition > 0.0) + { + m_transition_time += Deltatime; + auto const t{static_cast(std::clamp(m_transition_time / m_transition, 0.0, 1.0))}; + auto mixed{m_target}; + auto const mix = [t](float From, float To) { return From + (To - From) * t; }; + mixed.visibility = std::exp(mix(std::log(m_from.visibility), std::log(m_target.visibility))); + mixed.haze_height = mix(m_from.haze_height, m_target.haze_height); + mixed.groundfog_thickness = mix(m_from.groundfog_thickness, m_target.groundfog_thickness); + mixed.groundfog_visibility = std::exp(mix(std::log(m_from.groundfog_visibility), std::log(m_target.groundfog_visibility))); + mixed.fog_noise = mix(m_from.fog_noise, m_target.fog_noise); + auto const falling = [](weather_conditions const &Conditions) { return Conditions.precipitation == precipitation_type::none ? 0.f : Conditions.precipitation_intensity; }; + mixed.precipitation_intensity = mix(falling(m_from), falling(m_target)); + if (mixed.precipitation == precipitation_type::none && mixed.precipitation_intensity > 0.f) + mixed.precipitation = m_from.precipitation; + mixed.cloud_wind_speed = mix(m_from.cloud_wind_speed, m_target.cloud_wind_speed); + auto turn{m_target.cloud_wind_direction - m_from.cloud_wind_direction}; + turn -= 360.f * std::round(turn / 360.f); + mixed.cloud_wind_direction = m_from.cloud_wind_direction + turn * t; + // the same layers change their sizes; different ones fade out and in + auto const same{(m_from.clouds.size() == m_target.clouds.size()) && + std::equal(m_from.clouds.begin(), m_from.clouds.end(), m_target.clouds.begin(), [](cloud_layer const &Left, cloud_layer const &Right) { return Left.type == Right.type; })}; + if (same) + { + for (std::size_t i = 0; i < mixed.clouds.size(); ++i) + { + auto &layer{mixed.clouds[i]}; + auto const &from{m_from.clouds[i]}; + layer.coverage = mix(from.coverage, layer.coverage); + layer.base = mix(from.base, layer.base); + layer.top = mix(from.top, layer.top); + layer.density = mix(from.density, layer.density); + } + } + else if (t < 0.5f) + { + mixed.clouds = m_from.clouds; + for (auto &layer : mixed.clouds) + layer.coverage *= 1.f - 2.f * t; + } + else + { + for (auto &layer : mixed.clouds) + layer.coverage *= 2.f * t - 1.f; + } + mixed.clouds.erase(std::remove_if(mixed.clouds.begin(), mixed.clouds.end(), [](cloud_layer const &Layer) { return Layer.coverage <= 0.f; }), mixed.clouds.end()); + m_current = mixed; + if (t >= 1.f) + { + m_transition = 0.0; + m_current = m_target; + } + } + else + m_current = m_target; + + write_globals(); + + // the clouds go with the wind + m_time += Deltatime; + m_cloud_offset += glm::dvec2{cloud_velocity()} * Deltatime; + // kept small, the shaders take it in single precision; the noise of the clouds repeats long before this + auto const period{1024.0 * 1024.0}; + m_cloud_offset = glm::mod(m_cloud_offset, glm::dvec2{period}); +} + +glm::vec2 world_weather::cloud_velocity() const +{ + // where the wind goes to, from the north clockwise. the world has the north at +z and the west at +x + auto const to{glm::radians(m_current.cloud_wind_direction + 180.f)}; + return glm::vec2{-std::sin(to), std::cos(to)} * m_current.cloud_wind_speed; +} + +std::optional world_weather::precipitation_override() const +{ + if (!m_described_clouds) + return std::nullopt; + if (m_current.precipitation == precipitation_type::rain || m_current.precipitation == precipitation_type::snow) + return m_current.precipitation; + return std::nullopt; +} + +void world_weather::apply(weather_conditions const &Conditions, float const Transition, bool const Quiet) +{ + m_from = m_current; + m_target = Conditions; + m_target.normalize(); + if (m_target.temperature) + Global.AirTemperature = *m_target.temperature; + m_described_clouds = true; + m_described_fog = true; + m_transition = std::max(Transition, 0.f); + m_transition_time = 0.0; + if (m_transition <= 0.0) + m_current = m_target; + write_globals(); + if (false == Quiet) + WriteLog("Weather: changed to " + to_metar(m_target) + (m_transition > 0.0 ? " over " + std::to_string(static_cast(m_transition)) + " s" : "")); +} + +bool world_weather::apply_text(std::string_view const Text, float const Transition) +{ + auto tokens{split(Text)}; + if (tokens.empty()) + return false; + auto conditions{m_target}; + auto transition{Transition}; + if (is_section_keyword(tokens.front())) + { + // the quoted report of a section given as a text is split here, glue it back up to the next keyword + std::vector glued; + for (std::size_t i = 0; i < tokens.size(); ++i) + { + glued.push_back(tokens[i]); + if (lower(tokens[i]) == "metar") + { + std::string report; + while (i + 1 < tokens.size() && !is_section_keyword(tokens[i + 1])) + report += (report.empty() ? "" : " ") + strip_quotes(tokens[++i]); + glued.push_back(report); + } + } + parse_section(glued, conditions, nullptr, &transition); + } + else if (!parse_metar(Text, conditions)) + return false; + apply(conditions, transition); + return true; +} diff --git a/environment/weather.h b/environment/weather.h new file mode 100644 index 00000000..d50e716e --- /dev/null +++ b/environment/weather.h @@ -0,0 +1,197 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +class cParser; + +// cloud genera as aviation reports them (WMO), each drawn in its own way. the order is that of the names in cloud_genus_info() +enum class cloud_type : int +{ + cumulus = 0, + towering_cumulus, + cumulonimbus, + stratocumulus, + stratus, + nimbostratus, + altocumulus, + altostratus, + cirrus, + cirrostratus, + cirrocumulus, + count +}; + +// what a genus typically looks like, and how it is drawn +struct cloud_genus +{ + char const *name; // full name, as in the weather section of a scenery + char const *abbreviation; // as in reports (Cu, TCu, Cb...) + float base; // typical height of the base above the ground, metres + float thickness; // typical depth of the layer, metres + float extinction; // light taken out per metre inside the cloud at density 1 (1/m); for a flat layer its optical depth + float cell; // horizontal size of the single clouds or of the lumps of a sheet, metres + float spread; // horizontal size of the areas the coverage is spread over, metres + float bottom; // part of the depth over which the density rises from the base: small for the flat base of a cumulus + float top; // part of the depth from which the density falls towards the top + float top_variation; // how much the top of single clouds varies (towers of different heights), 0-1 + float anvil; // how far the top spreads out (the anvil of a cumulonimbus), 0-1 + float erosion; // how much the small scale noise eats into the edges, 0-1 + float stretch; // how much the noise is drawn out along the wind (streaks of a cirrus), 1 = not at all + float opacity; // share of the sky light a full layer holds back, for the overcast the rest of the simulation goes by + bool flat; // drawn as a thin sheet (high clouds), not ray marched through +}; + +cloud_genus const &cloud_genus_info(cloud_type Type); +// genus named by its full name or abbreviation, any case +std::optional cloud_type_from_name(std::string_view Name); + +struct cloud_layer +{ + cloud_type type{cloud_type::cumulus}; + float coverage{0.f}; // share of the sky the layer covers, 0-1 (oktas / 8) + float base{1000.f}; // height of the base above the ground reference, metres + float top{2000.f}; // height of the top above the ground reference, metres + float density{1.f}; // multiplier of the typical density of the genus +}; + +enum class precipitation_type : int +{ + automatic = 0, // rain or snow by the air temperature, as the overcast above 1 always did + none, + rain, + snow +}; + +// the weather of the scenery: what the simulation keeps and the renderer draws. all heights are above the ground reference +struct weather_conditions +{ + static constexpr std::size_t max_cloud_layers{4}; + + std::vector clouds; // ordered by the base, at most max_cloud_layers + // meteorological optical range at the ground, metres: how far a dark object is still seen against the horizon (5% contrast) + float visibility{10000.f}; + float haze_height{800.f}; // scale height of the haze: its density falls e times every this many metres up + float groundfog_thickness{0.f}; // fog lying on the ground (in the valleys below the reference too), metres; 0 = none + float groundfog_visibility{300.f}; // visibility inside it + float fog_noise{0.3f}; // how patchy the fog is, 0-1 + precipitation_type precipitation{precipitation_type::automatic}; + float precipitation_intensity{0.f}; // 0-1 + float cloud_wind_direction{270.f}; // where the wind at the height of the clouds blows from, degrees from the north, clockwise + float cloud_wind_speed{8.f}; // m/s + std::uint32_t seed{0}; // changes the shapes of the clouds + std::optional temperature; // air temperature a report gave, degrees C; applied with the weather + + // share of the sky the clouds hold back, 0-1, and the precipitation past it: the overcast the rest of the simulation goes by + float overcast() const; + // sorts the layers by their base and keeps them within their limits + void normalize(); +}; + +// reads a weather report (METAR, the body of it: the station and time may be there or not) into Conditions. what the report does +// not mention is left as it was, apart from the clouds, which are replaced when it has any cloud group or says there are none. +// returns false when nothing in it was understood +bool parse_metar(std::string_view Report, weather_conditions &Conditions); +// a report of the conditions in the METAR form, for the user interface and the scripting interface +std::string to_metar(weather_conditions const &Conditions); +// clouds and precipitation of the old single number of the weather (atmo, the overcast slider, scripts): up to 1 the sky clouds +// over, past it rain or snow falls +std::vector clouds_from_overcast(float Overcast); +// visibility of the old fog range (atmo, Global.fFogEnd), as the old fog thickened with the overcast, and back +float visibility_from_fog_range(float Fogend, float Overcast); +float fog_range_from_visibility(float Visibility, float Overcast); + +// the weather of the simulation. the scenery may describe it (the weather section); otherwise, and whenever something sets the +// old overcast and fog range (atmo, the sliders, the scripting interface, the setweather command of the network), the clouds and +// the visibility are made from those. while the weather is described, the overcast and fog range are worked out from it, so the +// rest of the simulation (lights, sounds, precipitation, friction) goes on as before +class world_weather +{ + public: + // forgets the weather of the previous scenery + void reset(); + // the weather section of a scenery, read before the simulation starts + void deserialize(cParser &Input); + // starts the weather of the loaded scenery + void init(); + // moves the clouds with the wind, goes on with a change of the weather and keeps the old global state in step + void update(double const Deltatime, glm::dvec3 const &Camera); + // changes the weather while the simulation runs, over Transition seconds. Quiet: not logged (changes made by a slider) + void apply(weather_conditions const &Conditions, float const Transition, bool const Quiet = false); + // changes the weather after a report or a weather section text, as the scripting interface gives it. false when it says nothing + bool apply_text(std::string_view Text, float const Transition); + + // the weather as it is now (in the middle of a change, a mix of the old and the new one) + weather_conditions const &conditions() const + { + return m_current; + } + // the weather a change goes to, or the current one + weather_conditions const &target() const + { + return m_target; + } + // true when the clouds come from a description, false when from the old overcast + bool described() const + { + return m_described_clouds; + } + // world height (y) of the ground the heights of the weather are counted from + float ground() const + { + return m_ground; + } + // how far the wind carried the clouds, world x and z, metres + glm::dvec2 const &cloud_offset() const + { + return m_cloud_offset; + } + // seconds of simulation since the start, for the clouds and fog to change their shapes slowly + double time() const + { + return m_time; + } + // world space velocity of the clouds (x, z), m/s + glm::vec2 cloud_velocity() const; + // precipitation the description sets, or nullopt when the air temperature decides + std::optional precipitation_override() const; + + private: + // the clouds and the visibility of the old global state + void follow_overcast(); + void follow_fog_range(); + // the old global state of the weather described + void write_globals(); + + std::optional m_scenery; // read from the scenery + std::optional m_elevation; // ground reference the scenery gives + weather_conditions m_from; // weather a change started at + weather_conditions m_target; + weather_conditions m_current; + double m_transition{0.0}; // length of the change in progress, seconds + double m_transition_time{0.0}; // how far it got + bool m_described_clouds{false}; + bool m_described_fog{false}; + // the overcast and fog range last written or followed, to tell when something else changed them + float m_known_overcast{-1.f}; + float m_known_fogend{-1.f}; + bool m_ground_set{false}; + float m_ground{0.f}; + glm::dvec2 m_cloud_offset{0.0}; + double m_time{0.0}; +}; diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 846770d4..6392b33b 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -63,6 +63,18 @@ void gl::glsl_common_setup() "#define LOCAL_SHADOW_LAMPS_TEX " + std::to_string(LOCAL_SHADOW_LAMPS_TEX) + "\n" + "#define HEADLIGHT_SHADOW_TEX " + std::to_string(HEADLIGHT_SHADOW_TEX) + "\n" + "#define MAX_HEADLIGHT_SHADOWS " + std::to_string(MAX_HEADLIGHT_SHADOWS) + "U\n" + + "#define FOG_VOLUME_TEX " + std::to_string(FOG_VOLUME_TEX) + "\n" + + "#define CLOUD_SHADOW_TEX " + std::to_string(CLOUD_SHADOW_TEX) + "\n" + + "#define CLOUD_TEX " + std::to_string(CLOUD_TEX) + "\n" + + "#define CLOUD_SHAPE_TEX " + std::to_string(CLOUD_SHAPE_TEX) + "\n" + + "#define CLOUD_DETAIL_TEX " + std::to_string(CLOUD_DETAIL_TEX) + "\n" + + "#define CLOUD_COVERAGE_TEX " + std::to_string(CLOUD_COVERAGE_TEX) + "\n" + + "#define CLOUD_HISTORY_TEX " + std::to_string(CLOUD_HISTORY_TEX) + "\n" + + "#define WEATHER_UBO_BINDING " + std::to_string(WEATHER_UBO_BINDING) + "\n" + + "#define WEATHER_SCRATCH_SSBO_BINDING " + std::to_string(WEATHER_SCRATCH_SSBO_BINDING) + "\n" + + "#define MAX_CLOUD_LAYERS " + std::to_string(MAX_CLOUD_LAYERS) + "\n" + + "#define FOG_HORIZON_SAMPLES " + std::to_string(FOG_HORIZON_SAMPLES) + "\n" + + "#define WEATHER_GROUP_SIZE " + std::to_string(WEATHER_GROUP_SIZE) + "\n" + R"STRING( const uint LIGHT_SPOT = 0U; const uint LIGHT_POINT = 1U; diff --git a/gl/ubo.h b/gl/ubo.h index 68c63cbd..531f810d 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -65,6 +65,16 @@ namespace gl // shadow maps of the headlights of vehicles (2d array, depth compared; opengl46_local_shadows), and how many of them there may be const size_t HEADLIGHT_SHADOW_TEX = MAX_TEXTURES + HELPER_TEXTURES + 3; const size_t MAX_HEADLIGHT_SHADOWS = 4; + // weather (opengl46_weather): the fog of the main view (3d, apply_fog.glsl) and the shadows the clouds cast (2d), read by the + // shading. bound directly, like the ones above + const size_t FOG_VOLUME_TEX = MAX_TEXTURES + HELPER_TEXTURES + 4; + const size_t CLOUD_SHADOW_TEX = MAX_TEXTURES + HELPER_TEXTURES + 5; + // the clouds drawn into the sky, and the noise the clouds and the fog are made of; bound by the passes which read them + const size_t CLOUD_TEX = MAX_TEXTURES + HELPER_TEXTURES + 6; + const size_t CLOUD_SHAPE_TEX = MAX_TEXTURES + HELPER_TEXTURES + 7; + const size_t CLOUD_DETAIL_TEX = MAX_TEXTURES + HELPER_TEXTURES + 8; + const size_t CLOUD_COVERAGE_TEX = MAX_TEXTURES + HELPER_TEXTURES + 9; + const size_t CLOUD_HISTORY_TEX = MAX_TEXTURES + HELPER_TEXTURES + 10; struct scene_ubs { @@ -392,6 +402,55 @@ namespace gl static_assert(sizeof(atmosphere_result) == 16 * (SKY_SH_COEFFICIENTS + 4), "bad size of atmosphere_result"); + // weather of the opengl 4.6 renderer (opengl46_weather, weather_common.glsl): fog, clouds, their shadows + const GLuint WEATHER_UBO_BINDING = 5; + // layers of clouds drawn, see weather_conditions::max_cloud_layers + const size_t MAX_CLOUD_LAYERS = 4; + // colours of the fog towards the horizon, by the angle from the azimuth of the sun (0-180 degrees) + const size_t FOG_HORIZON_SAMPLES = 32; + // work groups of the weather passes: 8x8 for the images, 8x8x1 for the fog volume + const GLuint WEATHER_GROUP_SIZE = 8; + + // layout std140: one layer of clouds (GLSL: cloud_layer_s), see weather_common.glsl + struct cloud_layer_ubs + { + glm::vec4 heights; + glm::vec4 shape; + glm::vec4 profile; + glm::vec4 motion; + glm::vec4 extra; + }; + + static_assert(sizeof(cloud_layer_ubs) == 80, "bad size of cloud_layer_ubs"); + + // layout std140: the weather for the shading and the weather passes (GLSL: weather_ubo), see weather_common.glsl + struct weather_ubs + { + glm::vec4 fog_haze; + glm::vec4 fog_ground; + glm::vec4 fog_noise; + glm::vec4 fog_light; + glm::vec4 fog_volume_params; + glm::vec4 fog_volume_target; + glm::vec4 weather_camera; + glm::vec4 weather_sun_direction; + glm::vec4 weather_sun_color; + glm::vec4 weather_ambient; + glm::vec4 cloud_shadow_params; + glm::vec4 cloud_wind; + cloud_layer_ubs cloud_layers[MAX_CLOUD_LAYERS]; + // filled on the gpu (fog_horizon.comp) while the atmosphere is used, by the cpu otherwise + glm::vec4 fog_horizon[FOG_HORIZON_SAMPLES]; + }; + + static_assert(offsetof(weather_ubs, cloud_layers) == 192, "bad layout of weather_ubs"); + static_assert(offsetof(weather_ubs, fog_horizon) == 192 + 80 * MAX_CLOUD_LAYERS, "bad layout of weather_ubs"); + static_assert(sizeof(weather_ubs) == 192 + 80 * MAX_CLOUD_LAYERS + 16 * FOG_HORIZON_SAMPLES, "bad size of weather_ubs"); + + // the weather buffer is written by fog_horizon.comp as a storage block bound here for the pass. the point is one of the + // instance culling, which binds its buffers before every use + const GLuint WEATHER_SCRATCH_SSBO_BINDING = 9; + // culling of model instances in compute passes (instance_cull.comp, instance_cull_offsets.comp). binding points of // the shader storage blocks, apart from the ones bound for the shading so the passes don't disturb them const GLuint INSTANCE_RECORD_SSBO_BINDING = 9; // scene::basic_cell::instance_record of the queued buckets diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index 4a572e79..a35caca4 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -190,6 +190,7 @@ bool opengl46_renderer::Init(GLFWwindow *Window) try { m_sky.init(); + m_weather.init(); m_instancecull.init(); m_hiz.init(); m_exposure.init(); @@ -485,6 +486,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp) scene_ubo->bind_uniform(); light_ssbo->bind_base(); m_sky.bind(); + m_weather.bind(); m_exposure.bind(); vp.clusters = std::make_unique(); @@ -627,7 +629,12 @@ bool opengl46_renderer::Render() // the light of the sky for the render: colours of the sun, ambient light and fog from the atmosphere where it is // used, the directional part of the ambient light m_fogcolor = Global.FogColor; + // the volumetric clouds of the previous frame tell the sky whether to look overcast and dim the sun itself + m_sky.set_clouds(m_weather.clouds_drawn(), m_weather.cloud_shadows()); m_sky.update(m_sunlight, m_fogcolor, simulation::Environment.skydome(), simulation::Environment.sun().getDirection(), simulation::Environment.sun().getAngle()); + // fog, clouds and stars of the weather, in the light of the sky worked out above + if (false == Global.gfx_skiprendering) + m_weather.update(m_sky, -m_sunlight.direction, glm::vec3{m_sunlight.diffuse} * simulation::Environment.light_intensity(), glm::vec3{m_sunlight.ambient}, m_fogcolor, Global.pCamera.Pos); // lamps of models and vehicles around the camera and the cab lamp, for the views of the frame to take into their light buffers if ((false == Global.gfx_skiprendering) && (simulation::Region != nullptr)) m_locallights.gather(Global.pCamera.Pos, *simulation::Region, m_geometry, m_framestamp); @@ -729,6 +736,7 @@ void opengl46_renderer::SwapBuffers() if (auto const culltime = m_cullstimer.result()) m_debugtimestext += " cull: " + to_string(static_cast(*culltime) / 1000000.0, 3) + " ms\n"; m_debugtimestext += m_sky.info_times(); + m_debugtimestext += m_weather.info_times(); m_debugtimestext += m_exposure.info_times(); m_debugtimestext += m_ao.info_times(); m_debugtimestext += m_instancecull.info_times(); @@ -834,6 +842,12 @@ void opengl46_renderer::draw_debug_ui() } ImGui::End(); + if (ImGui::Begin("Weather")) + { + m_weather.draw_debug_ui(); + } + ImGui::End(); + if (ImGui::Begin("Sky and exposure")) { m_sky.draw_debug_ui(); @@ -1068,6 +1082,19 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode) Cull_Lights( vp ); } + // the clouds of the view and the fog volume, which take the lights of the clusters and the shadow map of the pass + { + auto const fogtarget{ Global.gfx_skippipeline ? target_size : glm::ivec2{ vp.width, vp.height } }; + if( vp.main ) { + if( Global.gfx_shadowmap_enabled ) + setup_shadow_bind_map(); + m_weather.render_view( + OpenGLMatrices.data( GL_PROJECTION ), glm::mat4{ m_colorpass.pass_camera.modelview() }, m_colorpass.pass_camera.position(), fogtarget, + ( m_cablamplit && Global.gfx_lights_clustered && vp.clusters->valid() ) ? 1 : -1 ); + } + m_weather.setup_pass( vp.main, fogtarget ); + } + glDebug("render environment"); scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION); @@ -1344,6 +1371,8 @@ if (!Global.gfx_skippipeline) m_env_fb->bind(); setup_env_map(m_empty_cubemap.get()); + // the fog volume is the one of the main view + m_weather.setup_pass(false, glm::ivec2{static_cast(gl::ENVMAP_SIZE)}); setup_matrices(); // the light buffer holds what the last color pass left in it, in the view space of its camera, while the face is drawn @@ -2096,6 +2125,9 @@ bool opengl46_renderer::Render(world_environment *Environment) Bind_Material(null_handle); ::glPushMatrix(); + // the sky gets its fog all at once, laid over it with the clouds (opengl46_weather::draw_sky) + auto const fogdensity{model_ubs.fog_density}; + model_ubs.fog_density = 0.f; // skydome // drawn with 500m radius to blend in if the fog range is low @@ -2118,28 +2150,18 @@ bool opengl46_renderer::Render(world_environment *Environment) ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); - // stars - if (Environment->m_stars.m_stars != nullptr && Global.fFogEnd > 500.f) - { - // setup - ::glPushMatrix(); - ::glRotatef(Environment->m_stars.m_latitude, 1.f, 0.f, 0.f); // ustawienie osi OY na północ - ::glRotatef(-std::fmod((float)Global.fTimeAngleDeg, 360.f), 0.f, 1.f, 0.f); // obrót dobowy osi OX + auto const &modelview = OpenGLMatrices.data(GL_MODELVIEW); - // render - Render(Environment->m_stars.m_stars, nullptr, 1.0); - - // post-render cleanup - ::glPopMatrix(); - } + // stars of the catalogue, turned by the sidereal time; the fog and the clouds go over them + m_weather.draw_stars(modelview, Environment->m_sun.getAngle(), Environment->m_sun.getSiderealTime(), Environment->m_sun.getLatitude()); + ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); // celestial bodies m_celestial_shader->bind(); m_empty_vao->bind(); - auto const &modelview = OpenGLMatrices.data(GL_MODELVIEW); - - auto const fogfactor{std::clamp(Global.fFogEnd / 2000.f, 0.f, 1.f)}; // stronger fog reduces opacity of the celestial bodies + // the fog and the clouds laid over the sky hide the sun and the moon; without the clouds the overcast fades them + auto const overcastfactor{m_weather.clouds_drawn() ? 1.f : std::clamp(1.5f - Global.Overcast, 0.f, 1.f)}; float const duskfactor = 1.0f - std::clamp(std::abs(Environment->m_sun.getAngle()), 0.0f, 12.0f) / 12.0f; glm::vec3 suncolor = glm::mix(glm::vec3(1.0f, 242.0f / 255.0f, 231.0f / 255.0f), glm::vec3(235.0f / 255.0f, 140.0f / 255.0f, 36.0f / 255.0f), duskfactor); suncolor = m_sky.sun_disc_color(suncolor); @@ -2147,7 +2169,7 @@ bool opengl46_renderer::Render(world_environment *Environment) // sun { Bind_Texture(0, m_suntexture); - glm::vec4 color(suncolor.x, suncolor.y, suncolor.z, std::clamp(1.5f - Global.Overcast, 0.f, 1.f) * fogfactor); + glm::vec4 color(suncolor.x, suncolor.y, suncolor.z, overcastfactor); auto const sunvector = Environment->m_sun.getDirection(); /*float const size = std::lerp( // TODO: expose distance/scale factor from the moon object @@ -2166,7 +2188,7 @@ bool opengl46_renderer::Render(world_environment *Environment) glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); glm::vec4 color(mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day - std::max(0.f, 1.0 - 0.5 * Global.fLuminance - 0.65 * std::max(0.f, glm::dot(Environment->m_sun.getDirection(), Environment->m_moon.getDirection()))) * fogfactor); + std::max(0.f, 1.0 - 0.5 * Global.fLuminance - 0.65 * std::max(0.f, glm::dot(Environment->m_sun.getDirection(), Environment->m_moon.getDirection()))) * overcastfactor); auto const moonvector = Environment->m_moon.getDirection(); @@ -2241,31 +2263,19 @@ bool opengl46_renderer::Render(world_environment *Environment) ::glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - // clouds - if (Environment->m_clouds.mdCloud) + // clouds of the weather and the fog over the sky: of the main view in its colour pass, of the panorama around the camera in + // the other views and in the faces of the environment map { - // setup - auto const color { - glm::clamp( - ( glm::vec3 { Global.DayLight.ambient } - + 0.35f * glm::vec3{ Global.DayLight.diffuse } ) * simulation::Environment.light_intensity() - * 0.5f, - glm::vec3{ 0.f }, glm::vec3{ 1.f } ) }; - - // write cloud color into material - TSubModel *mdl = Environment->m_clouds.mdCloud->Root; - if (mdl->m_material != null_handle) - m_materials.material(mdl->m_material).params[0] = glm::vec4(color * 2.5f, 1.0f); - - // render - Render(Environment->m_clouds.mdCloud, nullptr, 100.0); - Render_Alpha(Environment->m_clouds.mdCloud, nullptr, 100.0); - // post-render cleanup + auto const mainview{(m_renderpass.draw_mode == rendermode::color) && (m_current_viewport != nullptr) && m_current_viewport->main}; + m_empty_vao->bind(); + m_weather.draw_sky(OpenGLMatrices.data(GL_PROJECTION), modelview, mainview); } gl::program::unbind(); gl::vao::unbind(); ::glPopMatrix(); + model_ubs.fog_density = fogdensity; + model_ubo->update(model_ubs); m_sunlight.apply_angle(); m_sunlight.apply_intensity(); @@ -2277,8 +2287,9 @@ bool opengl46_renderer::Render(world_environment *Environment) // turn on moon shadows after nautical twilight, if the moon is actually up m_shadowcolor = colors::shadow; } - // soften shadows depending on sky overcast factor - m_shadowcolor = glm::min(colors::white, m_shadowcolor + ((colors::white - colors::shadow) * Global.Overcast)); + // soften shadows depending on sky overcast factor, unless the shadows of the clouds hold back the sun where they fall + if (false == m_weather.cloud_shadows()) + m_shadowcolor = glm::min(colors::white, m_shadowcolor + ((colors::white - colors::shadow) * Global.Overcast)); setup_shadow_color( m_shadowcolor); @@ -6522,8 +6533,9 @@ void opengl46_renderer::Update_Lights(light_array &Lights, bool const Headlights light_data.fog_color = m_fogcolor; if (Global.fFogEnd > 0) { - m_fogrange = Global.fFogEnd / std::max(1.f, Global.Overcast * 2.f); - model_ubs.fog_density = 1.0f / m_fogrange; + // the fog of the weather; the regular density is a reference the shading scales the fog of a draw with + m_fogrange = m_weather.fog_range(); + model_ubs.fog_density = m_weather.fog_density(); } else { diff --git a/rendering/opengl46/opengl46renderer.h b/rendering/opengl46/opengl46renderer.h index aa372fc2..ebc833d3 100644 --- a/rendering/opengl46/opengl46renderer.h +++ b/rendering/opengl46/opengl46renderer.h @@ -33,6 +33,7 @@ http://mozilla.org/MPL/2.0/. #include "gl/query.h" #include "rendering/opengl46/opengl46terrain.h" #include "rendering/opengl46/opengl46sky.h" +#include "rendering/opengl46/opengl46weather.h" #include "rendering/opengl46/opengl46exposure.h" #include "rendering/opengl46/opengl46ao.h" #include "rendering/opengl46/opengl46locallights.h" @@ -431,6 +432,8 @@ class opengl46_renderer : public gfx_renderer { glm::vec3 m_fogcolor{0.f}; // light of the sky: directional ambient light, atmosphere, prefiltered environment map opengl46_sky m_sky; + // fog, clouds and their shadows, stars + opengl46_weather m_weather; // automatic exposure and bloom of the post processing opengl46_exposure m_exposure; // screen space ambient occlusion of the post processing diff --git a/rendering/opengl46/opengl46sky.cpp b/rendering/opengl46/opengl46sky.cpp index 33b63ffa..c4608493 100644 --- a/rendering/opengl46/opengl46sky.cpp +++ b/rendering/opengl46/opengl46sky.cpp @@ -306,7 +306,9 @@ void opengl46_sky::update(opengl46_light &Sunlight, glm::vec3 &Fogcolor, CSkyDom m_white_balance = balance / std::max(luminance(balance), 1e-4f); // the atmosphere has no clouds: the overcast takes away the sunlight, greys the sky and evens out its light - auto const sun{glm::vec3{result.sun_transmittance} * m_white_balance * (ATMOSPHERE_SUN_SCALE * Global.gfx_atmosphere_sun) * (1.f - 0.65f * m_overcast)}; + // with the shadows of the clouds the sun is held back where they are, not everywhere + auto const overcastsun{m_cloud_shadows ? 1.f : (1.f - 0.65f * m_overcast)}; + auto const sun{glm::vec3{result.sun_transmittance} * m_white_balance * (ATMOSPHERE_SUN_SCALE * Global.gfx_atmosphere_sun) * overcastsun}; auto const ambientscale{ATMOSPHERE_AMBIENT_SCALE * Global.gfx_atmosphere_ambient}; auto const ambient{desaturate(glm::vec3{result.radiance_sh[0]} * SH_Y00 * m_white_balance * ambientscale, 0.5f * m_overcast)}; m_sky_scale = ATMOSPHERE_SKY_SCALE * Global.gfx_atmosphere_sky; @@ -349,7 +351,8 @@ void opengl46_sky::update(opengl46_light &Sunlight, glm::vec3 &Fogcolor, CSkyDom void opengl46_sky::setup_sky(gl::model_ubs &Model) const { glBindTextureUnit(gl::SKY_VIEW_TEX, m_skyview_table); - Model.param[0] = glm::vec4{m_weight, m_sky_scale, 0.75f * m_overcast, m_sky_flat_luminance}; + // the volumetric clouds make the overcast sky themselves + Model.param[0] = glm::vec4{m_weight, m_sky_scale, m_clouds ? 0.f : 0.75f * m_overcast, m_sky_flat_luminance}; Model.param[1] = glm::vec4{m_white_balance, 0.f}; } diff --git a/rendering/opengl46/opengl46sky.h b/rendering/opengl46/opengl46sky.h index 73fc3b92..ef74d386 100644 --- a/rendering/opengl46/opengl46sky.h +++ b/rendering/opengl46/opengl46sky.h @@ -54,6 +54,31 @@ class opengl46_sky { return m_weight; } + // the sky-view table of the atmosphere, its white balance, brightness and the overcast it was drawn with, for the colours of + // the fog (opengl46_weather) + GLuint skyview_table() const + { + return m_skyview_table; + } + glm::vec3 const &white_balance() const + { + return m_white_balance; + } + float sky_scale() const + { + return m_sky_scale; + } + float overcast() const + { + return m_overcast; + } + // the volumetric clouds are drawn over the sky (it doesn't take the overcast look then), and their shadows hold back the + // sunlight (it isn't dimmed by the overcast then) + void set_clouds(bool const Clouds, bool const Shadows) + { + m_clouds = Clouds; + m_cloud_shadows = Shadows; + } // sets the shader parameters of the sky drawn with the atmosphere (sky_atmosphere.frag) and binds its table void setup_sky(gl::model_ubs &Model) const; // colour of the sun disc, the old one blended with the colour the atmosphere gives it @@ -110,6 +135,8 @@ class opengl46_sky glm::vec3 m_white_balance{1.f}; float m_sky_scale{0.f}; float m_overcast{0.f}; + bool m_clouds{false}; + bool m_cloud_shadows{false}; float m_sky_flat_luminance{0.f}; glm::vec3 m_sun_disc_color{1.f}; glm::vec3 m_sunlight{0.f}; diff --git a/rendering/opengl46/opengl46weather.cpp b/rendering/opengl46/opengl46weather.cpp new file mode 100644 index 00000000..22540e7b --- /dev/null +++ b/rendering/opengl46/opengl46weather.cpp @@ -0,0 +1,687 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "rendering/opengl46/opengl46weather.h" + +#include "rendering/opengl46/opengl46sky.h" +#include "environment/starcatalog.h" +#include "environment/weather.h" +#include "simulation/simulationenvironment.h" +#include "model/Texture.h" +#include "utilities/Globals.h" +#include "utilities/Timer.h" +#include "utilities/utilities.h" +#include "widgets/weatherpanel.h" + +namespace +{ + +// sizes of the noises (clouds_noise.comp) +int const SHAPE_NOISE_SIZE{128}; +int const DETAIL_NOISE_SIZE{32}; +int const COVERAGE_NOISE_SIZE{512}; +// how many times the cell of a genus fits in the tile of the shape noise, and its spread in the tile of the coverage noise +double const SHAPE_TILE_CELLS{4.0}; +double const COVERAGE_TILE_SPREADS{4.0}; +// size of the patches of the ground fog +float const FOG_PATCH_SIZE{300.f}; +// the haze the atmosphere draws in the sky already, as an extinction: about the haze of a day with 30 km of visibility +float const SKY_HAZE_EXTINCTION{3.f / 30000.f}; +// near end of the fog volume (view depth, metres) +float const FOG_VOLUME_NEAR{0.5f}; +// bands the panorama is drawn in, one a frame +int const PANORAMA_BANDS{8}; +// brightness of a star of magnitude 0 in the units of the scene +float const STAR_BRIGHTNESS{4.f}; +// stars dimmer than this aren't drawn +float const STAR_LIMIT{6.5f}; + +float luminance(glm::vec3 const &Color) +{ + return glm::dot(Color, glm::vec3{0.2126f, 0.7152f, 0.0722f}); +} + +// the position in a tiling noise, of a period in metres, kept small for the single precision of the shaders +double wrap(double const Value, double const Period) +{ + return Value - Period * std::floor(Value / Period); +} + +// the colour of a star of a colour temperature, linear, of luminance 1, somewhat paler than the black body: the eye sees most +// stars white +glm::vec3 star_color(float const Temperature) +{ + auto const t{std::clamp(Temperature, 1500.f, 40000.f) / 100.f}; + glm::vec3 color; + color.r = (t <= 66.f) ? 255.f : 329.698727446f * std::pow(t - 60.f, -0.1332047592f); + color.g = (t <= 66.f) ? 99.4708025861f * std::log(t) - 161.1195681661f : 288.1221695283f * std::pow(t - 60.f, -0.0755148492f); + color.b = (t >= 66.f) ? 255.f : (t <= 19.f) ? 0.f : 138.5177312231f * std::log(t - 10.f) - 305.0447927307f; + color = glm::pow(glm::clamp(color / 255.f, glm::vec3{0.f}, glm::vec3{1.f}), glm::vec3{2.2f}); + color /= std::max(luminance(color), 1e-3f); + return glm::mix(color, glm::vec3{1.f}, 0.5f); +} + +std::unique_ptr make_compute(std::string const &Name) +{ + gl::shader shader(Name); + return std::make_unique(std::vector>{shader}); +} + +std::unique_ptr make_program(std::string const &Vertex, std::string const &Fragment) +{ + gl::shader vertex(Vertex); + gl::shader fragment(Fragment); + return std::make_unique(std::vector>{vertex, fragment}); +} + +GLuint groups(int const Size) +{ + return static_cast((Size + static_cast(gl::WEATHER_GROUP_SIZE) - 1) / static_cast(gl::WEATHER_GROUP_SIZE)); +} + +} // namespace + +opengl46_weather::~opengl46_weather() +{ + for (auto *texture : {&m_shape_noise, &m_detail_noise, &m_coverage_noise, &m_shadow_map, &m_clouds_color, &m_clouds_depth, &m_clouds_history[0], &m_clouds_history[1], &m_panorama, + &m_fog_scattering[0], &m_fog_scattering[1], &m_fog_integrated}) + release(*texture); +} + +void opengl46_weather::release(GLuint &Texture) +{ + if (Texture != 0) + glDeleteTextures(1, &Texture); + Texture = 0; +} + +GLuint opengl46_weather::create_texture(GLenum const Target, GLenum const Format, glm::ivec3 const &Size, GLint const Filter, GLint const Wrap) +{ + GLuint texture{0}; + glCreateTextures(Target, 1, &texture); + if (Target == GL_TEXTURE_3D) + glTextureStorage3D(texture, 1, Format, Size.x, Size.y, Size.z); + else + glTextureStorage2D(texture, 1, Format, Size.x, Size.y); + glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, Filter); + glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, Filter); + glTextureParameteri(texture, GL_TEXTURE_WRAP_S, Wrap); + glTextureParameteri(texture, GL_TEXTURE_WRAP_T, Wrap); + glTextureParameteri(texture, GL_TEXTURE_WRAP_R, Wrap); + return texture; +} + +opengl46_weather::quality_level opengl46_weather::quality(int const Clouds, int const Fog) +{ + quality_level level; + switch (std::clamp(Clouds, 1, 4)) + { + case 1: level = {4, 32, 3, false, {512, 128}, {}}; break; + case 2: level = {2, 48, 4, true, {1024, 256}, {}}; break; + case 3: level = {2, 80, 6, true, {1024, 256}, {}}; break; + default: level = {2, 128, 8, true, {2048, 512}, {}}; break; + } + switch (std::clamp(Fog, 1, 4)) + { + case 1: level.fog_grid = {80, 45, 48}; break; + case 2: level.fog_grid = {160, 90, 64}; break; + case 3: level.fog_grid = {160, 90, 96}; break; + default: level.fog_grid = {240, 135, 128}; break; + } + return level; +} + +void opengl46_weather::init() +{ + m_noise_program = make_compute("clouds_noise.comp"); + m_march_program = make_compute("clouds_march.comp"); + m_temporal_program = make_compute("clouds_temporal.comp"); + m_shadow_program = make_compute("clouds_shadow.comp"); + m_horizon_program = make_compute("fog_horizon.comp"); + m_inject_program = make_compute("fog_inject.comp"); + m_integrate_program = make_compute("fog_integrate.comp"); + m_sky_program = make_program("quad.vert", "sky_weather.frag"); + m_stars_program = make_program("stars.vert", "stars.frag"); + + // a grey fog and no clouds until the first update + for (auto &color : m_weather.fog_horizon) + color = glm::vec4{0.5f, 0.5f, 0.5f, 1.f}; + m_weather.fog_haze = glm::vec4{3.f / 10000.f, 1.f / 800.f, SKY_HAZE_EXTINCTION, 1.f}; + m_weather.fog_light = glm::vec4{0.f, 1.f, 0.6f, 1.f / 5000.f}; + m_weather_ubo = std::make_unique(sizeof(gl::weather_ubs), gl::WEATHER_UBO_BINDING); + m_weather_ubo->update(m_weather); + + make_noise(); + + // the stars, in the equatorial frame + std::vector stars; + auto const *catalogue{star_catalogue::stars()}; + for (std::size_t i = 0; i < star_catalogue::size(); ++i) + { + auto const &star{catalogue[i]}; + auto const magnitude{static_cast(star.magnitude) * 0.01f}; + if (magnitude > STAR_LIMIT) + continue; + auto const ascension{glm::radians(static_cast(star.right_ascension) * 0.01f)}; + auto const declination{glm::radians(static_cast(star.declination) * 0.01f)}; + stars.emplace_back(std::cos(declination) * std::cos(ascension), std::cos(declination) * std::sin(ascension), std::sin(declination), magnitude); + // a phase of the twinkling which doesn't repeat from star to star + auto const phase{std::fmod(static_cast(i) * 0.6180339887f, 1.f)}; + stars.emplace_back(star_color(static_cast(star.temperature) * 10.f), phase); + } + m_stars_count = static_cast(stars.size() / 2); + m_stars_vao.emplace(); + m_stars_vao->bind(); + m_stars_buffer.emplace(); + m_stars_buffer->allocate(gl::buffer::ARRAY_BUFFER, static_cast(stars.size() * sizeof(glm::vec4)), GL_STATIC_DRAW); + m_stars_buffer->upload(gl::buffer::ARRAY_BUFFER, stars.data(), 0, static_cast(stars.size() * sizeof(glm::vec4))); + m_stars_vao->setup_attrib(*m_stars_buffer, 0, 4, GL_FLOAT, 2 * sizeof(glm::vec4), 0); + m_stars_vao->setup_attrib(*m_stars_buffer, 1, 4, GL_FLOAT, 2 * sizeof(glm::vec4), sizeof(glm::vec4)); + gl::vao::unbind(); +} + +void opengl46_weather::make_noise() +{ + auto const mipmapped = [](GLenum const Target, GLenum const Format, glm::ivec3 const &Size) { + GLuint texture{0}; + glCreateTextures(Target, 1, &texture); + auto const levels{static_cast(std::floor(std::log2(static_cast(std::max(Size.x, Size.y))))) + 1}; + if (Target == GL_TEXTURE_3D) + glTextureStorage3D(texture, levels, Format, Size.x, Size.y, Size.z); + else + glTextureStorage2D(texture, levels, Format, Size.x, Size.y); + glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTextureParameteri(texture, GL_TEXTURE_WRAP_R, GL_REPEAT); + return texture; + }; + m_shape_noise = mipmapped(GL_TEXTURE_3D, GL_RGBA8, glm::ivec3{SHAPE_NOISE_SIZE}); + m_detail_noise = mipmapped(GL_TEXTURE_3D, GL_RGBA8, glm::ivec3{DETAIL_NOISE_SIZE}); + m_coverage_noise = mipmapped(GL_TEXTURE_2D, GL_RGBA8, glm::ivec3{COVERAGE_NOISE_SIZE, COVERAGE_NOISE_SIZE, 1}); + + m_noise_program->bind(); + auto const make = [&](int const Kind, GLuint const Volume, int const Size) { + glBindImageTexture(0, Volume, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8); + glBindImageTexture(1, m_coverage_noise, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + glProgramUniform1i(*m_noise_program, 0, Kind); + glProgramUniform1i(*m_noise_program, 1, Size); + m_noise_program->dispatch(groups(Size), groups(Size), Kind == 2 ? 1 : static_cast(Size)); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + }; + make(0, m_shape_noise, SHAPE_NOISE_SIZE); + make(1, m_detail_noise, DETAIL_NOISE_SIZE); + make(2, m_shape_noise, COVERAGE_NOISE_SIZE); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + gl::program::unbind(); + glGenerateTextureMipmap(m_shape_noise); + glGenerateTextureMipmap(m_detail_noise); + glGenerateTextureMipmap(m_coverage_noise); +} + +void opengl46_weather::bind() +{ + m_weather_ubo->bind_uniform(); + if (m_shadow_map != 0) + glBindTextureUnit(gl::CLOUD_SHADOW_TEX, m_shadow_map); + if (m_fog_integrated != 0) + glBindTextureUnit(gl::FOG_VOLUME_TEX, m_fog_integrated); +} + +void opengl46_weather::bind_noise() const +{ + glBindTextureUnit(gl::CLOUD_SHAPE_TEX, m_shape_noise); + glBindTextureUnit(gl::CLOUD_DETAIL_TEX, m_detail_noise); + glBindTextureUnit(gl::CLOUD_COVERAGE_TEX, m_coverage_noise); + if (m_shadow_map != 0) + glBindTextureUnit(gl::CLOUD_SHADOW_TEX, m_shadow_map); +} + +void opengl46_weather::upload_header() +{ + // the colours of the horizon are left alone when the gpu writes them + auto const size{m_horizon_on_gpu ? offsetof(gl::weather_ubs, fog_horizon) : sizeof(gl::weather_ubs)}; + m_weather_ubo->update(reinterpret_cast(&m_weather), 0, static_cast(size)); + m_weather_ubo->bind_uniform(); +} + +void opengl46_weather::update(opengl46_sky const &Sky, glm::vec3 const &Keylight, glm::vec3 const &Keycolor, glm::vec3 const &Ambient, glm::vec3 const &Fogcolor, glm::dvec3 const &Camera) +{ + auto const &weather{simulation::Environment.weather()}; + auto const &conditions{weather.conditions()}; + m_fog_volume_valid = false; + m_clouds_view_valid = false; + ++m_frame; + + auto const height{static_cast(Camera.y) - weather.ground()}; + auto const time{weather.time()}; + auto const sun{glm::normalize(Keylight)}; + + // the fog. the visibility is where a dark object fades to 5%: the extinction is 3 over it + auto const haze{3.f / conditions.visibility}; + auto const hazerate{1.f / conditions.haze_height}; + m_weather.fog_haze = glm::vec4{haze, hazerate, SKY_HAZE_EXTINCTION, 1.f}; + if (conditions.groundfog_thickness > 0.f) + { + auto const thickness{conditions.groundfog_thickness}; + m_weather.fog_ground = glm::vec4{std::max(3.f / conditions.groundfog_visibility - haze, 0.f), 1.f / (0.25f * thickness), 0.75f * thickness, conditions.fog_noise}; + } + else + m_weather.fog_ground = glm::vec4{0.f, 1.f, 0.f, conditions.fog_noise}; + // the patches drift with the wind, slower than the clouds + auto const drift{glm::dvec2{weather.cloud_offset()} * 0.3}; + auto const patch{static_cast(FOG_PATCH_SIZE)}; + m_weather.fog_noise = glm::vec4{static_cast(wrap(Camera.x - drift.x, patch)), static_cast(wrap(Camera.y, patch)), static_cast(wrap(Camera.z - drift.y, patch)), 1.f / FOG_PATCH_SIZE}; + // the extinction at the height of the camera tells how far things are hidden + auto const extinction{haze * std::exp(-hazerate * std::max(height, 0.f)) + + m_weather.fog_ground.x * std::exp(-m_weather.fog_ground.y * std::max(height - m_weather.fog_ground.z, 0.f))}; + // 2% left at 1.3 visibilities; the draw range is twice this + m_fog_range = std::clamp(3.f / std::max(extinction, 1e-7f) / 1.5f, 5.f, 1e6f); + + // the share of the light of the fog the sun gives, which the shadows hold back + auto const sunlight{luminance(Keycolor) * 1.5f * std::max(sun.y, 0.f)}; + auto const skylight{luminance(Ambient) * 0.65f}; + m_sun_share = (sunlight + skylight > 1e-6f) ? sunlight / (sunlight + skylight) : 0.f; + + // clouds + auto const layers{std::min(conditions.clouds.size(), gl::MAX_CLOUD_LAYERS)}; + m_clouds_drawn = (Global.gfx_clouds > 0) && (layers > 0); + m_average_transmittance = 1.f; + auto const velocity{weather.cloud_velocity()}; + auto const speed{glm::length(velocity)}; + auto const wind{speed > 1e-3f ? glm::dvec2{velocity / speed} : glm::dvec2{1.0, 0.0}}; + m_weather.cloud_wind = glm::vec4{static_cast(wind.x), static_cast(wind.y), speed, 0.f}; + for (std::size_t i = 0; i < gl::MAX_CLOUD_LAYERS; ++i) + { + auto &target{m_weather.cloud_layers[i]}; + if (i >= layers) + { + target = gl::cloud_layer_ubs{}; + continue; + } + auto const &layer{conditions.clouds[i]}; + auto const &genus{cloud_genus_info(layer.type)}; + target.heights = glm::vec4{layer.base, layer.top, 1.f / std::max(layer.top - layer.base, 1.f), static_cast(layer.type)}; + auto const shapetile{static_cast(genus.cell) * SHAPE_TILE_CELLS}; + auto const coveragetile{static_cast(genus.spread) * COVERAGE_TILE_SPREADS}; + target.shape = glm::vec4{layer.coverage, genus.extinction * layer.density, static_cast(1.0 / shapetile), genus.erosion}; + target.profile = glm::vec4{genus.bottom, genus.top, genus.top_variation, genus.anvil}; + // the higher, the faster the wind carries them + auto const offset{glm::dvec2{weather.cloud_offset()} * (1.0 + static_cast(layer.base) / 8000.0)}; + // the camera's place in the noise, in the space stretched along the wind as the shaders take it + auto const place{glm::dvec2{Camera.x, Camera.z} - offset}; + auto const along{glm::dot(place, wind)}; + auto const stretched{(place - wind * along) + wind * (along / std::max(static_cast(genus.stretch), 1.0))}; + target.motion = glm::vec4{static_cast(wrap(stretched.x, shapetile)), static_cast(wrap(stretched.y, shapetile)), static_cast(1.0 / coveragetile), genus.stretch}; + // the clouds change their shapes slowly; the seed moves the layers apart in the noise + auto const evolution{std::fmod(static_cast(conditions.seed) * 0.6180339887 + static_cast(i) * 0.37 + time * 0.00002, 1.0)}; + target.extra = glm::vec4{genus.flat ? 1.f : 0.f, static_cast(evolution), static_cast(wrap(stretched.x, coveragetile)), static_cast(wrap(stretched.y, coveragetile))}; + m_average_transmittance *= 1.f - std::clamp(layer.coverage * genus.opacity * std::min(layer.density, 1.5f), 0.f, 1.f); + } + if (Global.gfx_clouds <= 0) + m_average_transmittance = 1.f; + m_weather.fog_light = glm::vec4{m_sun_share, m_average_transmittance, 0.6f, fog_density()}; + m_weather.weather_camera = glm::vec4{height, static_cast(std::fmod(time, 100000.0)), m_clouds_drawn ? 1.f : 0.f, m_clouds_drawn ? static_cast(layers) : 0.f}; + m_weather.weather_sun_direction = glm::vec4{sun, sun.y > -0.02f ? 1.f : 0.f}; + m_weather.weather_sun_color = glm::vec4{Keycolor, 0.f}; + m_weather.weather_ambient = glm::vec4{Ambient * 1.3f, 0.f}; + m_weather.fog_volume_params = glm::vec4{0.f, FOG_VOLUME_NEAR, 1.f, 1.f}; + m_weather.fog_volume_target = glm::vec4{1.f, 1.f, Global.gfx_fog_range, 1.f - Sky.atmosphere_weight()}; + + // the colours of the horizon: from the atmosphere on the gpu, the old fog colour otherwise + m_horizon_on_gpu = (Sky.atmosphere_weight() > 0.f) && (Sky.skyview_table() != 0); + if (false == m_horizon_on_gpu) + { + for (auto &color : m_weather.fog_horizon) + color = glm::vec4{Fogcolor, 1.f}; + } + + // the shadows of the clouds, on a map snapped to its texels so it doesn't crawl as the camera moves + auto const shadows{m_clouds_drawn && Global.gfx_clouds_shadows && (sun.y > 0.02f)}; + m_weather.cloud_shadow_params = glm::vec4{0.f}; + if (shadows) + { + if (m_shadow_size != Global.gfx_clouds_shadows_size) + { + release(m_shadow_map); + m_shadow_size = Global.gfx_clouds_shadows_size; + m_shadow_map = create_texture(GL_TEXTURE_2D, GL_R16F, glm::ivec3{m_shadow_size, m_shadow_size, 1}, GL_LINEAR, GL_CLAMP_TO_EDGE); + } + auto const extent{static_cast(Global.gfx_clouds_shadows_range)}; + auto const texel{extent / static_cast(m_shadow_size)}; + auto const centre{glm::dvec2{std::round(Camera.x / texel) * texel - Camera.x, std::round(Camera.z / texel) * texel - Camera.z}}; + m_weather.cloud_shadow_params = glm::vec4{static_cast(centre.x), static_cast(centre.y), static_cast(1.0 / extent), 0.f}; + } + upload_header(); + + if (m_horizon_on_gpu) + { + glBindTextureUnit(gl::SKY_VIEW_TEX, Sky.skyview_table()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, gl::WEATHER_SCRATCH_SSBO_BINDING, *m_weather_ubo); + glProgramUniform1i(*m_horizon_program, 0, static_cast(offsetof(gl::weather_ubs, fog_horizon) / sizeof(glm::vec4))); + glProgramUniform4f(*m_horizon_program, 1, Sky.atmosphere_weight(), Sky.sky_scale(), 0.5f * Sky.overcast(), 64.f); + auto const &balance{Sky.white_balance()}; + glProgramUniform3f(*m_horizon_program, 2, balance.r, balance.g, balance.b); + glProgramUniform3f(*m_horizon_program, 3, Fogcolor.r, Fogcolor.g, Fogcolor.b); + m_horizon_program->dispatch(1); + glMemoryBarrier(GL_UNIFORM_BARRIER_BIT); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, gl::WEATHER_SCRATCH_SSBO_BINDING, 0); + } + + if (shadows) + { + m_shadow_timer.begin(); + bind_noise(); + glBindImageTexture(0, m_shadow_map, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R16F); + glProgramUniform1i(*m_shadow_program, 1, m_shadow_size); + glProgramUniform3f(*m_shadow_program, 2, m_weather.cloud_shadow_params.x, m_weather.cloud_shadow_params.y, Global.gfx_clouds_shadows_range); + glProgramUniform1i(*m_shadow_program, 3, 8); + m_shadow_program->dispatch(groups(m_shadow_size), groups(m_shadow_size), 1); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R16F); + m_shadow_timer.end(); + m_shadows_valid = true; + m_weather.cloud_shadow_params.w = 1.f; + upload_header(); + } + else + m_shadows_valid = false; + + // a band of the panorama of the clouds, all of it the first time + if (m_clouds_drawn) + { + auto const level{quality(Global.gfx_clouds, Global.gfx_fog_quality)}; + if (m_panorama_size != level.panorama) + { + release(m_panorama); + m_panorama_size = level.panorama; + m_panorama = create_texture(GL_TEXTURE_2D, GL_RGBA16F, glm::ivec3{m_panorama_size, 1}, GL_LINEAR, GL_CLAMP_TO_EDGE); + glTextureParameteri(m_panorama, GL_TEXTURE_WRAP_S, GL_REPEAT); + m_panorama_full = false; + m_panorama_band = 0; + } + auto const bandrows{(m_panorama_size.y + PANORAMA_BANDS - 1) / PANORAMA_BANDS}; + auto const first{m_panorama_full ? m_panorama_band * bandrows : 0}; + auto const rows{m_panorama_full ? bandrows : m_panorama_size.y}; + m_panorama_timer.begin(); + bind_noise(); + glBindImageTexture(0, m_panorama, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + // the depth isn't written in this mode, but something has to be bound + glBindImageTexture(1, m_panorama, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glProgramUniform1i(*m_march_program, 0, 1); + glProgramUniform2i(*m_march_program, 1, m_panorama_size.x, m_panorama_size.y); + glProgramUniform1i(*m_march_program, 4, m_frame); + // the panorama is seen blurred: fewer samples + glProgramUniform4f(*m_march_program, 5, static_cast(std::max(level.steps / 2, 24)), static_cast(std::max(level.light_steps - 1, 2)), Global.gfx_clouds_distance, 0.f); + glProgramUniform2i(*m_march_program, 6, first, rows); + m_march_program->dispatch(groups(m_panorama_size.x), groups(rows), 1); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + m_panorama_timer.end(); + m_panorama_band = m_panorama_full ? (m_panorama_band + 1) % PANORAMA_BANDS : 0; + m_panorama_full = true; + } + gl::program::unbind(); + m_report = to_metar(conditions); +} + +void opengl46_weather::prepare_view(glm::ivec2 const &Size, quality_level const &Level) +{ + auto const cloudsize{glm::max((Size + Level.divisor - 1) / Level.divisor, glm::ivec2{1})}; + if (m_clouds_drawn && (cloudsize != m_view_size || m_clouds_color == 0)) + { + for (auto *texture : {&m_clouds_color, &m_clouds_depth, &m_clouds_history[0], &m_clouds_history[1]}) + release(*texture); + m_view_size = cloudsize; + m_clouds_color = create_texture(GL_TEXTURE_2D, GL_RGBA16F, glm::ivec3{m_view_size, 1}, GL_LINEAR, GL_CLAMP_TO_EDGE); + m_clouds_depth = create_texture(GL_TEXTURE_2D, GL_R32F, glm::ivec3{m_view_size, 1}, GL_NEAREST, GL_CLAMP_TO_EDGE); + for (auto &history : m_clouds_history) + history = create_texture(GL_TEXTURE_2D, GL_RGBA16F, glm::ivec3{m_view_size, 1}, GL_LINEAR, GL_CLAMP_TO_EDGE); + m_previous_clouds.valid = false; + } + if ((Global.gfx_fog == 1) && (Level.fog_grid != m_fog_grid || m_fog_integrated == 0)) + { + for (auto *texture : {&m_fog_scattering[0], &m_fog_scattering[1], &m_fog_integrated}) + release(*texture); + m_fog_grid = Level.fog_grid; + for (auto &scattering : m_fog_scattering) + scattering = create_texture(GL_TEXTURE_3D, GL_RGBA16F, m_fog_grid, GL_LINEAR, GL_CLAMP_TO_EDGE); + m_fog_integrated = create_texture(GL_TEXTURE_3D, GL_RGBA16F, m_fog_grid, GL_LINEAR, GL_CLAMP_TO_EDGE); + m_previous_fog.valid = false; + } + m_view_level = Level; +} + +void opengl46_weather::render_view(glm::mat4 const &Projection, glm::mat4 const &View, glm::dvec3 const &Camera, glm::ivec2 const &Size, int const Cablamp) +{ + auto const level{quality(Global.gfx_clouds, Global.gfx_fog_quality)}; + prepare_view(Size, level); + auto const rotation{glm::mat4{glm::mat3{View}}}; + auto const inverseprojection{glm::inverse(Projection)}; + auto const viewtoworld{glm::transpose(rotation)}; + auto const projectionview{Projection * rotation}; + // a jump of the camera (another vehicle, the free camera) leaves nothing to blend with + auto const continuity = [&](view_state const &Previous) { return Previous.valid && glm::length(Previous.camera - Camera) < 50.0; }; + + if (m_clouds_drawn) + { + m_clouds_timer.begin(); + bind_noise(); + glBindImageTexture(0, m_clouds_color, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindImageTexture(1, m_clouds_depth, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glProgramUniform1i(*m_march_program, 0, 0); + glProgramUniform2i(*m_march_program, 1, m_view_size.x, m_view_size.y); + glProgramUniformMatrix4fv(*m_march_program, 2, 1, GL_FALSE, glm::value_ptr(inverseprojection)); + glProgramUniformMatrix4fv(*m_march_program, 3, 1, GL_FALSE, glm::value_ptr(viewtoworld)); + glProgramUniform1i(*m_march_program, 4, m_frame); + glProgramUniform4f(*m_march_program, 5, static_cast(level.steps), static_cast(level.light_steps), Global.gfx_clouds_distance, level.detail ? 1.f : 0.f); + glProgramUniform2i(*m_march_program, 6, 0, m_view_size.y); + m_march_program->dispatch(groups(m_view_size.x), groups(m_view_size.y), 1); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + auto const next{1 - m_clouds_current}; + glBindTextureUnit(gl::CLOUD_TEX, m_clouds_color); + glBindTextureUnit(gl::CLOUD_SHAPE_TEX, m_clouds_depth); + glBindTextureUnit(gl::CLOUD_HISTORY_TEX, m_clouds_history[m_clouds_current]); + glBindImageTexture(0, m_clouds_history[next], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glProgramUniform2i(*m_temporal_program, 1, m_view_size.x, m_view_size.y); + glProgramUniformMatrix4fv(*m_temporal_program, 2, 1, GL_FALSE, glm::value_ptr(inverseprojection)); + glProgramUniformMatrix4fv(*m_temporal_program, 3, 1, GL_FALSE, glm::value_ptr(viewtoworld)); + glProgramUniformMatrix4fv(*m_temporal_program, 4, 1, GL_FALSE, glm::value_ptr(m_previous_clouds.projection_view)); + auto const previous{glm::vec3{m_previous_clouds.camera - Camera}}; + glProgramUniform3f(*m_temporal_program, 5, previous.x, previous.y, previous.z); + glProgramUniform1f(*m_temporal_program, 6, continuity(m_previous_clouds) ? 0.15f : 1.f); + m_temporal_program->dispatch(groups(m_view_size.x), groups(m_view_size.y), 1); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + // the shape noise back where the clouds of the fog read it + glBindTextureUnit(gl::CLOUD_SHAPE_TEX, m_shape_noise); + m_clouds_current = next; + m_clouds_timer.end(); + m_previous_clouds = {projectionview, Camera, {}, true}; + m_clouds_view_valid = true; + } + else + m_previous_clouds.valid = false; + + if (Global.gfx_fog == 1) + { + m_fog_timer.begin(); + auto const farthest{std::max(Global.gfx_fog_range, FOG_VOLUME_NEAR * 4.f)}; + // the cells are sampled at another depth in every frame, in the Halton sequence of 2 + auto const jitter{[](int Frame) { + float value{0.f}, fraction{0.5f}; + for (Frame = (Frame % 64) + 1; Frame > 0; Frame /= 2, fraction *= 0.5f) + value += fraction * static_cast(Frame % 2); + return value; + }(m_frame)}; + auto const temporal{Global.gfx_fog_temporal && continuity(m_previous_fog)}; + glm::vec4 const slicing{FOG_VOLUME_NEAR, farthest, static_cast(m_fog_grid.z) / std::log(farthest / FOG_VOLUME_NEAR), temporal ? jitter : 0.5f}; + bind_noise(); + auto const next{1 - m_fog_current}; + glBindImageTexture(0, m_fog_scattering[next], 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindTextureUnit(gl::CLOUD_HISTORY_TEX, m_fog_scattering[m_fog_current]); + glProgramUniform3i(*m_inject_program, 0, m_fog_grid.x, m_fog_grid.y, m_fog_grid.z); + glProgramUniformMatrix4fv(*m_inject_program, 1, 1, GL_FALSE, glm::value_ptr(inverseprojection)); + glProgramUniformMatrix4fv(*m_inject_program, 2, 1, GL_FALSE, glm::value_ptr(viewtoworld)); + glProgramUniform4fv(*m_inject_program, 3, 1, glm::value_ptr(slicing)); + glProgramUniformMatrix4fv(*m_inject_program, 4, 1, GL_FALSE, glm::value_ptr(m_previous_fog.projection_view)); + auto const previous{glm::vec3{m_previous_fog.camera - Camera}}; + glProgramUniform3f(*m_inject_program, 5, previous.x, previous.y, previous.z); + auto const sunshadows{Global.gfx_fog_shadows && Global.gfx_shadowmap_enabled}; + glProgramUniform4f(*m_inject_program, 6, temporal ? 0.25f : 1.f, Global.gfx_fog_lights, sunshadows ? 1.f : 0.f, Global.gfx_fog_shadows ? 1.f : 0.f); + glProgramUniform1i(*m_inject_program, 7, Cablamp); + glProgramUniform2f(*m_inject_program, 8, m_previous_fog.slicing.x, m_previous_fog.slicing.z); + m_inject_program->dispatch(groups(m_fog_grid.x), groups(m_fog_grid.y), static_cast(m_fog_grid.z)); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + glBindTextureUnit(gl::CLOUD_HISTORY_TEX, m_fog_scattering[next]); + glBindImageTexture(0, m_fog_integrated, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glProgramUniform3i(*m_integrate_program, 0, m_fog_grid.x, m_fog_grid.y, m_fog_grid.z); + glProgramUniformMatrix4fv(*m_integrate_program, 1, 1, GL_FALSE, glm::value_ptr(inverseprojection)); + glProgramUniform4fv(*m_integrate_program, 3, 1, glm::value_ptr(slicing)); + m_integrate_program->dispatch(groups(m_fog_grid.x), groups(m_fog_grid.y), 1); + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + m_fog_current = next; + m_fog_slicing = slicing; + m_fog_timer.end(); + m_previous_fog = {projectionview, Camera, slicing, true}; + m_fog_volume_valid = true; + } + else + m_previous_fog.valid = false; + gl::program::unbind(); +} + +void opengl46_weather::setup_pass(bool const Volume, glm::ivec2 const &Size) +{ + auto const volume{Volume && m_fog_volume_valid && (Global.gfx_fog == 1)}; + m_weather.fog_volume_params = volume ? glm::vec4{1.f, m_fog_slicing.x, m_fog_slicing.z, static_cast(m_fog_grid.z)} : glm::vec4{0.f, FOG_VOLUME_NEAR, 1.f, 1.f}; + m_weather.fog_volume_target.x = 1.f / static_cast(std::max(Size.x, 1)); + m_weather.fog_volume_target.y = 1.f / static_cast(std::max(Size.y, 1)); + m_weather.fog_volume_target.z = volume ? m_fog_slicing.y : Global.gfx_fog_range; + upload_header(); + bind(); +} + +void opengl46_weather::draw_stars(glm::mat4 const &View, float const Sunangle, double const Siderealtime, double const Latitude) +{ + if (!Global.gfx_stars || !m_stars_vao || m_stars_count == 0) + return; + // the stars come out between the civil and the nautical twilight + auto const night{1.f - glm::smoothstep(-12.f, -3.f, Sunangle)}; + if (night <= 0.f) + return; + // the equatorial frame (z to the pole, x to the vernal equinox) to the sky of the observer (x west, y up, z north) + auto const theta{glm::radians(Siderealtime)}; + auto const phi{glm::radians(Latitude)}; + auto const st{static_cast(std::sin(theta))}, ct{static_cast(std::cos(theta))}; + auto const sp{static_cast(std::sin(phi))}, cp{static_cast(std::cos(phi))}; + glm::mat3 equatorial; + equatorial[0] = glm::vec3{st, cp * ct, -sp * ct}; + equatorial[1] = glm::vec3{-ct, cp * st, -sp * st}; + equatorial[2] = glm::vec3{0.f, sp, cp}; + auto const toworld{glm::mat4{equatorial}}; + auto const toview{glm::mat4{glm::mat3{View}}}; + m_stars_program->bind(); + glProgramUniformMatrix4fv(*m_stars_program, 0, 1, GL_FALSE, glm::value_ptr(toworld)); + glProgramUniformMatrix4fv(*m_stars_program, 1, 1, GL_FALSE, glm::value_ptr(toview)); + glProgramUniform4f(*m_stars_program, 2, STAR_BRIGHTNESS * Global.gfx_stars_brightness * night, static_cast(std::fmod(Timer::GetTime(), 10000.0)), 2.5f, 0.f); + glEnable(GL_PROGRAM_POINT_SIZE); + glBlendFunc(GL_ONE, GL_ONE); + m_stars_vao->bind(); + glDrawArrays(GL_POINTS, 0, m_stars_count); + glDisable(GL_PROGRAM_POINT_SIZE); + gl::vao::unbind(); +} + +void opengl46_weather::draw_sky(glm::mat4 const &Projection, glm::mat4 const &View, bool const Mainview) +{ + int mode{0}; + if (m_clouds_drawn) + { + if (Mainview && m_clouds_view_valid) + { + mode = 1; + glBindTextureUnit(gl::CLOUD_TEX, m_clouds_history[m_clouds_current]); + } + else if (m_panorama != 0) + { + mode = 2; + glBindTextureUnit(gl::CLOUD_TEX, m_panorama); + } + } + auto const inverseprojection{glm::inverse(Projection)}; + auto const viewtoworld{glm::transpose(glm::mat4{glm::mat3{View}})}; + m_sky_program->bind(); + glProgramUniformMatrix4fv(*m_sky_program, 0, 1, GL_FALSE, glm::value_ptr(inverseprojection)); + glProgramUniformMatrix4fv(*m_sky_program, 1, 1, GL_FALSE, glm::value_ptr(viewtoworld)); + glProgramUniform1i(*m_sky_program, 2, mode); + + GLboolean depthmask; + glGetBooleanv(GL_DEPTH_WRITEMASK, &depthmask); + auto const depthtest{glIsEnabled(GL_DEPTH_TEST)}; + auto const cull{glIsEnabled(GL_CULL_FACE)}; + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_SRC_ALPHA); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(depthmask); + if (depthtest) + glEnable(GL_DEPTH_TEST); + if (cull) + glEnable(GL_CULL_FACE); +} + +std::string opengl46_weather::info_times() +{ + std::string text; + auto const line = [&text](char const *Name, gl::gpu_timer &Timer) { + if (auto const time = Timer.result()) + text += std::string{" "} + Name + ": " + to_string(static_cast(*time) / 1000000.0, 3) + " ms\n"; + }; + line("clouds", m_clouds_timer); + line("clouds panorama", m_panorama_timer); + line("cloud shadows", m_shadow_timer); + line("fog volume", m_fog_timer); + return text; +} + +void opengl46_weather::draw_debug_ui() +{ + char const *fogmodes[]{"analytic (along the ray, as the old fog)", "volumetric"}; + ImGui::Combo("fog (gfx.fog)", &Global.gfx_fog, fogmodes, IM_ARRAYSIZE(fogmodes)); + char const *levels[]{"low", "medium", "high", "ultra"}; + auto fogquality{std::clamp(Global.gfx_fog_quality, 1, 4) - 1}; + if (ImGui::Combo("fog volume quality (gfx.fog.quality)", &fogquality, levels, IM_ARRAYSIZE(levels))) + Global.gfx_fog_quality = fogquality + 1; + ImGui::SliderFloat("fog volume depth, m (gfx.fog.range)", &Global.gfx_fog_range, 100.f, 10000.f, "%.0f", ImGuiSliderFlags_Logarithmic); + ImGui::SliderFloat("lamps in the fog (gfx.fog.lights)", &Global.gfx_fog_lights, 0.f, 4.f); + ImGui::Checkbox("shadows of the sun and headlights in the fog (gfx.fog.shadows)", &Global.gfx_fog_shadows); + ImGui::Checkbox("fog blended over frames (gfx.fog.temporal)", &Global.gfx_fog_temporal); + char const *cloudlevels[]{"none", "low", "medium", "high", "ultra"}; + ImGui::Combo("clouds (gfx.clouds)", &Global.gfx_clouds, cloudlevels, IM_ARRAYSIZE(cloudlevels)); + ImGui::SliderFloat("cloud distance, m (gfx.clouds.distance)", &Global.gfx_clouds_distance, 5000.f, 200000.f, "%.0f", ImGuiSliderFlags_Logarithmic); + ImGui::Checkbox("cloud shadows (gfx.clouds.shadows)", &Global.gfx_clouds_shadows); + ImGui::SliderFloat("cloud shadow map, m (gfx.clouds.shadows.range)", &Global.gfx_clouds_shadows_range, 1000.f, 100000.f, "%.0f", ImGuiSliderFlags_Logarithmic); + ImGui::Checkbox("stars (gfx.stars)", &Global.gfx_stars); + ImGui::SliderFloat("star brightness", &Global.gfx_stars_brightness, 0.f, 8.f); + ImGui::Text("fog range %.0f m, sun share of the fog light %.2f, clouds let through %.2f on average", m_fog_range, m_sun_share, m_average_transmittance); + ImGui::Separator(); + ui::weather_controls(simulation::Environment.weather()); +} diff --git a/rendering/opengl46/opengl46weather.h b/rendering/opengl46/opengl46weather.h new file mode 100644 index 00000000..fd9c0b68 --- /dev/null +++ b/rendering/opengl46/opengl46weather.h @@ -0,0 +1,185 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include +#include +#include +#include + +#include "gl/ubo.h" +#include "gl/shader.h" +#include "gl/vao.h" +#include "gl/buffer.h" +#include "gl/query.h" + +class opengl46_sky; + +// the weather of the opengl 4.6 renderer, drawn from the weather of the simulation (environment/weather.h): +// - the fog, for the shading (apply_fog.glsl): in the colour pass of the main view a volume laid over the view (fog_inject.comp, +// fog_integrate.comp) with the shafts of the sun, the shadows of the clouds and the light of the lamps and headlights, and +// along the ray (weather_common.glsl) past it and in the other passes; gfx.fog analytic leaves the volume out. lit with the +// colour the atmosphere has towards the horizon (fog_horizon.comp), so far fog and the sky behind it meet +// - the volumetric clouds of the layers of the weather (clouds_common.glsl, clouds_march.comp): the main view at a part of the +// resolution, blended over frames (clouds_temporal.comp), and a panorama around the camera for the environment map and the +// other views; laid over the sky with the fog (sky_weather.frag) +// - the shadows of the clouds on the ground around the camera (clouds_shadow.comp), which hold back the sun in the shading and +// in the fog +// - the stars of the catalogue (environment/starcatalog.h) +// the weather, cloud and fog buffer is weather_ubo (gl::weather_ubs), bound for every pass +class opengl46_weather +{ + public: + opengl46_weather() = default; + ~opengl46_weather(); + opengl46_weather(const opengl46_weather &) = delete; + opengl46_weather &operator=(const opengl46_weather &) = delete; + + // creates the programs, buffers and noise textures. throws gl::shader_exception + void init(); + // binds the buffer and textures the shading reads; bindings are context state, so done in every context + void bind(); + // what the weather of the simulation is for the frame: the buffer, the shadows of the clouds, a band of the panorama, the + // colours of the fog. in the main context, before the passes of the frame. Keylight: world direction towards the sun or the + // moon, Keycolor: its colour as the shading gets it, Ambient: the ambient light of the sky, Fogcolor: the old colour of the fog + void update(opengl46_sky const &Sky, glm::vec3 const &Keylight, glm::vec3 const &Keycolor, glm::vec3 const &Ambient, glm::vec3 const &Fogcolor, glm::dvec3 const &Camera); + // the clouds and the fog volume of the colour pass of the main view. Projection and View (rotation, world axes to the view) + // of its camera, Size of the target the scene is drawn into, Cablamp the light of the cab lamp (it lights no fog) or -1. + // the light buffer, the light clusters and the shadow map of the pass are bound + void render_view(glm::mat4 const &Projection, glm::mat4 const &View, glm::dvec3 const &Camera, glm::ivec2 const &Size, int const Cablamp); + // the fog of a pass: the volume of the main view (after render_view() in the same frame), or along the ray + void setup_pass(bool const Volume, glm::ivec2 const &Size); + // the stars, in the sky pass. View: rotation of the camera of the pass + void draw_stars(glm::mat4 const &View, float const Sunangle, double const Siderealtime, double const Latitude); + // the clouds and the fog over the sky, in the sky pass. Mainview: the clouds of the main view, the panorama otherwise + void draw_sky(glm::mat4 const &Projection, glm::mat4 const &View, bool const Mainview); + + // true when the volumetric clouds are drawn (the sky leaves its own overcast look then) + bool clouds_drawn() const + { + return m_clouds_drawn; + } + // true when the clouds cast shadows (the sunlight isn't dimmed by the overcast then, the shadows do it) + bool cloud_shadows() const + { + return m_shadows_valid; + } + // fog density of a regular draw (model_ubs.fog_density): 1 / the fog range + float fog_density() const + { + return 1.f / m_fog_range; + } + // distance the fog hides things at, for the draw range: a dark object fades to about 2% there + float fog_range() const + { + return m_fog_range; + } + // lines for the gpu times of the debug panel + std::string info_times(); + // controls of the debug window + void draw_debug_ui(); + + private: + struct quality_level + { + int divisor; // of the resolution of the clouds of the main view + int steps; // samples through a layer at most + int light_steps; // samples towards the sun + bool detail; // with the small noise + glm::ivec2 panorama; // size of the panorama + glm::ivec3 fog_grid; // cells of the fog volume + }; + static quality_level quality(int const Clouds, int const Fog); + + static GLuint create_texture(GLenum Target, GLenum Format, glm::ivec3 const &Size, GLint Filter, GLint Wrap); + static void release(GLuint &Texture); + // the three noises, once + void make_noise(); + // the textures of the clouds of the main view and of the fog volume, for the size and quality + void prepare_view(glm::ivec2 const &Size, quality_level const &Level); + void bind_noise() const; + void upload_header(); + + // programs + std::unique_ptr m_noise_program; + std::unique_ptr m_march_program; + std::unique_ptr m_temporal_program; + std::unique_ptr m_shadow_program; + std::unique_ptr m_horizon_program; + std::unique_ptr m_inject_program; + std::unique_ptr m_integrate_program; + std::unique_ptr m_sky_program; + std::unique_ptr m_stars_program; + + std::unique_ptr m_weather_ubo; + gl::weather_ubs m_weather{}; + bool m_horizon_on_gpu{false}; + + // noise + GLuint m_shape_noise{0}; + GLuint m_detail_noise{0}; + GLuint m_coverage_noise{0}; + + // shadows of the clouds + GLuint m_shadow_map{0}; + int m_shadow_size{0}; + bool m_shadows_valid{false}; + gl::gpu_timer m_shadow_timer; + + // clouds of the main view + glm::ivec2 m_view_size{0}; + quality_level m_view_level{}; + GLuint m_clouds_color{0}; + GLuint m_clouds_depth{0}; + std::array m_clouds_history{0, 0}; + int m_clouds_current{0}; // the history holding the latest result + bool m_clouds_view_valid{false}; // render_view() made the clouds of the frame + gl::gpu_timer m_clouds_timer; + // panorama + GLuint m_panorama{0}; + glm::ivec2 m_panorama_size{0}; + int m_panorama_band{0}; + bool m_panorama_full{false}; // every band was drawn once since it was made + gl::gpu_timer m_panorama_timer; + bool m_clouds_drawn{false}; + + // fog volume + glm::ivec3 m_fog_grid{0}; + std::array m_fog_scattering{0, 0}; + int m_fog_current{0}; + GLuint m_fog_integrated{0}; + bool m_fog_volume_valid{false}; + float m_fog_range{5000.f}; + glm::vec4 m_fog_slicing{0.f}; + gl::gpu_timer m_fog_timer; + + // the camera of the previous frame of the main view, for the blending over frames + struct view_state + { + glm::mat4 projection_view{1.f}; // world axes to clip space, camera at the origin + glm::dvec3 camera{0.0}; + glm::vec4 slicing{0.f}; + bool valid{false}; + }; + view_state m_previous_clouds; + view_state m_previous_fog; + int m_frame{0}; + + // stars + std::optional m_stars_vao; + std::optional m_stars_buffer; + int m_stars_count{0}; + + // what the debug window shows + float m_sun_share{0.f}; + float m_average_transmittance{1.f}; + std::string m_report; + char m_report_input[256]{}; +}; diff --git a/shaders/apply_fog.glsl b/shaders/apply_fog.glsl index 34fc529b..19f59a30 100644 --- a/shaders/apply_fog.glsl +++ b/shaders/apply_fog.glsl @@ -1,30 +1,41 @@ +// fog between the camera and a fragment, see fog_volume.glsl and weather_common.glsl. a draw with a fog_density of 0 is not +// fogged; one with another density than the regular one (the glare of lamps) gets the optical depth scaled by the ratio +#include + +// rgb: light the fog adds, a: what it lets through +vec4 fog_at_fragment() +{ + if (fog_density <= 0.0) + return vec4(0.0, 0.0, 0.0, 1.0); + vec4 fog = fog_along_view(f_pos.xyz, gl_FragCoord.xy); + float scale = fog_density / max(fog_light.w, 1e-12); + if (abs(scale - 1.0) > 1e-3) + { + float transmittance = exp(log(max(fog.a, 1e-8)) * scale); + fog.rgb *= (1.0 - transmittance) / max(1.0 - fog.a, 1e-4); + fog.a = transmittance; + } + return fog; +} + vec3 get_fog_color() { - vec3 fog_color_v = fog_color; - if (lights_count >= 1U && lights[0].type == LIGHT_DIR) { - float sun_amount = max(dot(normalize(f_pos.xyz), normalize(-lights[0].dir)), 0.0); - fog_color_v += lights[0].color * pow(sun_amount, 30.0); - } - return fog_color_v; + vec4 fog = fog_at_fragment(); + return fog.rgb / max(1.0 - fog.a, 1e-4); } float get_fog_amount() { - float z = length(f_pos.xyz); -// float fog_amount_v = 1.0 - z * fog_density; // linear -// float fog_amount_v = exp( -fog_density * z ); // exp - float fog_amount_v = exp2( -fog_density * fog_density * z * z * 1.442695 ); // exp2 - - fog_amount_v = 1.0 - clamp(fog_amount_v, 0.0, 1.0); - return fog_amount_v; + return 1.0 - fog_at_fragment().a; } vec3 apply_fog(vec3 color) { - return mix(color, get_fog_color(), get_fog_amount()); + vec4 fog = fog_at_fragment(); + return color * fog.a + fog.rgb; } vec3 add_fog(vec3 color, float amount) { - return color + get_fog_color() * get_fog_amount() * amount; + return color + fog_at_fragment().rgb * amount; } diff --git a/shaders/clouds_common.glsl b/shaders/clouds_common.glsl new file mode 100644 index 00000000..804b9422 --- /dev/null +++ b/shaders/clouds_common.glsl @@ -0,0 +1,213 @@ +// clouds of the weather (opengl46_weather): density of the layers of weather_common.glsl, made of three tiling noises. +// the shape noise (3d, clouds_noise.comp) gives the lumps of a cloud, the detail noise (3d) eats into their edges, the coverage +// noise (2d) spreads the clouds of a layer over the sky so they take up its coverage. positions are world offsets from the +// camera, in metres, y up; the earth curves away from the camera, so far clouds sink below the horizon +#include + +layout(binding = CLOUD_SHAPE_TEX) uniform sampler3D cloud_shape_noise; +layout(binding = CLOUD_DETAIL_TEX) uniform sampler3D cloud_detail_noise; +layout(binding = CLOUD_COVERAGE_TEX) uniform sampler2D cloud_coverage_noise; + +const float CLOUD_EARTH_RADIUS = 6360000.0; + +float cloud_remap(float value, float low, float high, float newlow, float newhigh) +{ + return newlow + (value - low) / max(high - low, 1e-5) * (newhigh - newlow); +} + +// height of a point above the ground reference, the earth curving down with the square of the distance +float cloud_height(vec3 position) +{ + return weather_camera.x + position.y - dot(position.xz, position.xz) * (0.5 / CLOUD_EARTH_RADIUS); +} + +// distances along a ray (unit Direction from the camera) where it is at Height: the height goes as +// camera + up * t - (1 - up^2) / (2 R) * t^2. returns the number of crossings ahead (0-2), sorted into T +int cloud_ray_height(float up, float height, out vec2 t) +{ + t = vec2(-1.0); + float a = (1.0 - up * up) * (0.5 / CLOUD_EARTH_RADIUS); + float b = -up; + float c = height - weather_camera.x; + // a t^2 + b t + c = 0 + if (a < 1e-12) + { + if (abs(b) < 1e-8) + return 0; + float root = -c / b; + t = vec2(root, -1.0); + return root > 0.0 ? 1 : 0; + } + float discriminant = b * b - 4.0 * a * c; + if (discriminant < 0.0) + return 0; + float q = -0.5 * (b + (b >= 0.0 ? 1.0 : -1.0) * sqrt(discriminant)); + float r0 = q / a; + float r1 = (abs(q) > 1e-12) ? c / q : r0; + vec2 roots = vec2(min(r0, r1), max(r0, r1)); + if (roots.y <= 0.0) + return 0; + if (roots.x <= 0.0) + { + t = vec2(roots.y, -1.0); + return 1; + } + t = roots; + return 2; +} + +// the first stretch of a ray within the heights of a layer, x to y; y <= x when it never gets there +vec2 cloud_layer_segment(float up, float base, float top) +{ + vec2 tb, tt; + int nb = cloud_ray_height(up, base, tb); + int nt = cloud_ray_height(up, top, tt); + // the crossings in order; the ray goes in and out of the layer at each + float crossings[4]; + int count = 0; + for (int i = 0; i < nb; i++) + crossings[count++] = tb[i]; + for (int i = 0; i < nt; i++) + crossings[count++] = tt[i]; + for (int i = 1; i < count; i++) + for (int j = i; j > 0 && crossings[j - 1] > crossings[j]; j--) + { + float swap = crossings[j]; + crossings[j] = crossings[j - 1]; + crossings[j - 1] = swap; + } + bool inside = (weather_camera.x >= base) && (weather_camera.x <= top); + float start = 0.0; + for (int i = 0; i < count; i++) + { + if (inside) + return vec2(start, crossings[i]); + inside = true; + start = crossings[i]; + } + return inside ? vec2(start, 1e9) : vec2(0.0, -1.0); +} + +// the position in the noise of a layer: the camera's place in it and the offset from the camera, stretched along the wind +vec2 cloud_noise_plane(cloud_layer_s layer, vec2 offset) +{ + vec2 wind = cloud_wind.xy; + float along = dot(offset, wind); + vec2 across = offset - wind * along; + return across + wind * (along / max(layer.motion.w, 1.0)); +} + +// share of the layer filled at a place, x: the soft mask of the coverage, y: how far into a cloud area (0 at its edge) +vec2 cloud_coverage(cloud_layer_s layer, vec2 offset) +{ + vec2 coords = (layer.extra.zw + cloud_noise_plane(layer, offset)) * layer.motion.z; + float noise = textureLod(cloud_coverage_noise, coords + layer.extra.y, 0.0).r; + float coverage = layer.shape.x; + float threshold = 1.0 - coverage; + float mask = smoothstep(threshold - 0.06, threshold + 0.06, noise); + float depth = clamp((noise - threshold) / max(coverage, 0.05), 0.0, 1.0); + return vec2(mask, depth); +} + +// extinction (1/m) of a volumetric layer at a point; Detail eats the edges with the small noise +float cloud_layer_density(int index, vec3 position, float height, bool detail) +{ + cloud_layer_s layer = cloud_layers[index]; + float h = (height - layer.heights.x) * layer.heights.z; + if (h <= 0.0 || h >= 1.0) + return 0.0; + vec2 coverage = cloud_coverage(layer, position.xz); + // the anvil spreads the top of the storm clouds out of their area + if (layer.profile.w > 0.0) + { + float anvil = layer.profile.w * smoothstep(0.7, 0.92, h) * (1.0 - smoothstep(0.97, 1.0, h)); + coverage.x = max(coverage.x, anvil * smoothstep(0.0, 0.5, coverage.y + 0.35)); + coverage.y = max(coverage.y, anvil * 0.6); + } + if (coverage.x <= 0.0) + return 0.0; + // the tops of the single clouds are of different heights, the larger ones (deeper in their area) the taller + float top = mix(1.0 - layer.profile.z, 1.0, coverage.y); + float gradient = smoothstep(0.0, layer.profile.x, h) * (1.0 - smoothstep(top * layer.profile.y, top, h)); + if (gradient <= 0.0) + return 0.0; + vec2 plane = layer.motion.xy + cloud_noise_plane(layer, position.xz); + vec3 coords = vec3(plane.x, height + layer.extra.y * 4096.0, plane.y) * layer.shape.z; + vec4 shape = textureLod(cloud_shape_noise, coords, 0.0); + float worley = shape.g * 0.625 + shape.b * 0.25 + shape.a * 0.125; + float base = clamp(cloud_remap(shape.r, worley - 1.0, 1.0, 0.0, 1.0), 0.0, 1.0); + // the sheets (little erosion) keep few holes, the heaps are made of lumps + base = mix(1.0, base, clamp(layer.shape.w * 2.5, 0.25, 1.0)); + float density = clamp(cloud_remap(base * gradient, 1.0 - coverage.x, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x; + if (detail && density > 0.0 && layer.shape.w > 0.0) + { + vec4 small = textureLod(cloud_detail_noise, coords * 6.0, 0.0); + float fbm = small.r * 0.625 + small.g * 0.25 + small.b * 0.125; + // wispy at the base, billowing at the top + float erosion = mix(fbm, 1.0 - fbm, clamp(h * 4.0, 0.0, 1.0)); + density = clamp(cloud_remap(density, erosion * layer.shape.w * 0.5, 1.0, 0.0, 1.0), 0.0, 1.0); + } + return density * layer.shape.y; +} + +// the same without the detail, cheaper, for the shadows and the fog +float cloud_density_coarse(vec3 position) +{ + float height = cloud_height(position); + float density = 0.0; + for (int i = 0; i < int(weather_camera.w); i++) + { + if (cloud_layers[i].extra.x > 0.5) + continue; + density += cloud_layer_density(i, position, height, false); + } + return density; +} + +// optical depth of a flat layer (cirrus and the like) where a ray crosses its middle, Up the vertical part of the ray there +float cloud_flat_depth(int index, vec3 position) +{ + cloud_layer_s layer = cloud_layers[index]; + vec2 coverage = cloud_coverage(layer, position.xz); + if (coverage.x <= 0.0) + return 0.0; + vec2 plane = layer.motion.xy + cloud_noise_plane(layer, position.xz); + vec2 coords = plane * layer.shape.z; + // strands and ripples out of slices of the noise + vec4 shape = textureLod(cloud_shape_noise, vec3(coords, 0.37 + layer.extra.y), 0.0); + vec4 small = textureLod(cloud_detail_noise, vec3(coords * 4.0, 0.61), 0.0); + float strands = clamp(cloud_remap(shape.r * 0.7 + small.r * 0.3, 0.35 * layer.shape.w + (1.0 - coverage.x) * 0.5, 1.0, 0.0, 1.0), 0.0, 1.0); + return strands * coverage.x * layer.shape.y; +} + +// the shadow of the clouds at a point: what they let through towards the key light. Steps per layer +float cloud_transmittance_to_sun(vec3 position, int steps) +{ + vec3 sun = weather_sun_direction.xyz; + if (sun.y <= 0.01) + return fog_light.y; + float height = cloud_height(position); + float depth = 0.0; + for (int i = 0; i < int(weather_camera.w); i++) + { + cloud_layer_s layer = cloud_layers[i]; + if (layer.extra.x > 0.5) + { + float middle = 0.5 * (layer.heights.x + layer.heights.y); + if (middle > height) + depth += cloud_flat_depth(i, position + sun * ((middle - height) / sun.y)) / sun.y; + continue; + } + float enter = max(layer.heights.x - height, 0.0) / sun.y; + float leave = (layer.heights.y - height) / sun.y; + if (leave <= 0.0) + continue; + float step = (leave - enter) / float(steps); + for (int s = 0; s < steps; s++) + { + vec3 point = position + sun * (enter + step * (float(s) + 0.5)); + depth += cloud_layer_density(i, point, cloud_height(point), false) * step; + } + } + return exp(-depth); +} diff --git a/shaders/clouds_march.comp b/shaders/clouds_march.comp new file mode 100644 index 00000000..fcf9365e --- /dev/null +++ b/shaders/clouds_march.comp @@ -0,0 +1,226 @@ +// clouds of the weather ray marched through their layers (clouds_common.glsl), into an image the sky pass lays over the sky +// (sky_weather.frag). mode 0: the view of the main camera at a part of the resolution, smoothed over frames by +// clouds_temporal.comp; mode 1: a panorama around the camera (azimuth across, elevation up), a band of rows per frame, for the +// environment map and the other views. rgb: the light of the clouds with the fog in front of them, a: what they let through +// of the sky behind; the depth image gets the distance of the clouds, for the reprojection + +#include +#include + +layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in; + +layout(rgba16f, binding = 0) uniform writeonly image2D clouds_color; +layout(r32f, binding = 1) uniform writeonly image2D clouds_depth; + +layout(location = 0) uniform int mode; +layout(location = 1) uniform ivec2 size; +// mode 0: clip space to view space of the camera, and view space to world axes +layout(location = 2) uniform mat4 inverse_projection; +layout(location = 3) uniform mat4 view_to_world; +layout(location = 4) uniform int frame; +// x: samples through a layer at most, y: samples towards the sun, z: farthest distance (m), w: 1 with the detail noise +layout(location = 5) uniform vec4 quality; +// mode 1: first row of the band and the rows in it +layout(location = 6) uniform ivec2 rows; + +// lowest elevation of the panorama (radians); its rows go with the square of the height, denser at the horizon +const float PANORAMA_LOW = -0.1; +// scales of the light of the clouds against the shading of the surfaces (light_common.glsl: SUN_DIFFUSE_SCALE, AMBIENT_SCALE) +const float CLOUD_SUN_SCALE = 1.5; +const float CLOUD_AMBIENT_SCALE = 1.0; + +float henyey_greenstein(float cosangle, float g) +{ + // times 4 pi: 1 for an even scattering + float g2 = g * g; + return (1.0 - g2) / pow(max(1.0 + g2 - 2.0 * g * cosangle, 1e-4), 1.5); +} + +float cloud_phase(float cosangle, float scale) +{ + return mix(henyey_greenstein(cosangle, 0.8 * scale), henyey_greenstein(cosangle, -0.25 * scale), 0.3); +} + +// light of the sun reaching a point inside a layer through the cloud towards it, with the light scattered many times +// (octaves after Wrenninge), and the phase of the view +float cloud_sunlight(int index, vec3 position, float cosangle, int steps) +{ + vec3 sun = weather_sun_direction.xyz; + cloud_layer_s layer = cloud_layers[index]; + float thickness = layer.heights.y - layer.heights.x; + float reach = min(thickness, 1500.0); + float depth = 0.0; + float travelled = 0.0; + for (int s = 0; s < steps; s++) + { + // growing steps: near the point the shape matters, far only the bulk + float step = reach * (float(s) + 1.0) / float(steps * (steps + 1) / 2); + vec3 point = position + sun * (travelled + 0.5 * step); + travelled += step; + depth += cloud_layer_density(index, point, cloud_height(point), false) * step; + } + float light = 0.0; + float amount = 1.0; + float fade = 1.0; + float spread = 1.0; + for (int octave = 0; octave < 3; octave++) + { + light += amount * exp(-depth * fade) * cloud_phase(cosangle, spread); + amount *= 0.5; + fade *= 0.5; + spread *= 0.5; + } + return light; +} + +float interleaved_noise(vec2 pixel) +{ + return fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715)))); +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec3 direction; + if (mode == 0) + { + if (any(greaterThanEqual(texel, size))) + return; + vec2 ndc = (vec2(texel) + 0.5) / vec2(size) * 2.0 - 1.0; + // reverse z: the near plane at 1 + vec4 near = inverse_projection * vec4(ndc, 1.0, 1.0); + direction = normalize(mat3(view_to_world) * normalize(near.xyz / near.w)); + } + else + { + texel.y += rows.x; + if (texel.x >= size.x || texel.y >= min(size.y, rows.x + rows.y)) + return; + vec2 coords = (vec2(texel) + 0.5) / vec2(size); + float elevation = PANORAMA_LOW + (0.5 * WEATHER_PI - PANORAMA_LOW) * coords.y * coords.y; + float azimuth = coords.x * 2.0 * WEATHER_PI; + direction = vec3(cos(elevation) * sin(azimuth), sin(elevation), cos(elevation) * cos(azimuth)); + } + + vec3 sun = weather_sun_direction.xyz; + float cosangle = dot(direction, sun); + float sunup = weather_sun_direction.w; + float maxdistance = quality.z; + int maxsteps = int(quality.x); + int lightsteps = int(quality.y); + bool detail = quality.w > 0.5; + float jitter = fract(interleaved_noise(vec2(texel)) + float(frame) * 0.618034); + + vec3 scattered = vec3(0.0); + float transmittance = 1.0; + float depthsum = 0.0; + float weightsum = 0.0; + int layers = int(weather_camera.w); + + // the stretches of the volumetric layers along the ray, nearest first + vec2 segments[MAX_CLOUD_LAYERS]; + int order[MAX_CLOUD_LAYERS]; + int count = 0; + for (int i = 0; i < layers; i++) + { + if (cloud_layers[i].extra.x > 0.5) + continue; + vec2 segment = cloud_layer_segment(direction.y, cloud_layers[i].heights.x, cloud_layers[i].heights.y); + segment.y = min(segment.y, maxdistance); + if (segment.y <= segment.x) + continue; + int at = count++; + segments[at] = segment; + order[at] = i; + for (int j = at; j > 0 && segments[j - 1].x > segments[j].x; j--) + { + vec2 swapsegment = segments[j]; + segments[j] = segments[j - 1]; + segments[j - 1] = swapsegment; + int swaporder = order[j]; + order[j] = order[j - 1]; + order[j - 1] = swaporder; + } + } + + vec3 ambienttop = weather_ambient.rgb * CLOUD_AMBIENT_SCALE; + vec3 sunlight = weather_sun_color.rgb * CLOUD_SUN_SCALE * sunup; + for (int k = 0; k < count && transmittance > 0.01; k++) + { + int index = order[k]; + cloud_layer_s layer = cloud_layers[index]; + vec2 segment = segments[k]; + float span = segment.y - segment.x; + float thickness = layer.heights.y - layer.heights.x; + int steps = clamp(int(float(maxsteps) * span / (thickness * 4.0)), 8, maxsteps); + float step = span / float(steps); + for (int s = 0; s < steps; s++) + { + float t = segment.x + (float(s) + jitter) * step; + vec3 position = direction * t; + float height = cloud_height(position); + float density = cloud_layer_density(index, position, height, detail); + if (density <= 0.0) + continue; + float h = clamp((height - layer.heights.x) * layer.heights.z, 0.0, 1.0); + float sunshare = (sunup > 0.0) ? cloud_sunlight(index, position, cosangle, lightsteps) : 0.0; + // the light of the sky reaches inside through the cloud above: the base of a thick cloud is darker than its top + float above = layer.shape.y * layer.shape.x * (layer.heights.y - height); + vec3 light = sunlight * sunshare + ambienttop * mix(0.35, 1.0, h) * mix(0.25, 1.0, exp(-0.05 * above)); + float through = exp(-density * step); + vec3 inscattered = light * (1.0 - through); + scattered += transmittance * inscattered; + float weight = transmittance * (1.0 - through); + depthsum += t * weight; + weightsum += weight; + transmittance *= through; + if (transmittance < 0.01) + break; + } + } + + // the thin high layers, past the others + for (int i = 0; i < layers && transmittance > 0.01; i++) + { + cloud_layer_s layer = cloud_layers[i]; + if (layer.extra.x < 0.5) + continue; + vec2 crossing; + if (cloud_ray_height(direction.y, 0.5 * (layer.heights.x + layer.heights.y), crossing) == 0) + continue; + float t = crossing.x; + if (t > maxdistance * 2.0) + continue; + vec3 position = direction * t; + // the slant of the ray through the sheet where it crosses it, the earth curving away + float slope = max(abs(direction.y - (1.0 - direction.y * direction.y) * t / CLOUD_EARTH_RADIUS), 0.03); + float depth = cloud_flat_depth(i, position) / slope; + if (depth <= 0.0) + continue; + float through = exp(-depth); + // ice crystals scatter forwards strongly + vec3 light = sunlight * henyey_greenstein(cosangle, 0.6) * 0.8 + ambienttop; + scattered += transmittance * light * (1.0 - through); + float weight = transmittance * (1.0 - through); + depthsum += t * weight; + weightsum += weight; + transmittance *= through; + } + + // the haze the atmosphere has in the sky already lies between the camera and the clouds too; the rest of the fog goes over + // the clouds and the sky alike when they are laid over it (sky_weather.frag) + float span_distance = (weightsum > 1e-5) ? depthsum / weightsum : maxdistance; + if (weightsum > 1e-5) + { + vec4 fog = fog_analytic_with(direction, 0.0, span_distance, min(fog_haze.x, fog_haze.z), 0.0); + scattered = scattered * fog.a + fog.rgb * (1.0 - transmittance); + } + if (any(isnan(scattered)) || isnan(transmittance)) + { + scattered = vec3(0.0); + transmittance = 1.0; + } + imageStore(clouds_color, texel, vec4(scattered, transmittance)); + if (mode == 0) + imageStore(clouds_depth, texel, vec4(span_distance)); +} diff --git a/shaders/clouds_noise.comp b/shaders/clouds_noise.comp new file mode 100644 index 00000000..21459ac4 --- /dev/null +++ b/shaders/clouds_noise.comp @@ -0,0 +1,121 @@ +// the tiling noises the clouds and the fog are made of, made once at the start (opengl46_weather::init). +// kind 0, the shape (3d): r perlin-worley (perlin fbm with the worley cells carved in), g b a worley fbm of rising frequency; +// kind 1, the detail (3d): r g b worley fbm of rising frequency; +// kind 2, the coverage (2d): r g two perlin fbm, spread evenly over 0-1 so a threshold of 1 - c leaves a share c above it + +#include +#include + +layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in; + +layout(rgba8, binding = 0) uniform writeonly image3D noise_volume; +layout(rgba8, binding = 1) uniform writeonly image2D noise_plane; + +layout(location = 0) uniform int kind; +layout(location = 1) uniform int size; + +vec3 hash3(ivec3 cell, int period, uint seed) +{ + uvec3 wrapped = uvec3((cell % period + period) % period); + uint h = Hash(uvec3(wrapped.x + seed * 977U, wrapped.y, wrapped.z)); + uint h2 = Hash(h + 0x68bc21ebU); + uint h3 = Hash(h2 + 0x02e5be93U); + return vec3(float(h & 0xffffU), float(h2 & 0xffffU), float(h3 & 0xffffU)) / 65535.0; +} + +// gradient noise repeating every Period cells, -1..1 about +float perlin(vec3 position, int period, uint seed) +{ + ivec3 cell = ivec3(floor(position)); + vec3 local = fract(position); + vec3 fade = local * local * local * (local * (local * 6.0 - 15.0) + 10.0); + float corners[8]; + for (int i = 0; i < 8; i++) + { + ivec3 offset = ivec3(i & 1, (i >> 1) & 1, (i >> 2) & 1); + vec3 gradient = normalize(hash3(cell + offset, period, seed) * 2.0 - 1.0 + 1e-4); + corners[i] = dot(gradient, local - vec3(offset)); + } + float x0 = mix(corners[0], corners[1], fade.x); + float x1 = mix(corners[2], corners[3], fade.x); + float x2 = mix(corners[4], corners[5], fade.x); + float x3 = mix(corners[6], corners[7], fade.x); + return mix(mix(x0, x1, fade.y), mix(x2, x3, fade.y), fade.z) * 1.6; +} + +// inverted distance to the nearest of the points scattered one per cell, 0-1, repeating every Period cells +float worley(vec3 position, int period, uint seed) +{ + ivec3 cell = ivec3(floor(position)); + vec3 local = fract(position); + float nearest = 1.0; + for (int z = -1; z <= 1; z++) + for (int y = -1; y <= 1; y++) + for (int x = -1; x <= 1; x++) + { + ivec3 offset = ivec3(x, y, z); + vec3 point = vec3(offset) + hash3(cell + offset, period, seed); + nearest = min(nearest, length(point - local)); + } + return 1.0 - clamp(nearest, 0.0, 1.0); +} + +float perlin_fbm(vec3 position, int frequency, int octaves, uint seed) +{ + float sum = 0.0; + float amplitude = 0.5; + float total = 0.0; + for (int i = 0; i < octaves; i++) + { + sum += perlin(position * float(frequency), frequency, seed + uint(i)) * amplitude; + total += amplitude; + amplitude *= 0.5; + frequency *= 2; + } + return sum / total; +} + +float worley_fbm(vec3 position, int frequency, uint seed) +{ + return worley(position * float(frequency), frequency, seed) * 0.625 + + worley(position * float(frequency * 2), frequency * 2, seed + 1U) * 0.25 + + worley(position * float(frequency * 4), frequency * 4, seed + 2U) * 0.125; +} + +float remap(float value, float low, float high, float newlow, float newhigh) +{ + return newlow + (value - low) / (high - low) * (newhigh - newlow); +} + +// a sum of noises is about normally distributed; this spreads it about evenly over 0-1 +float even(float value, float spread) +{ + return clamp(0.5 + 0.5 * tanh(value / spread * 1.1), 0.0, 1.0); +} + +void main() +{ + ivec3 texel = ivec3(gl_GlobalInvocationID); + if (any(greaterThanEqual(texel.xy, ivec2(size)))) + return; + if (kind == 2) + { + vec3 position = vec3((vec2(texel.xy) + 0.5) / float(size), 0.0); + float first = perlin_fbm(position, 4, 6, 11U); + float second = perlin_fbm(position + vec3(0.0, 0.0, 0.5), 8, 5, 23U); + imageStore(noise_plane, texel.xy, vec4(even(first, 0.22), even(second, 0.22), 0.0, 1.0)); + return; + } + if (texel.z >= size) + return; + vec3 position = (vec3(texel) + 0.5) / float(size); + if (kind == 0) + { + float low = perlin_fbm(position, 4, 4, 3U) * 0.5 + 0.5; + float cells = worley_fbm(position, 4, 5U); + float perlinworley = clamp(remap(low, cells - 1.0, 1.0, 0.0, 1.0), 0.0, 1.0); + imageStore(noise_volume, texel, vec4(perlinworley, cells, worley_fbm(position, 8, 7U), worley_fbm(position, 16, 9U))); + } + else + imageStore(noise_volume, texel, vec4(worley_fbm(position, 4, 13U), worley_fbm(position, 8, 15U), worley_fbm(position, 16, 17U), 1.0)); +} diff --git a/shaders/clouds_shadow.comp b/shaders/clouds_shadow.comp new file mode 100644 index 00000000..4e9d53af --- /dev/null +++ b/shaders/clouds_shadow.comp @@ -0,0 +1,27 @@ +// the shadow the clouds cast on the ground: for points of the ground reference around the camera, what the clouds let through +// towards the key light. the shading (light_common.glsl) and the fog (fog_inject.comp) look it up along the ray of the sun, +// see cloud_shadow() in weather_common.glsl + +#include +#include + +layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in; + +layout(r16f, binding = 0) uniform writeonly image2D shadow; + +layout(location = 1) uniform int size; +// world x and z of the centre of the map relative to the camera, size of the map (metres) +layout(location = 2) uniform vec3 placement; +// samples through a layer +layout(location = 3) uniform int steps; + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(texel, ivec2(size)))) + return; + vec2 offset = placement.xy + ((vec2(texel) + 0.5) / float(size) - 0.5) * placement.z; + // the ground reference, the earth curving away + vec3 ground = vec3(offset.x, -weather_camera.x + dot(offset, offset) * (0.5 / CLOUD_EARTH_RADIUS), offset.y); + imageStore(shadow, texel, vec4(cloud_transmittance_to_sun(ground, steps))); +} diff --git a/shaders/clouds_temporal.comp b/shaders/clouds_temporal.comp new file mode 100644 index 00000000..241fc53e --- /dev/null +++ b/shaders/clouds_temporal.comp @@ -0,0 +1,65 @@ +// the clouds of the main view smoothed over frames: the image of this frame (clouds_march.comp, ray started at another +// distance every frame) blended into the one of the previous frames, found where the clouds were by the distance of the +// clouds, the rotation and the move of the camera. the history is held to the range of the neighbourhood of this frame, +// so moving clouds and a turning camera don't leave trails + +#include + +layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in; + +layout(binding = CLOUD_TEX) uniform sampler2D current_color; +layout(binding = CLOUD_SHAPE_TEX) uniform sampler2D current_depth; +layout(binding = CLOUD_HISTORY_TEX) uniform sampler2D history_color; +layout(rgba16f, binding = 0) uniform writeonly image2D result; + +layout(location = 1) uniform ivec2 size; +layout(location = 2) uniform mat4 inverse_projection; +layout(location = 3) uniform mat4 view_to_world; +// world axes to the clip space of the previous frame, the camera of the previous frame at the origin +layout(location = 4) uniform mat4 previous_projection_view; +// where the camera was in the previous frame, relative to where it is (world, metres) +layout(location = 5) uniform vec3 previous_camera; +// share of this frame in the result; 1 when there is no history +layout(location = 6) uniform float blend; + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(texel, size))) + return; + vec4 current = texelFetch(current_color, texel, 0); + if (blend >= 1.0) + { + imageStore(result, texel, current); + return; + } + // the range of the neighbourhood + vec4 low = current; + vec4 high = current; + for (int y = -1; y <= 1; y++) + for (int x = -1; x <= 1; x++) + { + vec4 neighbour = texelFetch(current_color, clamp(texel + ivec2(x, y), ivec2(0), size - 1), 0); + low = min(low, neighbour); + high = max(high, neighbour); + } + vec2 ndc = (vec2(texel) + 0.5) / vec2(size) * 2.0 - 1.0; + vec4 near = inverse_projection * vec4(ndc, 1.0, 1.0); + vec3 direction = normalize(mat3(view_to_world) * normalize(near.xyz / near.w)); + float span_distance = texelFetch(current_depth, texel, 0).r; + vec3 position = direction * span_distance - previous_camera; + vec4 clip = previous_projection_view * vec4(position, 1.0); + float share = blend; + vec4 history = current; + if (clip.w > 0.0) + { + vec2 coords = clip.xy / clip.w * 0.5 + 0.5; + if (all(greaterThanEqual(coords, vec2(0.0))) && all(lessThanEqual(coords, vec2(1.0)))) + history = clamp(textureLod(history_color, coords, 0.0), low, high); + else + share = 1.0; + } + else + share = 1.0; + imageStore(result, texel, mix(history, current, share)); +} diff --git a/shaders/fog_horizon.comp b/shaders/fog_horizon.comp new file mode 100644 index 00000000..4c1d2265 --- /dev/null +++ b/shaders/fog_horizon.comp @@ -0,0 +1,42 @@ +// colours of the fog towards the horizon by the angle from the azimuth of the sun, from the sky-view table of the atmosphere, +// written into the weather buffer (weather_common.glsl: fog_horizon). the far fog then has the colour of the sky behind it. +// the atmosphere blends into the old sky as opengl46_sky does it, by its share, white balance and brightness + +#include +#include + +layout(local_size_x = FOG_HORIZON_SAMPLES) in; + +layout(binding = SKY_VIEW_TEX) uniform sampler2D sky_view_lut; + +layout(std430, binding = WEATHER_SCRATCH_SSBO_BINDING) buffer weather_scratch +{ + vec4 weather_words[]; +}; + +// first vec4 of fog_horizon in the weather buffer +layout(location = 0) uniform int offset; +// x: share of the atmosphere, y: its brightness, z: desaturation by the overcast, w: brightest the colour gets +layout(location = 1) uniform vec4 params; +layout(location = 2) uniform vec3 white_balance; +// the colour of the old fog, which the atmosphere replaces by its share +layout(location = 3) uniform vec3 old_color; + +void main() +{ + uint index = gl_LocalInvocationID.x; + float angle = float(index) / float(FOG_HORIZON_SAMPLES - 1) * ATMO_PI; + vec3 sun = normalize(atmo_sun_direction.xyz); + vec2 flat_sun = (dot(sun.xz, sun.xz) > 1e-8) ? normalize(sun.xz) : vec2(1.0, 0.0); + vec2 side = vec2(-flat_sun.y, flat_sun.x); + vec2 flat_view = flat_sun * cos(angle) + side * sin(angle); + // a little above the horizon, where the far fog meets the sky + vec3 direction = normalize(vec3(flat_view.x, 0.035, flat_view.y)); + vec3 color = textureLod(sky_view_lut, atmo_skyview_uv_world(direction, sun), 0.0).rgb * white_balance * params.y; + color = min(color, vec3(params.w)); + float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722)); + color = mix(color, vec3(luminance), params.z); + if (any(isnan(color))) + color = old_color; + weather_words[offset + int(index)] = vec4(mix(old_color, color, params.x), 1.0); +} diff --git a/shaders/fog_inject.comp b/shaders/fog_inject.comp new file mode 100644 index 00000000..e24262f4 --- /dev/null +++ b/shaders/fog_inject.comp @@ -0,0 +1,179 @@ +// the fog volume of the main view, first pass: in every cell of a grid laid over the view (slices spaced exponentially in the +// depth), how dense the fog is and how much light it scatters towards the camera. the density: the haze, the patches of the +// ground fog, the low clouds; the light: the colour the fog has towards the horizon (weather_common.glsl), less what the +// shadows of the sun and of the clouds hold back, and the lamps and headlights of the light clusters. blended with the +// previous frame, found where the cell was then; the cells are sampled at another depth every frame. +// rgb: light scattered towards the camera per metre, a: extinction per metre + +#include +#include + +layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in; + +layout(rgba16f, binding = 0) uniform writeonly image3D fog_scattering; +layout(binding = CLOUD_HISTORY_TEX) uniform sampler3D fog_history; + +#if SHADOWMAP_ENABLED +uniform sampler2DArrayShadow shadowmap; +#endif +uniform sampler2D headlightmap; +layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_shadowmap; + +layout(location = 0) uniform ivec3 grid; +layout(location = 1) uniform mat4 inverse_projection; +layout(location = 2) uniform mat4 view_to_world; +// x: near, y: far, z: slices / log(far / near), w: where in its slice the cell is sampled this frame (0-1) +layout(location = 3) uniform vec4 slicing; +// world axes to the clip space of the previous frame, the camera of the previous frame at the origin +layout(location = 4) uniform mat4 previous_projection_view; +layout(location = 5) uniform vec3 previous_camera; +// x: share of this frame (1 without history), y: brightness of the lamps in the fog (0: none), z: 1 when the sun casts shadows +// into the fog, w: 1 when the headlights cast shadows into it +layout(location = 6) uniform vec4 options; +// the light of the cab lamp, which lights no fog (the air of the cab is clear), or -1 +layout(location = 7) uniform int cablamp; +// x: near, y: slices / log(far / near) of the previous frame +layout(location = 8) uniform vec2 previous_slicing; + +float henyey_greenstein(float cosangle, float g) +{ + float g2 = g * g; + return (1.0 - g2) / pow(max(1.0 + g2 - 2.0 * g * cosangle, 1e-4), 1.5); +} + +// share of the sunlight the shadow map holds back at a point of the view space, one comparison +float sun_shadow(vec3 position) +{ +#if SHADOWMAP_ENABLED + float span_distance = dot(position, position); + uint cascade; + for (cascade = 0U; cascade < MAX_CASCADES; cascade++) + if (span_distance <= cascade_end[cascade]) + break; + if (cascade >= MAX_CASCADES) + return 0.0; + vec4 lightpos = lightview[cascade] * vec4(position, 1.0); + vec3 coords = lightpos.xyz / lightpos.w; + if (coords.z < 0.0 || any(lessThan(coords.xy, vec2(0.0))) || any(greaterThan(coords.xy, vec2(1.0)))) + return 0.0; + return texture(shadowmap, vec4(coords.xy, float(cascade), coords.z + 0.0001 * float(cascade + 1U))); +#else + return 0.0; +#endif +} + +// light of a lamp or of the beam of headlights at a point of the view space, before the phase +vec3 lamp_light(light_s light, vec3 position) +{ + if (light.type == LIGHT_HEADLIGHTS) + { + vec4 projected = light.headlight_projection * vec4(position, 1.0); + vec3 coords = projected.xyz / projected.w; + if (coords.z < 0.0 || coords.z > 1.0 || any(lessThan(coords.xy, vec2(0.0))) || any(greaterThan(coords.xy, vec2(1.0)))) + return vec3(0.0); + float span_distance = length(light.pos - position); + float atten = 1.0 / (1.0 + light.linear * span_distance + light.quadratic * span_distance * span_distance); + atten *= mix(1.0, 0.0, clamp((coords.z - 0.998) * 500.0, 0.0, 1.0)); + vec3 beam = textureLod(headlightmap, coords.xy, 1.0).rgb * light.headlight_weights.rgb; + float intensity = max(max(beam.r, beam.g), beam.b); + if ((options.w > 0.5) && (light.headlight_weights.w > 0.5) && (intensity > 0.0)) + { + // the shadow map of the beam, as in light_common.glsl without the offset along a normal + uint map = uint(light.headlight_weights.w - 0.5); + vec4 shadowpos = headlight_shadow[map] * vec4(position, 1.0); + if (shadowpos.w > 0.0) + { + vec2 shadowcoords = shadowpos.xy / shadowpos.w; + if (all(greaterThanEqual(shadowcoords, vec2(0.0))) && all(lessThanEqual(shadowcoords, vec2(1.0)))) + { + float texel = headlight_shadow_params[map].x * shadowpos.w; + float reference = shadowpos.z / max(shadowpos.w - texel * 1.5, 1e-3); + intensity *= 1.0 - texture(headlight_shadowmap, vec4(shadowcoords, float(map), reference)); + } + } + } + return light.color * (atten * intensity * light.intensity); + } + vec3 tolight = light.pos - position; + float distance2 = dot(tolight, tolight); + float edge = clamp(1.0 - distance2 * distance2 * (light.quadratic * light.quadratic), 0.0, 1.0); + float atten = edge * edge / max(distance2, 0.01); + if (light.type == LIGHT_SPOT) + atten *= smoothstep(light.out_cutoff, light.in_cutoff, dot(-tolight * inversesqrt(max(distance2, 1e-8)), normalize(light.dir))); + return light.color * (atten * light.intensity); +} + +void main() +{ + ivec3 cell = ivec3(gl_GlobalInvocationID); + if (any(greaterThanEqual(cell, grid))) + return; + vec2 coords = (vec2(cell.xy) + 0.5) / vec2(grid.xy); + vec4 near = inverse_projection * vec4(coords * 2.0 - 1.0, 1.0, 1.0); + vec3 ray = near.xyz / near.w; + ray /= -ray.z; + float depth = slicing.x * exp((float(cell.z) + slicing.w) / slicing.z); + vec3 view = ray * depth; + vec3 world = mat3(view_to_world) * view; + float height = weather_camera.x + world.y; + + // density + float haze = fog_haze.x * exp(-fog_haze.y * max(height, 0.0)); + float ground = fog_ground.x * exp(-fog_ground.y * max(height - fog_ground.z, 0.0)); + if (ground > 0.0 && fog_ground.w > 0.0) + { + float patches = textureLod(cloud_shape_noise, (world + fog_noise.xyz) * fog_noise.w, 0.0).g; + ground *= mix(1.0, clamp((patches - 0.3) * 2.6, 0.0, 2.2), fog_ground.w); + } + float clouds = cloud_density_coarse(world); + float extinction = haze + ground + clouds; + + vec4 result = vec4(0.0); + if (extinction > 1e-7) + { + vec3 direction = normalize(world); + // the light of the sky and the sun, held back by the shadows + float sunlit = cloud_shadow(world); + if (options.z > 0.5 && sunlit > 0.0) + sunlit *= 1.0 - sun_shadow(view); + vec3 light = fog_whiten(fog_color_world(direction), (ground + clouds) / extinction) * fog_light_factor(sunlit); + // lamps and headlights of the cluster of the cell + if (options.y > 0.0 && lights_clustered != 0U && cluster_valid != 0U) + { + ivec2 tile = clamp(ivec2(coords * vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y)), ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1)); + int slice = clamp(int(floor(cluster_slice_bias + log(depth) * cluster_slice_scale)), 0, CLUSTER_GRID_Z - 1); + cluster_range_s range = cluster_ranges[uint(tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice))]; + vec3 tocamera = -normalize(view); + vec3 lamps = vec3(0.0); + for (uint i = 0U; i < range.count; i++) + { + uint index = cluster_light_indices[range.offset + i]; + if (int(index) == cablamp) + continue; + light_s lamp = lights[index]; + vec3 incoming = lamp_light(lamp, view); + if (dot(incoming, incoming) <= 0.0) + continue; + lamps += incoming * henyey_greenstein(dot(normalize(view - lamp.pos), tocamera), 0.4); + } + light += lamps * options.y; + } + result = vec4(light * fog_haze.w * extinction, extinction); + } + + // the previous frame, where the cell was then + float share = options.x; + if (share < 1.0) + { + vec4 clip = previous_projection_view * vec4(world - previous_camera, 1.0); + if (clip.w > 0.0) + { + vec3 previous = vec3(clip.xy / clip.w * 0.5 + 0.5, log(max(clip.w / previous_slicing.x, 1e-6)) * previous_slicing.y / float(grid.z)); + if (all(greaterThanEqual(previous, vec3(0.0))) && all(lessThanEqual(previous, vec3(1.0)))) + result = mix(textureLod(fog_history, previous, 0.0), result, share); + } + } + if (any(isnan(result))) + result = vec4(0.0); + imageStore(fog_scattering, cell, result); +} diff --git a/shaders/fog_integrate.comp b/shaders/fog_integrate.comp new file mode 100644 index 00000000..b6258ddc --- /dev/null +++ b/shaders/fog_integrate.comp @@ -0,0 +1,44 @@ +// the fog volume of the main view, second pass: the light and extinction of the cells (fog_inject.comp) added up along the rays +// of the view, front to back, into what the fog adds and lets through from the camera to the far side of every slice +// (apply_fog.glsl). the light of a cell is integrated over its length as Hillaire (2015) does it, so it doesn't grow with +// the size of the cell + +#include + +layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in; + +layout(binding = CLOUD_HISTORY_TEX) uniform sampler3D fog_scattering; +layout(rgba16f, binding = 0) uniform writeonly image3D fog_integrated; + +layout(location = 0) uniform ivec3 grid; +layout(location = 1) uniform mat4 inverse_projection; +// x: near, y: far, z: slices / log(far / near) +layout(location = 3) uniform vec4 slicing; + +void main() +{ + ivec2 column = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(column, grid.xy))) + return; + vec2 coords = (vec2(column) + 0.5) / vec2(grid.xy); + vec4 near = inverse_projection * vec4(coords * 2.0 - 1.0, 1.0, 1.0); + vec3 ray = near.xyz / near.w; + // metres along the ray per metre of the depth + float stretch = length(ray / -ray.z); + + vec3 light = vec3(0.0); + float transmittance = 1.0; + float front = slicing.x; + for (int slice = 0; slice < grid.z; slice++) + { + float back = slicing.x * exp(float(slice + 1) / slicing.z); + float span = (back - front) * stretch; + front = back; + vec4 cell = texelFetch(fog_scattering, ivec3(column, slice), 0); + float extinction = max(cell.a, 1e-7); + float through = exp(-extinction * span); + light += transmittance * cell.rgb * ((1.0 - through) / extinction); + transmittance *= through; + imageStore(fog_integrated, ivec3(column, slice), vec4(light, transmittance)); + } +} diff --git a/shaders/fog_volume.glsl b/shaders/fog_volume.glsl new file mode 100644 index 00000000..6b8cdc44 --- /dev/null +++ b/shaders/fog_volume.glsl @@ -0,0 +1,48 @@ +// fog between the camera and a point of the view space (weather_common.glsl): from the fog volume of the main view +// (fog_inject.comp, fog_integrate.comp) up to its far end and along the ray past it, or along the ray all the way in the +// other passes. Fragcoord: the pixel of the target the point is seen at +#ifndef FOG_VOLUME_GLSL +#define FOG_VOLUME_GLSL + +#include + +layout(binding = FOG_VOLUME_TEX) uniform sampler3D fog_volume; + +// rgb: light the fog adds, a: what it lets through +vec4 fog_along_view(vec3 view, vec2 fragcoord) +{ + float span_distance = length(view); + if (span_distance <= 0.0) + return vec4(0.0, 0.0, 0.0, 1.0); + vec3 direction = mat3(inv_view) * (view / span_distance); + if (fog_volume_params.x < 0.5) + return fog_analytic(direction, 0.0, span_distance, false); + // slices are spaced exponentially in the view depth; slice k holds the fog up to its far side + float depth = max(-view.z, 1e-4); + float slices = fog_volume_params.w; + float slice = log(max(depth / fog_volume_params.y, 1e-6)) * fog_volume_params.z; + vec2 coords = fragcoord * fog_volume_target.xy; + if (slice < 1.0) + return mix(vec4(0.0, 0.0, 0.0, 1.0), textureLod(fog_volume, vec3(coords, 0.5 / slices), 0.0), max(slice, 0.0)); + if (slice < slices) + return textureLod(fog_volume, vec3(coords, (slice - 0.5) / slices), 0.0); + vec4 volume = textureLod(fog_volume, vec3(coords, (slices - 0.5) / slices), 0.0); + vec4 rest = fog_analytic(direction, fog_volume_target.z * span_distance / depth, span_distance, false); + return vec4(volume.rgb + volume.a * rest.rgb, volume.a * rest.a); +} + +// fog in front of the sky in a view space direction: through the fog volume to its far end, then along the ray without the +// haze the atmosphere draws already. the far fog of the volume and the fog over the sky behind it are the same then +vec4 fog_sky(vec3 viewdirection, vec2 fragcoord) +{ + vec3 view = normalize(viewdirection); + vec3 direction = mat3(inv_view) * view; + if (fog_volume_params.x < 0.5 || view.z > -1e-3) + return fog_analytic(direction, 0.0, 200000.0, true); + float slices = fog_volume_params.w; + vec4 volume = textureLod(fog_volume, vec3(fragcoord * fog_volume_target.xy, (slices - 0.5) / slices), 0.0); + vec4 rest = fog_analytic(direction, fog_volume_target.z / -view.z, 200000.0, true); + return vec4(volume.rgb + volume.a * rest.rgb, volume.a * rest.a); +} + +#endif diff --git a/shaders/light_common.glsl b/shaders/light_common.glsl index 1e435afe..ccf8d785 100644 --- a/shaders/light_common.glsl +++ b/shaders/light_common.glsl @@ -11,6 +11,7 @@ layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_sh #include #include +#include float glossiness = 1.0; float metalic = 0.0; @@ -510,7 +511,9 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl // panels, vehicle bodies and terrain reads as a clear edge rather // than a soft Lambertian ramp. Tunable via SUN_NDOTL_SHARPNESS. float sun_NdotL = pow(sunlight.x, SUN_NDOTL_SHARPNESS); - float diffuseamount = sun_NdotL * lights[0].intensity; + // the clouds hold back the sun (the moon at night) where their shadow falls + float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz); + float diffuseamount = sun_NdotL * lights[0].intensity * cloudsun; float shadow1 = 0.0; if (shadowtone < 1.0) @@ -523,7 +526,7 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl fragcolor = add_headlights(fragcolor, fragnormal); - float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity + float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity * cloudsun * clamp(1.0 - shadowtone, 0.0, 1.0); if (shadowtone < 1.0) specularamount *= clamp(1.0 - shadow1, 0.0, 1.0); diff --git a/shaders/mat_sunlessnormalmap.frag b/shaders/mat_sunlessnormalmap.frag index 1e9e2ac7..5778253e 100644 --- a/shaders/mat_sunlessnormalmap.frag +++ b/shaders/mat_sunlessnormalmap.frag @@ -62,15 +62,17 @@ vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, fl // surfaces (uses SUN_NDOTL_SHARPNESS from light_common.glsl). float sun_NdotL = pow(sunlight.x, SUN_NDOTL_SHARPNESS); float diffuseamount = sun_NdotL * lights[0].intensity; + // the clouds hold back the sun where their shadow falls, as the shadow map does + float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz); fragcolor += envcolor * reflectivity; - float specularamount = (sunlight.y * param[1].y * specularity) * lights[0].intensity; + float specularamount = (sunlight.y * param[1].y * specularity) * lights[0].intensity * cloudsun; glossiness = abs(param[1].w); fragcolor = add_headlights(fragcolor, fragnormal); if (shadowtone < 1.0) { - float shadow = calc_shadow(); + float shadow = max(calc_shadow(), 1.0 - cloudsun); specularamount *= clamp(1.0 - shadow, 0.0, 1.0); fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * shadow + specularamount, 0.0, 1.0)); } diff --git a/shaders/mat_sunlessnormalmap_specgloss.frag b/shaders/mat_sunlessnormalmap_specgloss.frag index 6659c5fd..4ab9ae37 100644 --- a/shaders/mat_sunlessnormalmap_specgloss.frag +++ b/shaders/mat_sunlessnormalmap_specgloss.frag @@ -72,14 +72,16 @@ vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, fl // surfaces (uses SUN_NDOTL_SHARPNESS from light_common.glsl). float sun_NdotL = pow(sunlight.x, SUN_NDOTL_SHARPNESS); float diffuseamount = sun_NdotL * lights[0].intensity; + // the clouds hold back the sun where their shadow falls, as the shadow map does + float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz); - float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity; + float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity * cloudsun; fragcolor = add_headlights(fragcolor, fragnormal); if (shadowtone < 1.0) { - float shadow = calc_shadow(); + float shadow = max(calc_shadow(), 1.0 - cloudsun); specularamount *= clamp(1.0 - shadow, 0.0, 1.0); fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * shadow, 0.0, 1.0)); } diff --git a/shaders/postfx_ssao_apply.frag b/shaders/postfx_ssao_apply.frag index 795c1db7..259ed526 100644 --- a/shaders/postfx_ssao_apply.frag +++ b/shaders/postfx_ssao_apply.frag @@ -3,9 +3,13 @@ // close they are and by how close their distance is to that of the pixel, so the occlusion does not spill over the // edges of objects. each pixel of the occlusion stands for the top left one of the four full size pixels it covers // params.x: 0 ground truth ambient occlusion, 1 the older ssao (its own fixed curve) -// params.y: strength, a power of the occlusion (1 = as computed); params.z: albedo assumed by the multiple bounce term +// params.y: strength, a power of the occlusion (1 = as computed); params.z: albedo assumed by the multiple bounce term. +// the fog in front of a surface hides its occlusion as it hides the surface (fog_volume.glsl) in vec2 f_coords; + +#include +#include layout(location = 0) out vec4 out_color; layout(binding = 0) uniform sampler2D depth_tex; @@ -14,12 +18,17 @@ layout(binding = 1) uniform sampler2D ao_tex; layout(location = 0) uniform mat4 inv_projection; layout(location = 1) uniform vec4 params; -float view_distance(ivec2 pixel, float depth) +vec3 view_position(ivec2 pixel, float depth) { ivec2 full_size = textureSize(depth_tex, 0); vec2 uv = (vec2(pixel) + 0.5) / vec2(full_size); vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0); - return length(position.xyz / position.w); + return position.xyz / position.w; +} + +float view_distance(ivec2 pixel, float depth) +{ + return length(view_position(pixel, depth)); } // light which comes back after more than one bounce off surfaces of the given albedo, fit of Jimenez et al. 2016 @@ -76,5 +85,7 @@ void main() ao = pow(ao, params.y); ao = multi_bounce(ao, params.z); } + // what the fog lets through of the surface is what is left to occlude + ao = mix(1.0, ao, fog_along_view(view_position(pixel, depth), gl_FragCoord.xy).a); out_color = vec4(vec3(ao), 1.0); } diff --git a/shaders/sky_weather.frag b/shaders/sky_weather.frag new file mode 100644 index 00000000..c1114818 --- /dev/null +++ b/shaders/sky_weather.frag @@ -0,0 +1,52 @@ +// laid over the sky after the dome, the stars, the sun and the moon (opengl46_weather::draw_sky): the clouds (clouds_march.comp) +// and the fog in front of them, which hides the sky and the clouds towards the horizon. blended with (1, source alpha): the colour is +// what the clouds and the fog add, the alpha what they let through of the sky behind. +// mode 0: no clouds, 1: the clouds of the main view (an image over the screen), 2: the panorama of the clouds around the camera + +in vec2 f_coords; + +#include +#include +#include + +layout(binding = CLOUD_TEX) uniform sampler2D clouds; + +layout(location = 0) uniform mat4 inverse_projection; +layout(location = 1) uniform mat4 view_to_world; +layout(location = 2) uniform int mode; + +layout(location = 0) out vec4 out_color; +#if MOTIONBLUR_ENABLED +layout(location = 1) out vec4 out_motion; +#endif + +// as in clouds_march.comp +const float PANORAMA_LOW = -0.1; + +void main() +{ + vec4 near = inverse_projection * vec4(f_coords * 2.0 - 1.0, 1.0, 1.0); + vec3 direction = normalize(mat3(view_to_world) * normalize(near.xyz / near.w)); + vec4 cloud = vec4(0.0, 0.0, 0.0, 1.0); + if (mode == 1) + cloud = textureLod(clouds, f_coords, 0.0); + else if (mode == 2) + { + float elevation = asin(clamp(direction.y, -1.0, 1.0)); + float v = sqrt(clamp((elevation - PANORAMA_LOW) / (0.5 * WEATHER_PI - PANORAMA_LOW), 0.0, 1.0)); + float u = atan(direction.x, direction.z) / (2.0 * WEATHER_PI); + cloud = textureLod(clouds, vec2(fract(u), v), 0.0); + } + // the sky and the clouds are far: the fog all the way, without the haze the atmosphere has already (the clouds got it) + vec4 fog = fog_sky(near.xyz / near.w, gl_FragCoord.xy); + vec3 color = fog.rgb + fog.a * cloud.rgb; + float through = fog.a * cloud.a; +#if POSTFX_ENABLED + out_color = vec4(color, through); +#else + out_color = vec4(tonemap(vec4(color, 1.0)).rgb, through); +#endif +#if MOTIONBLUR_ENABLED + out_motion = vec4(0.0); +#endif +} diff --git a/shaders/stars.frag b/shaders/stars.frag new file mode 100644 index 00000000..0f1e66e6 --- /dev/null +++ b/shaders/stars.frag @@ -0,0 +1,26 @@ +// a star: a soft point added to the sky + +in vec3 f_color; + +#include +#include + +layout(location = 0) out vec4 out_color; +#if MOTIONBLUR_ENABLED +layout(location = 1) out vec4 out_motion; +#endif + +void main() +{ + vec2 offset = gl_PointCoord - 0.5; + float spot = exp(-dot(offset, offset) * 14.0); + vec3 color = f_color * spot; +#if POSTFX_ENABLED + out_color = vec4(color, 0.0); +#else + out_color = vec4(tonemap(vec4(color, 1.0)).rgb, 0.0); +#endif +#if MOTIONBLUR_ENABLED + out_motion = vec4(0.0); +#endif +} diff --git a/shaders/stars.vert b/shaders/stars.vert new file mode 100644 index 00000000..88309fb2 --- /dev/null +++ b/shaders/stars.vert @@ -0,0 +1,41 @@ +// the stars of the catalogue (environment/starcatalog.h) as points: turned from the equatorial frame to the sky of the observer +// by the sidereal time and the latitude, dimmed through the air towards the horizon and twinkling there + +layout(location = 0) in vec4 v_star; // xyz: direction in the equatorial frame (z to the north pole), w: visual magnitude +layout(location = 1) in vec4 v_color; // rgb: colour of the star, w: a random phase of the twinkling + +#include + +// equatorial frame to world axes (x west, y up, z north), and world axes to the view +layout(location = 0) uniform mat4 equatorial_to_world; +layout(location = 1) uniform mat4 world_to_view; +// x: brightness of a star of magnitude 0, y: seconds, z: size of the points in pixels +layout(location = 2) uniform vec4 params; + +out vec3 f_color; + +void main() +{ + vec3 world = mat3(equatorial_to_world) * v_star.xyz; + float elevation = world.y; + if (elevation < -0.01) + { + // below the horizon: out of the view + gl_Position = vec4(2.0, 2.0, 2.0, 1.0); + f_color = vec3(0.0); + return; + } + // air mass after Kasten and Young, 0.25 magnitude of extinction per air mass + float altitude = max(degrees(asin(clamp(elevation, 0.0, 1.0))), 0.0); + float airmass = 1.0 / (max(elevation, 0.0) + 0.50572 * pow(altitude + 6.07995, -1.6364)); + float magnitude = v_star.w + 0.25 * airmass; + // low stars twinkle + float twinkle = 1.0 + 0.35 * clamp((airmass - 1.0) * 0.25, 0.0, 1.0) * sin(params.y * (7.0 + 5.0 * v_color.w) + v_color.w * 40.0); + float flux = pow(10.0, -0.4 * magnitude) * params.x * twinkle; + f_color = v_color.rgb * flux; + vec3 view = mat3(world_to_view) * world; + gl_Position = projection * vec4(view, 1.0); + // at the far end of the depth (reverse z) + gl_Position.z = 0.0; + gl_PointSize = params.z * clamp(1.0 + 0.4 * log(1.0 + flux / max(params.x * 0.01, 1e-6)), 1.0, 2.5); +} diff --git a/shaders/weather_common.glsl b/shaders/weather_common.glsl new file mode 100644 index 00000000..af513b8d --- /dev/null +++ b/shaders/weather_common.glsl @@ -0,0 +1,163 @@ +// weather of the opengl 4.6 renderer (opengl46_weather, gl::weather_ubs): fog, clouds and their shadows. +// world axes around the camera: the positions here are world space offsets from the camera, in metres, y up; heights are +// above the ground reference of the weather. the fog is a haze whose density falls exponentially with the height and a fog +// lying on the ground, both of a constant density below the reference (in the valleys), lit with the colour the atmosphere +// has at the horizon in the direction of the view (fog_horizon), less the part of the sunlight held back by shadows +#ifndef WEATHER_COMMON_GLSL +#define WEATHER_COMMON_GLSL + +struct cloud_layer_s +{ + // x: base, y: top (metres above the ground reference), z: 1 / (top - base), w: genus (environment/weather.h) + vec4 heights; + // x: coverage 0-1, y: extinction at density 1 (1/m), or the optical depth of a flat layer, z: 1 / (size of the shape noise + // tile), w: erosion by the detail noise + vec4 shape; + // x: share of the depth over which the density rises from the base, y: share from which it falls towards the top, + // z: variation of the height of the tops, w: spread of the top (anvil) + vec4 profile; + // xy: position of the camera in the shape noise (metres, world x and z, wrapped to the tile), z: 1 / (size of the coverage + // noise tile), w: stretch along the wind + vec4 motion; + // x: 1 for a flat layer, y: offset of the layer in the noise (seed), zw: position of the camera in the coverage noise + vec4 extra; +}; + +layout(std140, binding = WEATHER_UBO_BINDING) uniform weather_ubo +{ + // x: extinction of the haze at the ground reference (1/m), y: 1 / its scale height, z: extinction the sky is not fogged + // with (the atmosphere has that haze already), w: albedo of the fog + vec4 fog_haze; + // x: extinction of the ground fog (1/m), y: 1 / scale height of its top, z: height its density starts to fall at, w: patchiness + vec4 fog_ground; + // xyz: position of the camera in the noise of the fog (metres, moved by the wind), w: 1 / size of the patches + vec4 fog_noise; + // x: share of the light of the fog which comes from the sun (and is held back by shadows), y: transmittance of the clouds + // for the sun on average, z: how much the dense ground fog is whitened, w: fog density of a regular draw (fog_density + // of a draw over it scales the optical depth) + vec4 fog_light; + // x: 1 when the fog volume holds the fog of the pass, y: near, z: slices / log(far / near), w: slices + vec4 fog_volume_params; + // xy: 1 / size of the target of the pass (pixels), z: far end of the volume (view depth), w: share of the old sun glow + vec4 fog_volume_target; + // x: height of the camera above the ground reference, y: seconds of simulation, z: 1 when the clouds are drawn, w: layers + vec4 weather_camera; + // xyz: world space direction towards the key light (the sun, or the moon at night), w: 1 when it is above the horizon + vec4 weather_sun_direction; + // rgb: colour of the key light as the shading gets it, without the shadow of the clouds; w: unused + vec4 weather_sun_color; + // rgb: ambient light of the sky above; w: unused + vec4 weather_ambient; + // xy: centre of the cloud shadow map (world x and z relative to the camera), z: 1 / its size (metres), w: 1 when it holds shadows + vec4 cloud_shadow_params; + // xy: world x and z of the direction the wind at the clouds blows to (unit), z: its speed, m/s + vec4 cloud_wind; + cloud_layer_s cloud_layers[MAX_CLOUD_LAYERS]; + // colour of the fog towards the horizon by the angle from the azimuth of the sun, 0 to 180 degrees, in the units of the + // scene. filled on the gpu from the sky-view table of the atmosphere + vec4 fog_horizon[FOG_HORIZON_SAMPLES]; +}; + +layout(binding = CLOUD_SHADOW_TEX) uniform sampler2D cloud_shadow_map; + +const float WEATHER_PI = 3.14159265359; + +// integral of the density of an exponential layer from the reference up to Height, the density being constant below Shift and +// falling with Rate (1 / scale height) above it +float fog_column(float height, float extinction, float rate, float shift) +{ + float above = height - shift; + if (above <= 0.0) + return extinction * height; + return extinction * (shift + (1.0 - exp(-rate * above)) / rate); +} + +// optical depth of the fog along a ray from the height Origin (above the reference), direction with the vertical part Up, +// between the distances Near and Far, for the haze and the ground fog of the given extinctions at the reference: x of the +// haze, y of the ground fog +vec2 fog_optical_depth(float origin, float up, float near, float far, float haze, float ground) +{ + float span = far - near; + if (span <= 0.0) + return vec2(0.0); + float h0 = origin + up * near; + float h1 = origin + up * far; + if (abs(up) * span < 0.5) + { + // nearly level: the density in the middle + float h = 0.5 * (h0 + h1); + return vec2(haze * exp(-fog_haze.y * max(h, 0.0)), ground * exp(-fog_ground.y * max(h - fog_ground.z, 0.0))) * span; + } + float hazedepth = fog_column(h1, haze, fog_haze.y, 0.0) - fog_column(h0, haze, fog_haze.y, 0.0); + float grounddepth = fog_column(h1, ground, fog_ground.y, fog_ground.z) - fog_column(h0, ground, fog_ground.y, fog_ground.z); + return max(vec2(hazedepth, grounddepth) / up, vec2(0.0)); +} + +// light of the fog in a world space direction: the colour of the horizon the atmosphere has that way, the old sun glow where +// the old sky is used. the dense ground fog is whiter than the air +vec3 fog_color_world(vec3 direction) +{ + vec3 sun = weather_sun_direction.xyz; + vec2 flat_view = direction.xz; + vec2 flat_sun = sun.xz; + float cosangle = 1.0; + if (dot(flat_view, flat_view) > 1e-8 && dot(flat_sun, flat_sun) > 1e-8) + cosangle = dot(normalize(flat_view), normalize(flat_sun)); + float position = acos(clamp(cosangle, -1.0, 1.0)) / WEATHER_PI * float(FOG_HORIZON_SAMPLES - 1); + int index = int(floor(position)); + vec3 color = mix(fog_horizon[clamp(index, 0, FOG_HORIZON_SAMPLES - 1)].rgb, fog_horizon[clamp(index + 1, 0, FOG_HORIZON_SAMPLES - 1)].rgb, fract(position)); + if (fog_volume_target.w > 0.0) + color += weather_sun_color.rgb * pow(max(dot(direction, sun), 0.0), 30.0) * fog_volume_target.w; + return color; +} + +// the light the fog takes from the sun, 0-1: the share the shadows may hold back is scaled by Sunlit +float fog_light_factor(float sunlit) +{ + return (1.0 - fog_light.x) + fog_light.x * sunlit; +} + +// the colour of the fog whitened by the share Ground of the dense ground fog in it +vec3 fog_whiten(vec3 color, float ground) +{ + float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722)); + return mix(color, vec3(luminance), clamp(ground, 0.0, 1.0) * fog_light.z); +} + +// fog along a ray of the view between two distances, of the haze and the ground fog of the given extinctions: rgb the light it +// adds, a what it lets through +vec4 fog_analytic_with(vec3 direction, float near, float far, float haze, float ground) +{ + vec2 depth = fog_optical_depth(weather_camera.x, direction.y, near, far, haze, ground); + float total = depth.x + depth.y; + float transmittance = exp(-total); + vec3 light = fog_whiten(fog_color_world(direction), depth.y / max(total, 1e-6)) * fog_light_factor(fog_light.y) * fog_haze.w; + return vec4(light * (1.0 - transmittance), transmittance); +} + +// fog along a ray of the view between two distances. Sky leaves out the haze the atmosphere draws in the sky already +vec4 fog_analytic(vec3 direction, float near, float far, bool sky) +{ + return fog_analytic_with(direction, near, far, sky ? max(fog_haze.x - fog_haze.z, 0.0) : fog_haze.x, fog_ground.x); +} + +// share of the key light the clouds let through to a point, World its offset from the camera +float cloud_shadow(vec3 world) +{ + if (cloud_shadow_params.w < 0.5) + return 1.0; + vec3 sun = weather_sun_direction.xyz; + if (sun.y < 0.02) + return fog_light.y; + // the map holds the shadow on the ground reference: down the ray of the sun to it + float height = weather_camera.x + world.y; + vec2 ground = world.xz - sun.xz * (height / sun.y); + vec2 coords = (ground - cloud_shadow_params.xy) * cloud_shadow_params.z + 0.5; + // towards the edges of the map the shadow fades into the average + vec2 edge = abs(coords - 0.5) * 2.0; + float fade = smoothstep(0.8, 1.0, max(edge.x, edge.y)); + float shadow = textureLod(cloud_shadow_map, coords, 0.0).r; + return mix(shadow, fog_light.y, fade); +} + +#endif diff --git a/simulation/simulation.cpp b/simulation/simulation.cpp index 70f9e307..a3f48b5d 100644 --- a/simulation/simulation.cpp +++ b/simulation/simulation.cpp @@ -108,6 +108,16 @@ state_manager::init_scripting_interface() { simulation::Memory.insert( memorycell ); simulation::Region->insert( memorycell ); } + { + // the weather as a report: text a weather report (METAR) or entries of the weather section, value1 the time of the + // change in seconds. holds the report of the weather it goes to + auto *memorycell = new TMemCell( { + 0, -1, + "__simulation.metar", + "memcell" } ); + simulation::Memory.insert( memorycell ); + simulation::Region->insert( memorycell ); + } } // legacy method, calculates changes in simulation state over specified time @@ -147,6 +157,7 @@ state_manager::update_scripting_interface() { auto *weather{ Memory.find( "__simulation.weather" ) }; auto *time{ Memory.find( "__simulation.time" ) }; auto *date{ Memory.find( "__simulation.date" ) }; + auto *metar{ Memory.find( "__simulation.metar" ) }; if( weather == nullptr || time == nullptr || date == nullptr ) { // scripting interface cells weren't created, nothing to synchronize @@ -167,6 +178,20 @@ state_manager::update_scripting_interface() { m_scriptinginterface.weather = std::make_shared( scene::node_data() ); m_scriptinginterface.date = std::make_shared( scene::node_data() ); m_scriptinginterface.time = std::make_shared( scene::node_data() ); + m_scriptinginterface.metar.clear(); + } + + if( metar != nullptr ) { + if( ( true == simulation::is_ready ) + && ( metar->Text() != m_scriptinginterface.metar ) ) { + // a script or an event put a report or weather entries in, the change takes value1 seconds + simulation::Environment.weather().apply_text( metar->Text(), static_cast( metar->Value1() ) ); + } + auto const report { to_metar( simulation::Environment.weather().target() ) }; + if( report != metar->Text() ) { + metar->UpdateValues( report, 0, 0, basic_event::flags::text ); + } + m_scriptinginterface.metar = report; } // update scripting interface diff --git a/simulation/simulation.h b/simulation/simulation.h index 0e96da68..04349ee8 100644 --- a/simulation/simulation.h +++ b/simulation/simulation.h @@ -78,6 +78,8 @@ private: state_serializer m_serializer; struct { std::shared_ptr weather, time, date; + // the weather report last written to __simulation.metar, to tell when something else changed it + std::string metar; } m_scriptinginterface; }; diff --git a/simulation/simulationenvironment.cpp b/simulation/simulationenvironment.cpp index 14ad2353..5ac804b0 100644 --- a/simulation/simulationenvironment.cpp +++ b/simulation/simulationenvironment.cpp @@ -67,6 +67,12 @@ world_environment::compute_weather() const { Global.AirTemperature > 1 ? "rain:" : "snow:"; + if( Global.Overcast > 1.f ) { + // a described weather may say what falls, whatever the temperature + if( auto const kind { m_weather.precipitation_override() } ) { + Global.Weather = ( *kind == precipitation_type::snow ? "snow:" : "rain:" ); + } + } Global.fTurbidity = Global.Overcast <= 0.10 ? 3 : Global.Overcast <= 0.20 ? 4 : @@ -87,6 +93,8 @@ world_environment::init() { m_moon.init(); m_stars.init(); m_clouds.Init(); + // the weather of the scenery, or the one made from its overcast and fog range, before anything reads them + m_weather.init(); m_precipitation.init(); { auto const rainsoundoverride { simulation::Sound_overrides.find( "weather.rainsound:" ) }; @@ -105,6 +113,8 @@ world_environment::init() { void world_environment::update() { + // the clouds move with the wind, a change of the weather goes on; the overcast and fog range follow a described weather + m_weather.update( Timer::GetDeltaTime(), Global.pCamera.Pos ); // move celestial bodies... m_sun.update(); m_moon.update(); diff --git a/simulation/simulationenvironment.h b/simulation/simulationenvironment.h index fcc93584..249d706e 100644 --- a/simulation/simulationenvironment.h +++ b/simulation/simulationenvironment.h @@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/. #include "environment/moon.h" #include "environment/stars.h" #include "environment/skydome.h" +#include "environment/weather.h" #include "rendering/precipitation.h" #include "audio/sound.h" @@ -62,6 +63,13 @@ public: inline auto const & wind_azimuth() const { return m_wind.azimuth; } + // clouds, fog and precipitation of the scenery, see environment/weather.h + inline auto const & + weather() const { + return m_weather; } + inline auto & + weather() { + return m_weather; } private: // types @@ -87,6 +95,7 @@ private: basic_precipitation m_precipitation; sound_source m_rainsound { sound_placement::external, -1 }; basic_wind m_wind; + world_weather m_weather; }; namespace simulation { diff --git a/simulation/simulationstateserializer.cpp b/simulation/simulationstateserializer.cpp index 1ff771d1..a4a75bec 100644 --- a/simulation/simulationstateserializer.cpp +++ b/simulation/simulationstateserializer.cpp @@ -64,6 +64,8 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) { Region = new scene::basic_region(); simulation::State.init_scripting_interface(); + // the weather section of the previous scenery doesn't carry over + simulation::Environment.weather().reset(); // NOTE: for the time being import from text format is a given, since we don't have full binary serialization std::shared_ptr state = @@ -134,6 +136,7 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) { { "test", &state_serializer::deserialize_test }, { "time", &state_serializer::deserialize_time }, { "trainset", &state_serializer::deserialize_trainset }, + { "weather", &state_serializer::deserialize_weather }, { "terrain", &state_serializer::deserialize_terrain }, { "heightmap_terrain", &state_serializer::deserialize_heightmapterrain }, { "editorterrain", &state_serializer::deserialize_editorterrain }, @@ -1041,6 +1044,13 @@ state_serializer::deserialize_sky( cParser &Input, scene::scratch_data &Scratchp skip_until( Input, "endsky" ); } +void +state_serializer::deserialize_weather( cParser &Input, scene::scratch_data &Scratchpad ) { + + // clouds, fog and precipitation, see environment/weather.h. wins over the atmo section + simulation::Environment.weather().deserialize( Input ); +} + void state_serializer::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) { @@ -1925,7 +1935,7 @@ std::pair state_serializer::preview_include(std::string const &Directi static std::unordered_map const statementends { { "event", "endevent" }, { "trainset", "endtrainset" }, { "isolated", "endisolated" }, { "area", "endarea" }, { "assignment", "endassignment" }, { "atmo", "endatmo" }, { "camera", "endcamera" }, { "config", "endconfig" }, { "description", "enddescription" }, { "light", "endlight" }, - { "sky", "endsky" }, { "test", "endtest" }, { "time", "endtime" }, { "terrain", "endterrain" }, { "heightmap_terrain", "endheightmap_terrain" }, { "editorterrain", "endeditorterrain" } }; + { "sky", "endsky" }, { "weather", "endweather" }, { "test", "endtest" }, { "time", "endtime" }, { "terrain", "endterrain" }, { "heightmap_terrain", "endheightmap_terrain" }, { "editorterrain", "endeditorterrain" } }; cParser parser(Directive, cParser::buffer_TEXT, Global.asCurrentSceneryPath, Global.bLoadTraction); // the template is processed with the placement its directive is going to be loaded with diff --git a/simulation/simulationstateserializer.h b/simulation/simulationstateserializer.h index 897749e7..95da529f 100644 --- a/simulation/simulationstateserializer.h +++ b/simulation/simulationstateserializer.h @@ -76,6 +76,7 @@ private: void deserialize_endscale( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_weather( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ); diff --git a/utilities/Globals.cpp b/utilities/Globals.cpp index cf23c9eb..6d0611f2 100644 --- a/utilities/Globals.cpp +++ b/utilities/Globals.cpp @@ -1644,6 +1644,41 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To Parser >> gfx_lights_shadows_headlights; gfx_lights_shadows_headlights = std::clamp( gfx_lights_shadows_headlights, 0, 4 ); } else if( Token == "gfx.lights.shadows.headlights.range" ) { readvalue( gfx_lights_shadows_headlights_range, 10.f, 1000.f ); } + else if( Token == "gfx.fog" ) { + // volumetric (yes) or analytic (no, old): the same weather either way + Parser.getTokens( 1 ); + std::string mode; + Parser >> mode; + gfx_fog = ( mode == "analytic" || mode == "old" || mode == "no" || mode == "false" || mode == "0" ) ? 0 : 1; } + else if( Token == "gfx.fog.quality" ) { + Parser.getTokens( 1 ); + std::string level; + Parser >> level; + gfx_fog_quality = ( level == "low" || level == "1" ) ? 1 : ( level == "high" || level == "3" ) ? 3 : ( level == "ultra" || level == "4" ) ? 4 : 2; } + else if( Token == "gfx.fog.range" ) { readvalue( gfx_fog_range, 100.f, 10000.f ); } + else if( Token == "gfx.fog.lights" ) { readvalue( gfx_fog_lights, 0.f, 10.f ); } + else if( Token == "gfx.fog.shadows" ) { readswitch( gfx_fog_shadows ); } + else if( Token == "gfx.fog.temporal" ) { readswitch( gfx_fog_temporal ); } + else if( Token == "gfx.clouds" ) { + // no, or the quality: low, medium (yes), high, ultra + Parser.getTokens( 1 ); + std::string level; + Parser >> level; + gfx_clouds = + ( level == "low" || level == "1" ) ? 1 : + ( level == "medium" || level == "yes" || level == "true" || level == "2" ) ? 2 : + ( level == "high" || level == "3" ) ? 3 : + ( level == "ultra" || level == "4" ) ? 4 : + 0; } + else if( Token == "gfx.clouds.distance" ) { readvalue( gfx_clouds_distance, 5000.f, 200000.f ); } + else if( Token == "gfx.clouds.shadows" ) { readswitch( gfx_clouds_shadows ); } + else if( Token == "gfx.clouds.shadows.size" ) { + Parser.getTokens( 1 ); + Parser >> gfx_clouds_shadows_size; + gfx_clouds_shadows_size = std::clamp( gfx_clouds_shadows_size, 64, 2048 ); } + else if( Token == "gfx.clouds.shadows.range" ) { readvalue( gfx_clouds_shadows_range, 1000.f, 100000.f ); } + else if( Token == "gfx.stars" ) { readswitch( gfx_stars ); } + else if( Token == "gfx.stars.brightness" ) { readvalue( gfx_stars_brightness, 0.f, 16.f ); } else { return false; } return true; @@ -1928,6 +1963,19 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "gfx.lights.shadows.vehicles.range", gfx_lights_shadows_vehicles_range ); export_as_text( Output, "gfx.lights.shadows.headlights", gfx_lights_shadows_headlights ); export_as_text( Output, "gfx.lights.shadows.headlights.range", gfx_lights_shadows_headlights_range ); + export_as_text( Output, "gfx.fog", std::string{ gfx_fog == 1 ? "volumetric" : "analytic" } ); + export_as_text( Output, "gfx.fog.quality", std::string{ gfx_fog_quality == 1 ? "low" : gfx_fog_quality == 3 ? "high" : gfx_fog_quality == 4 ? "ultra" : "medium" } ); + export_as_text( Output, "gfx.fog.range", gfx_fog_range ); + export_as_text( Output, "gfx.fog.lights", gfx_fog_lights ); + export_as_text( Output, "gfx.fog.shadows", gfx_fog_shadows ); + export_as_text( Output, "gfx.fog.temporal", gfx_fog_temporal ); + export_as_text( Output, "gfx.clouds", std::string{ gfx_clouds == 1 ? "low" : gfx_clouds == 2 ? "medium" : gfx_clouds == 3 ? "high" : gfx_clouds == 4 ? "ultra" : "no" } ); + export_as_text( Output, "gfx.clouds.distance", gfx_clouds_distance ); + export_as_text( Output, "gfx.clouds.shadows", gfx_clouds_shadows ); + export_as_text( Output, "gfx.clouds.shadows.size", gfx_clouds_shadows_size ); + export_as_text( Output, "gfx.clouds.shadows.range", gfx_clouds_shadows_range ); + export_as_text( Output, "gfx.stars", gfx_stars ); + export_as_text( Output, "gfx.stars.brightness", gfx_stars_brightness ); export_as_text( Output, "python.enabled", python_enabled ); export_as_text( Output, "python.threadedupload", python_threadedupload ); export_as_text( Output, "python.uploadmain", python_uploadmain ); diff --git a/utilities/Globals.h b/utilities/Globals.h index ab5ca4cf..60697e96 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -362,6 +362,27 @@ struct global_settings { // cast them, metres int gfx_lights_shadows_headlights { 2 }; float gfx_lights_shadows_headlights_range { 250.f }; + // weather of the opengl 4.6 renderer (debug window "Weather"); the weather itself comes from the scenery, see + // environment/weather.h. fog: 1 volumetric (a volume over the view with the shafts of the sun, the shadows of the clouds and + // the light of lamps and headlights, worked out along the ray past it), 0 analytic (the same fog along the ray only, the way + // the old one was drawn); the quality of the volume 1-4 (low, medium, high, ultra), its depth (metres), the brightness of the + // lamps in it (0: none), whether the sun and the headlights cast their shadows into it, blending over frames + int gfx_fog { 1 }; + int gfx_fog_quality { 2 }; + float gfx_fog_range { 1500.f }; + float gfx_fog_lights { 1.f }; + bool gfx_fog_shadows { true }; + bool gfx_fog_temporal { true }; + // volumetric clouds, 0 none, 1-4 the quality (low, medium, high, ultra); the farthest they are drawn (metres); the shadows + // they cast (a map around the camera: its size in texels and in metres) + int gfx_clouds { 2 }; + float gfx_clouds_distance { 40000.f }; + bool gfx_clouds_shadows { true }; + int gfx_clouds_shadows_size { 512 }; + float gfx_clouds_shadows_range { 16000.f }; + // stars from the catalogue, and their brightness + bool gfx_stars { true }; + float gfx_stars_brightness { 1.f }; float ui_fontsize = 13.0f; float ui_scale = 1.0f; diff --git a/widgets/weatherpanel.cpp b/widgets/weatherpanel.cpp new file mode 100644 index 00000000..b2fe5072 --- /dev/null +++ b/widgets/weatherpanel.cpp @@ -0,0 +1,95 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "widgets/weatherpanel.h" + +#include "environment/weather.h" + +bool ui::weather_controls(world_weather &Weather) +{ + static char input[512]{}; + static float transition{0.f}; + + auto conditions{Weather.target()}; + ImGui::TextWrapped("%s: %s", Weather.described() ? "described" : "made from the overcast", to_metar(conditions).c_str()); + ImGui::InputText("##weatherinput", input, sizeof(input)); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("a weather report (METAR), e.g. 24012KT 4000 -RA BR SCT008 BKN015 OVC040 08/06,\nor entries of the weather section, e.g. visibility 800 groundfog 60 300 clouds stratus ovc 150 400"); + ImGui::SameLine(); + auto changed{false}; + if (ImGui::Button("apply##weatherapply")) + changed = Weather.apply_text(input, transition); + ImGui::SliderFloat("change over, s##weathertransition", &transition, 0.f, 600.f, "%.0f"); + + auto edited{false}; + edited |= ImGui::SliderFloat("visibility, m##weathervisibility", &conditions.visibility, 50.f, 100000.f, "%.0f", ImGuiSliderFlags_Logarithmic); + edited |= ImGui::SliderFloat("haze height, m##weatherhaze", &conditions.haze_height, 50.f, 5000.f, "%.0f", ImGuiSliderFlags_Logarithmic); + edited |= ImGui::SliderFloat("ground fog, m##weathergroundfog", &conditions.groundfog_thickness, 0.f, 500.f, "%.0f"); + edited |= ImGui::SliderFloat("visibility in it, m##weathergroundfogvisibility", &conditions.groundfog_visibility, 20.f, 5000.f, "%.0f", ImGuiSliderFlags_Logarithmic); + edited |= ImGui::SliderFloat("patchiness##weathernoise", &conditions.fog_noise, 0.f, 1.f); + char const *precipitations[]{"by the temperature", "none", "rain", "snow"}; + auto precipitation{static_cast(conditions.precipitation)}; + if (ImGui::Combo("precipitation##weatherprecipitation", &precipitation, precipitations, IM_ARRAYSIZE(precipitations))) + { + conditions.precipitation = static_cast(precipitation); + edited = true; + } + edited |= ImGui::SliderFloat("intensity##weatherintensity", &conditions.precipitation_intensity, 0.f, 1.f); + edited |= ImGui::SliderFloat("wind from, deg##weatherwinddirection", &conditions.cloud_wind_direction, 0.f, 360.f, "%.0f"); + edited |= ImGui::SliderFloat("wind at the clouds, m/s##weatherwindspeed", &conditions.cloud_wind_speed, 0.f, 60.f, "%.1f"); + + std::vector genera; + for (int i = 0; i < static_cast(cloud_type::count); ++i) + genera.push_back(cloud_genus_info(static_cast(i)).name); + int removed{-1}; + for (std::size_t i = 0; i < conditions.clouds.size(); ++i) + { + auto &layer{conditions.clouds[i]}; + ImGui::PushID(static_cast(i)); + ImGui::Separator(); + auto type{static_cast(layer.type)}; + if (ImGui::Combo("genus", &type, genera.data(), static_cast(genera.size()))) + { + // a new genus gets its typical depth + layer.type = static_cast(type); + layer.top = layer.base + cloud_genus_info(layer.type).thickness; + edited = true; + } + auto oktas{layer.coverage * 8.f}; + if (ImGui::SliderFloat("coverage, oktas", &oktas, 0.f, 8.f, "%.1f")) + { + layer.coverage = oktas / 8.f; + edited = true; + } + edited |= ImGui::SliderFloat("base, m", &layer.base, 0.f, 12000.f, "%.0f"); + edited |= ImGui::SliderFloat("top, m", &layer.top, 0.f, 15000.f, "%.0f"); + edited |= ImGui::SliderFloat("density", &layer.density, 0.f, 3.f); + if (ImGui::Button("remove layer")) + removed = static_cast(i); + ImGui::PopID(); + } + if (removed >= 0) + { + conditions.clouds.erase(conditions.clouds.begin() + removed); + edited = true; + } + if (conditions.clouds.size() < weather_conditions::max_cloud_layers && ImGui::Button("add layer##weatheradd")) + { + auto const &genus{cloud_genus_info(cloud_type::cumulus)}; + conditions.clouds.push_back(cloud_layer{cloud_type::cumulus, 0.4f, genus.base, genus.base + genus.thickness, 1.f}); + edited = true; + } + if (edited) + { + Weather.apply(conditions, 0.f, true); + changed = true; + } + return changed; +} diff --git a/widgets/weatherpanel.h b/widgets/weatherpanel.h new file mode 100644 index 00000000..0f4903be --- /dev/null +++ b/widgets/weatherpanel.h @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +class world_weather; + +namespace ui +{ + +// controls of the weather of the simulation (environment/weather.h): a report or weather entries to apply, the layers of clouds, +// the fog, the precipitation and the wind at the clouds. what is changed applies to this computer at once, without being sent +// over the network. returns true when the weather was changed +bool weather_controls(world_weather &Weather); + +} // namespace ui