Full Code of andrewwillmott/sun-sky for AI

main 0afb061830b5 cached
14 files
391.9 KB
150.4k tokens
484 symbols
1 requests
Download .txt
Showing preview only (403K chars total). Download the full file or copy to clipboard to get everything.
Repository: andrewwillmott/sun-sky
Branch: main
Commit: 0afb061830b5
Files: 14
Total size: 391.9 KB

Directory structure:
gitextract_6nm16y2y/

├── .gitignore
├── HosekCubic.h
├── HosekDataXYZ.h
├── LICENSE.md
├── Makefile
├── README.md
├── SunSky.cpp
├── SunSky.hpp
├── SunSkyTool.cpp
├── VL234f.hpp
├── gen-images.sh
├── sky.sh
├── skybox_fs.sc
└── stb_image_mini.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
sunsky


================================================
FILE: HosekCubic.h
================================================
#if defined(__clang__)
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wmissing-braces"
#endif
#ifdef _MSC_VER
    // double->float
    #pragma warning(disable : 4305)
#endif

const float kHCX[10][4][2][4] =
{
    1.471043, 1.471043, 1.471043, 0.758034,     1.572623, 1.572623, 1.572623, 0.8574294,
    0.06211483, 0.06211483, 0.06211483, -14.77757,     -1.849289, -1.849289, -1.849289, -17.94017,
    13.80912, 13.80912, 13.80912, 33.88272,     16.35536, 16.35536, 16.35536, 36.58662,
    6.924387, 6.924387, 6.924387, 19.01121,     17.69317, 17.69317, 17.69317, 30.42875,

    -1.129483, -1.129483, -1.129483, -1.84131,     -1.131649, -1.131649, -1.131649, -1.877993,
    -1.146944, -1.146944, -1.146944, -4.513189,     -1.138856, -1.138856, -1.138856, -4.543661,
    -1.031804, -1.031804, -1.031804, -0.02013159,     -1.026747, -1.026747, -1.026747, 0.05537295,
    -1.072376, -1.072376, -1.072376, -1.161865,     -1.090789, -1.090789, -1.090789, -1.213788,

    -0.1890619, -0.1890619, -0.1890619, -0.9781779,     -0.1954455, -0.1954455, -0.1954455, -1.025135,
    -0.1989695, -0.1989695, -0.1989695, -2.169127,     -0.1932576, -0.1932576, -0.1932576, -2.237107,
    -0.1174885, -0.1174885, -0.1174885, -0.1769265,     -0.1240114, -0.1240114, -0.1240114, -0.1157619,
    -0.1819365, -0.1819365, -0.1819365, -0.1903353,     -0.1906185, -0.1906185, -0.1906185, -0.2468891,

    -9.065101, -9.065101, -9.065101, -4.610903,     -7.751595, -7.751595, -7.751595, -4.311037,
    6.90358, 6.90358, 6.90358, -6.342882,     5.907544, 5.907544, 5.907544, -3.683992,
    -2.278102, -2.278102, -2.278102, 4.859248,     -1.144623, -1.144623, -1.144623, 3.403649,
    1.951545, 1.951545, 1.951545, -1.057945,     2.379103, 2.379103, 2.379103, 0.2798364,

    9.659923, 9.659923, 9.659923, 4.824662,     8.685861, 8.685861, 8.685861, 4.715016,
    -1.256832, -1.256832, -1.256832, 11.42627,     -3.26039, -3.26039, -3.26039, 8.88321,
    -7.431575, -7.431575, -7.431575, -14.91425,     -2.056945, -2.056945, -2.056945, -13.34879,
    11.91233, 11.91233, 11.91233, 14.1308,     5.730488, 5.730488, 5.730488, 13.84061,

    -0.03607819, -0.03607819, -0.03607819, -0.05100806,     -0.04910871, -0.04910871, -0.04910871, -0.04711631,
    1.567098, 1.567098, 1.567098, -0.25833,     2.126393, 2.126393, 2.126393, -0.1852951,
    -2.984605, -2.984605, -2.984605, 1.169926,     -4.319021, -4.319021, -4.319021, 1.010055,
    -4.890863, -4.890863, -4.890863, -4.39955,     -3.860444, -3.860444, -3.860444, -4.504395,

    0.8314359, 0.8314359, 0.8314359, 0.6463776,     0.8992952, 0.8992952, 0.8992952, 0.6335844,
    0.6585674, 0.6585674, 0.6585674, 0.6981739,     0.4717289, 0.4717289, 0.4717289, 0.1284183,
    1.555342, 1.555342, 1.555342, -0.3360626,     1.649114, 1.649114, 1.649114, 0.5380275,
    0.633864, 0.633864, 0.633864, 0.4671616,     -0.2866762, -0.2866762, -0.2866762, -0.3228472,

    0.08181661, 0.08181661, 0.08181661, -6.377724e-06,     0.04710143, 0.04710143, 0.04710143, -7.665398e-06,
    0.1454988, 0.1454988, 0.1454988, 0.2645415,     0.1985848, 0.1985848, 0.1985848, 0.2123163,
    0.1480832, 0.1480832, 0.1480832, -0.004541874,     0.02987276, 0.02987276, 0.02987276, 0.09329225,
    0.1417575, 0.1417575, 0.1417575, 0.4694801,     0.09714939, 0.09714939, 0.09714939, 0.2526912,

    4.768868, 4.768868, 4.768868, 2.216875,     4.254818, 4.254818, 4.254818, 1.788017,
    3.128158, 3.128158, 3.128158, 3.146712,     2.910323, 2.910323, 2.910323, 2.880383,
    2.114148, 2.114148, 2.114148, -1.956962,     1.846372, 1.846372, 1.846372, -2.190931,
    2.104099, 2.104099, 2.104099, 1.781559,     1.535069, 1.535069, 1.535069, 1.028129,

    0.6339777, 0.6339777, 0.6339777, 0.861853,     0.6821116, 0.6821116, 0.6821116, 0.9001409,
    0.6501364, 0.6501364, 0.6501364, 0.2343821,     0.6093358, 0.6093358, 0.6093358, 0.1875418,
    0.7585136, 0.7585136, 0.7585136, 0.9343759,     0.8037416, 0.8037416, 0.8037416, 0.9764438,
    0.621865, 0.621865, 0.621865, 0.6298435,     0.6047409, 0.6047409, 0.6047409, 0.6153335,

};

const float kHCY[10][4][2][4] =
{
    1.522034, 1.522034, 1.522034, 0.7985143,     1.63172, 1.63172, 1.63172, 0.903157,
    0.01937461, 0.01937461, 0.01937461, -15.66538,     -2.025983, -2.025983, -2.025983, -18.97756,
    15.02212, 15.02212, 15.02212, 35.28313,     17.82944, 17.82944, 17.82944, 38.12075,
    7.200506, 7.200506, 7.200506, 20.2541,     18.83474, 18.83474, 18.83474, 32.12934,

    -1.144464, -1.144464, -1.144464, -1.896737,     -1.146417, -1.146417, -1.146417, -1.989159,
    -1.165054, -1.165054, -1.165054, -4.528923,     -1.156448, -1.156448, -1.156448, -4.570587,
    -1.031311, -1.031311, -1.031311, -0.09059,     -1.023365, -1.023365, -1.023365, -0.02246192,
    -1.080783, -1.080783, -1.080783, -1.144353,     -1.100497, -1.100497, -1.100497, -1.192592,

    -0.2043799, -0.2043799, -0.2043799, -1.005442,     -0.2119353, -0.2119353, -0.2119353, -1.09516,
    -0.2004164, -0.2004164, -0.2004164, -2.13232,     -0.1969813, -0.1969813, -0.1969813, -2.206897,
    -0.1307805, -0.1307805, -0.1307805, -0.2715155,     -0.1283175, -0.1283175, -0.1283175, -0.2106788,
    -0.1759721, -0.1759721, -0.1759721, -0.1738116,     -0.1921495, -0.1921495, -0.1921495, -0.2334839,

    -10.20188, -10.20188, -10.20188, -6.411032,     -7.187525, -7.187525, -7.187525, -2.91555,
    7.684557, 7.684557, 7.684557, -7.169267,     5.387022, 5.387022, 5.387022, -4.545533,
    -2.61168, -2.61168, -2.61168, 5.600186,     -0.7958836, -0.7958836, -0.7958836, 3.865181,
    2.071102, 2.071102, 2.071102, -1.244332,     2.340057, 2.340057, 2.340057, 0.1678497,

    10.71247, 10.71247, 10.71247, 6.54822,     8.058599, 8.058599, 8.058599, 3.275339,
    -1.234814, -1.234814, -1.234814, 11.66098,     -2.357146, -2.357146, -2.357146, 10.73598,
    -8.965322, -8.965322, -8.965322, -14.42182,     -3.07873, -3.07873, -3.07873, -15.61103,
    14.55553, 14.55553, 14.55553, 12.77613,     5.908181, 5.908181, 5.908181, 15.81022,

    -0.03256693, -0.03256693, -0.03256693, -0.03227596,     -0.05256438, -0.05256438, -0.05256438, -0.05735765,
    2.449292, 2.449292, 2.449292, -0.6445193,     2.653214, 2.653214, 2.653214, -0.09496659,
    -4.829063, -4.829063, -4.829063, 1.847776,     -5.56008, -5.56008, -5.56008, 0.8480998,
    -4.762894, -4.762894, -4.762894, -4.721684,     -3.895817, -3.895817, -3.895817, -4.460663,

    0.7860205, 0.7860205, 0.7860205, 0.5717262,     0.8375733, 0.8375733, 0.8375733, 0.5742174,
    0.5882703, 0.5882703, 0.5882703, 1.000333,     0.4095826, 0.4095826, 0.4095826, 0.23352,
    1.657856, 1.657856, 1.657856, -0.744886,     1.714468, 1.714468, 1.714468, 0.4990387,
    0.6540962, 0.6540962, 0.6540962, 0.8189706,     -0.2843079, -0.2843079, -0.2843079, -0.3637103,

    0.06872719, 0.06872719, 0.06872719, -8.115192e-06,     0.03887093, 0.03887093, 0.03887093, -7.683288e-06,
    0.1236639, 0.1236639, 0.1236639, 0.2336697,     0.1680872, 0.1680872, 0.1680872, 0.1651105,
    0.1423908, 0.1423908, 0.1423908, -0.008540288,     0.04715908, 0.04715908, 0.04715908, 0.119553,
    0.1537134, 0.1537134, 0.1537134, 0.4823636,     0.1017511, 0.1017511, 0.1017511, 0.2322143,

    4.824771, 4.824771, 4.824771, 2.296704,     4.222111, 4.222111, 4.222111,  1.7634,
    3.988477, 3.988477, 3.988477, 3.374217,     3.698663, 3.698663, 3.698663, 3.098112,
    1.194806, 1.194806, 1.194806, -2.087303,     1.141016, 1.141016, 1.141016, -2.325808,
    2.351988, 2.351988, 2.351988, 1.793429,     1.648862, 1.648862, 1.648862, 1.025036,

    0.6259836, 0.6259836, 0.6259836, 0.9000749,     0.6695347, 0.6695347, 0.6695347, 0.9001342,
      0.661,   0.661,   0.661, 0.2071559,     0.6285347, 0.6285347, 0.6285347, 0.1926854,
    0.7467407, 0.7467407, 0.7467407, 0.9458723,     0.7764328, 0.7764328, 0.7764328, 0.9730061,
    0.6239002, 0.6239002, 0.6239002, 0.627587,     0.6200643, 0.6200643, 0.6200643, 0.6151967,

};

const float kHCZ[10][4][2][4] =
{
    1.107408, 1.107408, 1.107408, 0.7623528,     1.286344, 1.286344, 1.286344, 0.8640731,
    -5.212171, -5.212171, -5.212171, -16.35464,     -8.835278, -8.835278, -8.835278, -18.67052,
    22.17711, 22.17711, 22.17711, 32.60661,     25.82934, 25.82934, 25.82934, 33.59812,
    13.87383, 13.87383, 13.87383, 24.37655,     32.24473, 32.24473, 32.24473, 38.16179,

    -1.353023, -1.353023, -1.353023, -2.728867,     -1.325672, -1.325672, -1.325672, -2.770408,
    -1.266555, -1.266555, -1.266555, -3.985086,     -1.257786, -1.257786, -1.257786, -4.017327,
    -0.9608197, -0.9608197, -0.9608197, -0.2876793,     -0.9304037, -0.9304037, -0.9304037, -0.08642912,
    -1.093978, -1.093978, -1.093978, -1.161188,     -1.106813, -1.106813, -1.106813, -1.147125,

    -0.4813523, -0.4813523, -0.4813523, -1.580388,     -0.4769313, -0.4769313, -0.4769313, -1.618911,
    -0.3736056, -0.3736056, -0.3736056, -2.138492,     -0.3975163, -0.3975163, -0.3975163, -2.227182,
    -0.1174827, -0.1174827, -0.1174827, -0.1843867,     -0.07153632, -0.07153632, -0.07153632, -0.1488523,
    -0.2007022, -0.2007022, -0.2007022, -0.3973276,     -0.2098484, -0.2098484, -0.2098484, -0.2583446,

    -31.0492, -31.0492, -31.0492, -30.79627,     -41.11215, -41.11215, -41.11215, -25.04031,
    -0.2305174, -0.2305174, -0.2305174, 18.77094,     5.851173, 5.851173, 5.851173, 2.370717,
    3.809405, 3.809405, 3.809405, -10.44642,     0.9176657, 0.9176657, 0.9176657, 0.8439043,
    -1.424772, -1.424772, -1.424772, 2.276975,     0.5190111, 0.5190111, 0.5190111, 0.5619681,

    31.40156, 31.40156, 31.40156, 30.92586,     41.68293, 41.68293, 41.68293, 25.31674,
    0.04713231, 0.04713231, 0.04713231, -18.70684,     -5.894221, -5.894221, -5.894221, -0.9216983,
    -3.092963, -3.092963, -3.092963, 10.31492,     0.2694842, 0.2694842, 0.2694842, -3.038832,
    2.058657, 2.058657, 2.058657, -0.5924354,     0.5363316, 0.5363316, 0.5363316, 3.965227,

    -0.009510741, -0.009510741, -0.009510741, -0.004197673,     -0.006274997, -0.006274997, -0.006274997, -0.004239279,
    -0.1208266, -0.1208266, -0.1208266, -0.2283553,     -0.03763597, -0.03763597, -0.03763597, -0.856935,
    0.1478871, 0.1478871, 0.1478871, 0.4028665,     -0.007877082, -0.007877082, -0.007877082, 2.010754,
    0.05271995, 0.05271995, 0.05271995, -1.454436,     0.1698868, 0.1698868, 0.1698868, -3.640249,

    0.554203, 0.554203, 0.554203, 0.3154759,     0.4649469, 0.4649469, 0.4649469, 0.3241013,
    0.981547, 0.981547, 0.981547, 0.5661614,     0.9146686, 0.9146686, 0.9146686, 0.7383983,
    -0.06462997, -0.06462997, -0.06462997, 0.1634221,     0.006675631, 0.006675631, 0.006675631, -0.1595713,
    1.522406, 1.522406, 1.522406, 0.2908837,     0.8347491, 0.8347491, 0.8347491, 0.3629976,

    0.008135471, 0.008135471, 0.008135471, -3.897675e-06,     0.01119411, 0.01119411, 0.01119411, -3.764484e-06,
    -0.008283749, -0.008283749, -0.008283749, -0.05800496,     -0.00357069, -0.00357069, -0.00357069, -0.05083194,
    0.110339, 0.110339, 0.110339, 0.1772075,     0.08754471, 0.08754471, 0.08754471, 0.1686509,
    0.1131037, 0.1131037, 0.1131037, 0.2486994,     0.05040883, 0.05040883, 0.05040883, 0.1849058,

    3.136646, 3.136646, 3.136646, 1.920567,     2.631267, 2.631267, 2.631267, 1.586843,
    4.22313, 4.22313, 4.22313, 3.329891,     3.320134, 3.320134, 3.320134, 3.273266,
    1.294505, 1.294505, 1.294505, -0.6514511,     1.608702, 1.608702, 1.608702, -1.662017,
    3.47811, 3.47811, 3.47811, 1.170225,     3.033226, 3.033226, 3.033226, 1.452241,

    0.5215989, 0.5215989, 0.5215989, 0.6664791,     0.5234546, 0.5234546, 0.5234546, 0.7035906,
    0.769796, 0.769796, 0.769796, 0.4996695,     0.794625, 0.794625, 0.794625, 0.4611094,
    0.5914896, 0.5914896, 0.5914896, 0.7820878,     0.5848916, 0.5848916, 0.5848916, 0.8114535,
    0.6819321, 0.6819321, 0.6819321, 0.6657748,     0.696082, 0.696082, 0.696082, 0.6572598,

};

#if defined(__clang__)
#pragma clang diagnostic pop
#endif


================================================
FILE: HosekDataXYZ.h
================================================
/*
This source is published under the following 3-clause BSD license.

Copyright (c) 2012 - 2013, Lukas Hosek and Alexander Wilkie
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * None of the names of the contributors may be used to endorse or promote
      products derived from this software without specific prior written
      permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


/* ============================================================================

This file is part of a sample implementation of the analytical skylight and
solar radiance models presented in the SIGGRAPH 2012 paper


           "An Analytic Model for Full Spectral Sky-Dome Radiance"

and the 2013 IEEE CG&A paper

       "Adding a Solar Radiance Function to the Hosek Skylight Model"

                                   both by

                       Lukas Hosek and Alexander Wilkie
                Charles University in Prague, Czech Republic


                        Version: 1.4a, February 22nd, 2013

Version history:

1.4a  February 22nd, 2013
      Removed unnecessary and counter-intuitive solar radius parameters
      from the interface of the colourspace sky dome initialisation functions.

1.4   February 11th, 2013
      Fixed a bug which caused the relative brightness of the solar disc
      and the sky dome to be off by a factor of about 6. The sun was too
      bright: this affected both normal and alien sun scenarios. The
      coefficients of the solar radiance function were changed to fix this.

1.3   January 21st, 2013 (not released to the public)
      Added support for solar discs that are not exactly the same size as
      the terrestrial sun. Also added support for suns with a different
      emission spectrum ("Alien World" functionality).

1.2a  December 18th, 2012
      Fixed a mistake and some inaccuracies in the solar radiance function
      explanations found in ArHosekSkyModel.h. The actual source code is
      unchanged compared to version 1.2.

1.2   December 17th, 2012
      Native RGB data and a solar radiance function that matches the turbidity
      conditions were added.

1.1   September 2012
      The coefficients of the spectral model are now scaled so that the output
      is given in physical units: W / (m^-2 * sr * nm). Also, the output of the
      XYZ model is now no longer scaled to the range [0...1]. Instead, it is
      the result of a simple conversion from spectral data via the CIE 2 degree
      standard observer matching functions. Therefore, after multiplication
      with 683 lm / W, the Y channel now corresponds to luminance in lm.

1.0   May 11th, 2012
      Initial release.


Please visit http://cgg.mff.cuni.cz/projects/SkylightModelling/ to check if
an updated version of this code has been published!

============================================================================ */


/*

This file contains the coefficient data for the XYZ colour space version of
the model.

*/

#if defined(__clang__)
	#pragma clang diagnostic push
	#pragma clang diagnostic ignored "-Wmissing-braces"
#endif
#ifdef _MSC_VER
	// double->float
	#pragma warning(disable : 4305)
#endif

// Uses Sep 9 pattern / Aug 23 mean dataset

// [albedo][turbidity][quinticCoeffs][coeffs]
float kHosekCoeffsX[2][10][6][9] =
{
	// albedo 0, turbidity 1
	-1.117001e+000,
	-1.867262e-001,
	-1.113505e+001,
	1.259865e+001,
	-3.937339e-002,
	1.167571e+000,

	7.100686e-003,
	3.592678e+000,
	6.083296e-001,
	-1.152006e+000,
	-1.926669e-001,
	6.152049e+000,

	-4.770802e+000,
	-8.704701e-002,
	7.483626e-001,
	3.372718e-002,
	4.464592e+000,
	4.036546e-001,
	-1.072371e+000,
	-2.696632e-001,
	2.816168e-001,
	1.820571e+000,
	-3.742666e-001,
	2.080607e+000,
	-7.675295e-002,
	-2.835366e+000,
	1.129329e+000,
	-1.109935e+000,
	-1.532764e-001,
	1.198787e+000,
	-9.015183e-001,
	5.173015e-003,
	5.749178e-001,
	1.075633e-001,
	4.387949e+000,
	2.650413e-001,
	-1.052297e+000,
	-2.229452e-001,
	1.952347e+000,
	5.727205e-001,
	-4.885070e+000,
	1.984016e+000,
	-1.106197e-001,
	-4.898361e-001,
	8.907873e-001,
	-1.070108e+000,
	-1.600465e-001,
	1.593886e+000,
	-4.479251e-005,
	-3.306541e+000,
	9.390193e-001,
	9.513168e-002,
	2.343583e+000,
	5.335404e-001,
	// albedo 0, turbidity 2
	-1.113253e+000,
	-1.699600e-001,
	-1.038822e+001,
	1.137513e+001,
	-4.040911e-002,
	1.037455e+000,
	4.991792e-002,
	4.801919e+000,
	6.302710e-001,
	-1.135747e+000,
	-1.678594e-001,
	4.970755e+000,
	-4.430230e+000,
	-6.657408e-002,
	3.636161e-001,
	1.558009e-001,
	6.013370e+000,
	3.959601e-001,
	-1.095892e+000,
	-2.732595e-001,
	7.666496e-001,
	1.350731e+000,
	-4.401401e-001,
	2.470135e+000,
	-1.707929e-001,
	-3.260793e+000,
	1.170337e+000,
	-1.073668e+000,
	-2.603929e-002,
	-1.944589e-001,
	4.575207e-001,
	6.878164e-001,
	-1.390770e-001,
	3.690299e-001,
	7.885781e+000,
	1.877694e-001,
	-1.070091e+000,
	-2.798957e-001,
	2.338478e+000,
	-2.647221e+000,
	-7.387808e+000,
	2.329210e+000,
	-1.644639e-001,
	-2.003710e+000,
	9.874527e-001,
	-1.067120e+000,
	-1.418866e-001,
	1.254090e+000,
	6.053048e+000,
	-2.918892e+000,
	5.322812e-001,
	1.613053e-001,
	3.018161e+000,
	5.274090e-001,
	// albedo 0, turbidity 3
	-1.129483e+000,
	-1.890619e-001,
	-9.065101e+000,
	9.659923e+000,
	-3.607819e-002,
	8.314359e-001,
	8.181661e-002,
	4.768868e+000,
	6.339777e-001,
	-1.146420e+000,
	-1.883579e-001,
	3.309173e+000,
	-3.127882e+000,
	-6.938176e-002,
	3.987113e-001,
	1.400581e-001,
	6.283042e+000,
	5.267076e-001,
	-1.128348e+000,
	-2.641305e-001,
	1.223176e+000,
	5.514952e-002,
	-3.490649e-001,
	1.997784e+000,
	-4.123709e-002,
	-2.251251e+000,
	9.483466e-001,
	-1.025820e+000,
	1.404690e-002,
	-1.187406e+000,
	2.729900e+000,
	5.877588e-001,
	-2.761140e-001,
	4.602633e-001,
	8.305125e+000,
	3.945001e-001,
	-1.083957e+000,
	-2.606679e-001,
	2.207108e+000,
	-7.202803e+000,
	-5.968103e+000,
	2.129455e+000,
	-7.789512e-002,
	-1.137688e+000,
	8.871769e-001,
	-1.062465e+000,
	-1.512189e-001,
	1.042881e+000,
	1.427839e+001,
	-4.242214e+000,
	4.038100e-001,
	1.997780e-001,
	2.814449e+000,
	5.803196e-001,
	// albedo 0, turbidity 4
	-1.175099e+000,
	-2.410789e-001,
	-1.108587e+001,
	1.133404e+001,
	-1.819300e-002,
	6.772942e-001,
	9.605043e-002,
	4.231166e+000,
	6.239972e-001,
	-1.224207e+000,
	-2.883527e-001,
	3.002206e+000,
	-2.649612e+000,
	-4.795418e-002,
	4.984398e-001,
	3.251434e-002,
	4.851611e+000,
	6.551019e-001,
	-1.136955e+000,
	-2.423048e-001,
	1.058823e+000,
	-2.489236e-001,
	-2.462179e-001,
	1.933140e+000,
	9.106828e-002,
	-1.905869e-001,
	8.171065e-001,
	-1.014535e+000,
	-8.262500e-003,
	-1.448017e+000,
	2.295788e+000,
	3.510334e-001,
	-1.477418e+000,
	5.432449e-001,
	5.762796e+000,
	4.908751e-001,
	-1.070666e+000,
	-2.379780e-001,
	1.844589e+000,
	-5.442448e+000,
	-4.012768e+000,
	2.945275e+000,
	9.854725e-003,
	8.455959e-002,
	8.145030e-001,
	-1.071525e+000,
	-1.777132e-001,
	8.076590e-001,
	9.925865e+000,
	-3.324623e+000,
	-6.367437e-001,
	2.844581e-001,
	2.248384e+000,
	6.544022e-001,
	// albedo 0, turbidity 5
	-1.218818e+000,
	-2.952382e-001,
	-1.345975e+001,
	1.347153e+001,
	-6.814585e-003,
	5.079068e-001,
	1.197230e-001,
	3.776949e+000,
	5.836961e-001,
	-1.409868e+000,
	-5.114330e-001,
	2.776539e+000,
	-2.039001e+000,
	-2.673769e-002,
	4.145288e-001,
	7.829342e-004,
	2.275883e+000,
	6.629691e-001,
	-1.069151e+000,
	-9.434247e-002,
	7.293972e-001,
	-1.222473e+000,
	-1.533461e-001,
	2.160357e+000,
	4.626837e-002,
	3.852415e+000,
	8.593570e-001,
	-1.021306e+000,
	-1.149551e-001,
	-1.108414e+000,
	4.178343e+000,
	4.013665e-001,
	-2.222814e+000,
	6.929462e-001,
	1.392652e+000,
	4.401662e-001,
	-1.074251e+000,
	-2.224002e-001,
	1.372356e+000,
	-8.858704e+000,
	-3.922660e+000,
	3.020018e+000,
	-1.458724e-002,
	1.511186e+000,
	8.288064e-001,
	-1.062048e+000,
	-1.526582e-001,
	4.921067e-001,
	1.485522e+001,
	-3.229936e+000,
	-8.426604e-001,
	3.916243e-001,
	2.678994e+000,
	6.689264e-001,
	// albedo 0, turbidity 6
	-1.257023e+000,
	-3.364700e-001,
	-1.527795e+001,
	1.504223e+001,
	2.717715e-003,
	3.029910e-001,
	1.636851e-001,
	3.561663e+000,
	5.283161e-001,
	-1.635124e+000,
	-7.329993e-001,
	3.523939e+000,
	-2.566337e+000,
	-1.902543e-002,
	5.505483e-001,
	-6.242176e-002,
	1.065992e+000,
	6.654236e-001,
	-9.295823e-001,
	4.845834e-002,
	-2.992990e-001,
	-2.001327e-001,
	-8.019339e-002,
	1.807806e+000,
	9.020277e-002,
	5.095372e+000,
	8.639936e-001,
	-1.093740e+000,
	-2.148608e-001,
	-5.216240e-001,
	2.119777e+000,
	9.506454e-002,
	-1.831439e+000,
	6.961204e-001,
	1.102084e-001,
	4.384319e-001,
	-1.044181e+000,
	-1.849257e-001,
	9.071246e-001,
	-4.648901e+000,
	-2.279385e+000,
	2.356502e+000,
	-4.169147e-002,
	1.932557e+000,
	8.296550e-001,
	-1.061451e+000,
	-1.458745e-001,
	2.952267e-001,
	8.967214e+000,
	-3.726228e+000,
	-5.022316e-001,
	5.684877e-001,
	3.102347e+000,
	6.658443e-001,
	// albedo 0, turbidity 7
	-1.332391e+000,
	-4.127769e-001,
	-9.328643e+000,
	9.046194e+000,
	3.457775e-003,
	3.377425e-001,
	1.530909e-001,
	3.301209e+000,
	4.997917e-001,
	-1.932002e+000,
	-9.947777e-001,
	-2.042329e+000,
	3.586940e+000,
	-5.642182e-002,
	8.130478e-001,
	-8.195988e-002,
	1.118294e-001,
	5.617231e-001,
	-8.707374e-001,
	1.286999e-001,
	1.820054e+000,
	-4.674706e+000,
	3.317471e-003,
	5.919018e-001,
	1.975278e-001,
	6.686519e+000,
	9.631727e-001,
	-1.070378e+000,
	-3.030579e-001,
	-9.041938e-001,
	6.200201e+000,
	1.232207e-001,
	-3.650628e-001,
	5.029403e-001,
	-2.903162e+000,
	3.811408e-001,
	-1.063035e+000,
	-1.637545e-001,
	5.853072e-001,
	-7.889906e+000,
	-1.200641e+000,
	1.035018e+000,
	1.192093e-001,
	3.267054e+000,
	8.416151e-001,
	-1.053655e+000,
	-1.562286e-001,
	2.423683e-001,
	1.128575e+001,
	-4.363262e+000,
	-7.314160e-002,
	5.642088e-001,
	2.514023e+000,
	6.670457e-001,
	// albedo 0, turbidity 8
	-1.366112e+000,
	-4.718287e-001,
	-7.876222e+000,
	7.746900e+000,
	-9.182309e-003,
	4.716076e-001,
	8.320252e-002,
	3.165603e+000,
	5.392334e-001,
	-2.468204e+000,
	-1.336340e+000,
	-5.386723e+000,
	7.072672e+000,
	-8.329266e-002,
	8.636876e-001,
	-1.978177e-002,
	-1.326218e-001,
	2.979222e-001,
	-9.653522e-001,
	-2.373416e-002,
	1.810250e+000,
	-6.467262e+000,
	1.410706e-001,
	-4.753717e-001,
	3.003095e-001,
	6.551163e+000,
	1.151083e+000,
	-8.943186e-001,
	-2.487152e-001,
	-2.308960e-001,
	8.512648e+000,
	1.298402e-001,
	1.034705e+000,
	2.303509e-001,
	-3.924095e+000,
	2.982717e-001,
	-1.146999e+000,
	-2.318784e-001,
	8.992419e-002,
	-9.933614e+000,
	-8.860920e-001,
	-3.071656e-002,
	2.852012e-001,
	3.046199e+000,
	8.599001e-001,
	-1.032399e+000,
	-1.645145e-001,
	2.683599e-001,
	1.327701e+001,
	-4.407670e+000,
	7.709869e-002,
	4.951727e-001,
	1.957277e+000,
	6.630943e-001,
	// albedo 0, turbidity 9
	-1.469070e+000,
	-6.135092e-001,
	-6.506263e+000,
	6.661315e+000,
	-3.835383e-002,
	7.150413e-001,
	7.784318e-003,
	2.820577e+000,
	6.756784e-001,
	-2.501583e+000,
	-1.247404e+000,
	-1.523462e+001,
	1.633191e+001,
	-1.204803e-002,
	5.896471e-001,
	-2.002023e-002,
	1.144647e+000,
	6.177874e-002,
	-2.438672e+000,
	-1.127291e+000,
	5.731172e+000,
	-1.021350e+001,
	6.165610e-002,
	-7.752641e-001,
	4.708254e-001,
	4.176847e+000,
	1.200881e+000,
	-1.513427e-001,
	9.792731e-002,
	-1.612349e+000,
	9.814289e+000,
	5.188921e-002,
	1.716403e+000,
	-7.039255e-002,
	-2.815115e+000,
	3.291874e-001,
	-1.318511e+000,
	-3.650554e-001,
	4.221268e-001,
	-9.294529e+000,
	-4.397520e-002,
	-8.100625e-001,
	3.742719e-001,
	1.834166e+000,
	8.223450e-001,
	-1.016009e+000,
	-1.820264e-001,
	1.278426e-001,
	1.182696e+001,
	-4.801528e+000,
	4.947899e-001,
	4.660378e-001,
	1.601254e+000,
	6.702359e-001,
	// albedo 0, turbidity 10
	-1.841310e+000,
	-9.781779e-001,
	-4.610903e+000,
	4.824662e+000,
	-5.100806e-002,
	6.463776e-001,
	-6.377724e-006,
	2.216875e+000,
	8.618530e-001,
	-2.376373e+000,
	-1.108657e+000,
	-1.489799e+001,
	1.546458e+001,
	4.091025e-002,
	9.761780e-002,
	-1.048958e-002,
	2.165834e+000,
	-1.609171e-001,
	-4.710318e+000,
	-2.261963e+000,
	6.947327e+000,
	-1.034828e+001,
	-1.325542e-001,
	7.508674e-001,
	2.247553e-001,
	2.873142e+000,
	1.297100e+000,
	2.163750e-001,
	-1.944345e-001,
	-2.437860e+000,
	1.011314e+001,
	4.450500e-001,
	3.111492e-001,
	2.751323e-001,
	-1.627906e+000,
	2.531213e-001,
	-1.258794e+000,
	-3.524641e-001,
	8.425444e-001,
	-1.085313e+001,
	-1.154381e+000,
	-4.638014e-001,
	-2.781115e-003,
	4.344498e-001,
	8.507091e-001,
	-1.018938e+000,
	-1.804153e-001,
	-6.354054e-002,
	1.573150e+001,
	-4.386999e+000,
	6.211115e-001,
	5.294648e-001,
	1.580749e+000,
	6.586655e-001,
	// albedo 1, turbidity 1
	-1.116416e+000,
	-1.917524e-001,
	-1.068233e+001,
	1.222221e+001,
	-3.668978e-002,
	1.054022e+000,
	1.592132e-002,
	3.180583e+000,
	5.627370e-001,
	-1.132341e+000,
	-1.671286e-001,
	5.976499e+000,
	-4.227366e+000,
	-9.542489e-002,
	8.664938e-001,
	8.351793e-003,
	4.876068e+000,
	4.492779e-001,
	-1.087635e+000,
	-3.173679e-001,
	4.314407e-001,
	1.100555e+000,
	-4.410057e-001,
	1.677253e+000,
	-3.005925e-002,
	-4.201249e+000,
	1.070902e+000,
	-1.083031e+000,
	-8.847705e-002,
	1.291773e+000,
	4.546776e-001,
	3.091894e-001,
	7.261760e-001,
	4.203659e-002,
	5.990615e+000,
	3.704756e-001,
	-1.057899e+000,
	-2.246706e-001,
	2.329563e+000,
	-1.219656e+000,
	-5.335260e+000,
	8.545378e-001,
	-3.906209e-002,
	-9.025499e-001,
	7.797348e-001,
	-1.073305e+000,
	-1.522553e-001,
	1.767063e+000,
	1.904280e+000,
	-3.101673e+000,
	3.995856e-001,
	2.905192e-002,
	2.563977e+000,
	5.753067e-001,
	// albedo 1, turbidity 2
	-1.113674e+000,
	-1.759694e-001,
	-9.754125e+000,
	1.087391e+001,
	-3.841093e-002,
	9.524272e-001,
	5.680219e-002,
	4.227034e+000,
	6.029571e-001,
	-1.126496e+000,
	-1.680281e-001,
	5.332352e+000,
	-4.575579e+000,
	-6.761755e-002,
	3.295335e-001,
	1.194896e-001,
	5.570901e+000,
	4.536185e-001,
	-1.103074e+000,
	-2.681801e-001,
	6.571479e-002,
	2.396522e+000,
	-4.551280e-001,
	2.466331e+000,
	-1.232022e-001,
	-3.023201e+000,
	1.086379e+000,
	-1.053299e+000,
	-2.697173e-002,
	8.379121e-001,
	-9.681458e-001,
	5.890692e-001,
	-4.872027e-001,
	2.936929e-001,
	7.510139e+000,
	3.079122e-001,
	-1.079553e+000,
	-2.710448e-001,
	2.462379e+000,
	-3.713554e-001,
	-8.534512e+000,
	1.828242e+000,
	-1.686398e-001,
	-1.961340e+000,
	8.941077e-001,
	-1.069741e+000,
	-1.396394e-001,
	1.657868e+000,
	3.236313e+000,
	-2.706344e+000,
	-2.948122e-001,
	1.314816e-001,
	2.868457e+000,
	5.413403e-001,
	// albedo 1, turbidity 3
	-1.131649e+000,
	-1.954455e-001,
	-7.751595e+000,
	8.685861e+000,
	-4.910871e-002,
	8.992952e-001,
	4.710143e-002,
	4.254818e+000,
	6.821116e-001,
	-1.156689e+000,
	-1.884324e-001,
	3.163519e+000,
	-3.091522e+000,
	-6.613927e-002,
	-2.575883e-002,
	1.640065e-001,
	6.073643e+000,
	4.453468e-001,
	-1.079224e+000,
	-2.621389e-001,
	9.446437e-001,
	1.448479e+000,
	-3.969384e-001,
	2.626638e+000,
	-8.101186e-002,
	-3.016355e+000,
	1.076295e+000,
	-1.080832e+000,
	1.033057e-002,
	-3.500156e-001,
	-3.281419e-002,
	5.655512e-001,
	-1.156742e+000,
	4.534710e-001,
	8.774122e+000,
	2.772869e-001,
	-1.051202e+000,
	-2.679975e-001,
	2.719109e+000,
	-2.190316e+000,
	-6.878798e+000,
	2.250481e+000,
	-2.030252e-001,
	-2.026527e+000,
	9.701096e-001,
	-1.089849e+000,
	-1.598589e-001,
	1.564748e+000,
	6.869187e+000,
	-3.053670e+000,
	-6.110435e-001,
	1.644472e-001,
	2.370452e+000,
	5.511770e-001,
	// albedo 1, turbidity 4
	-1.171419e+000,
	-2.429746e-001,
	-8.991334e+000,
	9.571216e+000,
	-2.772861e-002,
	6.688262e-001,
	7.683478e-002,
	3.785611e+000,
	6.347635e-001,
	-1.228554e+000,
	-2.917562e-001,
	2.753986e+000,
	-2.491780e+000,
	-4.663434e-002,
	3.118303e-001,
	7.546506e-002,
	4.463096e+000,
	5.955071e-001,
	-1.093124e+000,
	-2.447767e-001,
	9.097406e-001,
	5.448296e-001,
	-2.957824e-001,
	2.024167e+000,
	-5.152333e-004,
	-1.069081e+000,
	9.369565e-001,
	-1.056994e+000,
	1.569507e-002,
	-8.217491e-001,
	1.870818e+000,
	7.061930e-001,
	-1.483928e+000,
	5.978206e-001,
	6.864902e+000,
	3.673332e-001,
	-1.054871e+000,
	-2.758129e-001,
	2.712807e+000,
	-5.950110e+000,
	-6.554039e+000,
	2.447523e+000,
	-1.895171e-001,
	-1.454292e+000,
	9.131738e-001,
	-1.100218e+000,
	-1.746241e-001,
	1.438505e+000,
	1.115481e+001,
	-3.266076e+000,
	-8.837357e-001,
	1.970100e-001,
	1.991595e+000,
	5.907821e-001,
	// albedo 1, turbidity 5
	-1.207267e+000,
	-2.913610e-001,
	-1.103767e+001,
	1.140724e+001,
	-1.416800e-002,
	5.564047e-001,
	8.476262e-002,
	3.371255e+000,
	6.221335e-001,
	-1.429698e+000,
	-5.374218e-001,
	2.837524e+000,
	-2.221936e+000,
	-2.422337e-002,
	9.313758e-002,
	7.190250e-002,
	1.869022e+000,
	5.609035e-001,
	-1.002274e+000,
	-6.972810e-002,
	4.031308e-001,
	-3.932997e-001,
	-1.521923e-001,
	2.390646e+000,
	-6.893990e-002,
	2.999661e+000,
	1.017843e+000,
	-1.081168e+000,
	-1.178666e-001,
	-4.968080e-001,
	3.919299e+000,
	6.046866e-001,
	-2.440615e+000,
	7.891538e-001,
	2.140835e+000,
	2.740470e-001,
	-1.050727e+000,
	-2.307688e-001,
	2.276396e+000,
	-9.454407e+000,
	-5.505176e+000,
	2.992620e+000,
	-2.450942e-001,
	6.078372e-001,
	9.606765e-001,
	-1.103752e+000,
	-1.810202e-001,
	1.375044e+000,
	1.589095e+001,
	-3.438954e+000,
	-1.265669e+000,
	2.475172e-001,
	1.680768e+000,
	5.978056e-001,
	// albedo 1, turbidity 6
	-1.244324e+000,
	-3.378542e-001,
	-1.111001e+001,
	1.137784e+001,
	-7.896794e-003,
	4.808023e-001,
	9.249904e-002,
	3.025816e+000,
	5.880239e-001,
	-1.593165e+000,
	-7.027621e-001,
	2.220896e+000,
	-1.437709e+000,
	-1.534738e-002,
	6.286958e-002,
	6.644555e-002,
	1.091727e+000,
	5.470080e-001,
	-9.136506e-001,
	1.344874e-002,
	7.772636e-001,
	-1.209396e+000,
	-1.408978e-001,
	2.433718e+000,
	-1.041938e-001,
	3.791244e+000,
	1.037916e+000,
	-1.134968e+000,
	-1.803315e-001,
	-9.267335e-001,
	4.576670e+000,
	6.851928e-001,
	-2.805000e+000,
	8.687208e-001,
	1.161483e+000,
	2.571688e-001,
	-1.017037e+000,
	-2.053943e-001,
	2.361640e+000,
	-9.887818e+000,
	-5.122889e+000,
	3.287088e+000,
	-2.594102e-001,
	8.578927e-001,
	9.592340e-001,
	-1.118723e+000,
	-1.934942e-001,
	1.226023e+000,
	1.674140e+001,
	-3.277335e+000,
	-1.629809e+000,
	2.765232e-001,
	1.637713e+000,
	6.113963e-001,
	// albedo 1, turbidity 7
	-1.314779e+000,
	-4.119915e-001,
	-1.241150e+001,
	1.241578e+001,
	2.344284e-003,
	2.980837e-001,
	1.414613e-001,
	2.781731e+000,
	4.998556e-001,
	-1.926199e+000,
	-1.020038e+000,
	2.569200e+000,
	-1.081159e+000,
	-2.266833e-002,
	3.588668e-001,
	8.750078e-003,
	-2.452171e-001,
	4.796758e-001,
	-7.780002e-001,
	1.850647e-001,
	4.445456e-002,
	-2.409297e+000,
	-7.816346e-002,
	1.546790e+000,
	-2.807227e-002,
	5.998176e+000,
	1.132396e+000,
	-1.179326e+000,
	-3.578330e-001,
	-2.392933e-001,
	6.467883e+000,
	5.904596e-001,
	-1.869975e+000,
	8.045839e-001,
	-2.498121e+000,
	1.610633e-001,
	-1.009956e+000,
	-1.311896e-001,
	1.726577e+000,
	-1.219356e+001,
	-3.466239e+000,
	2.343602e+000,
	-2.252205e-001,
	2.573681e+000,
	1.027109e+000,
	-1.112460e+000,
	-2.063093e-001,
	1.233051e+000,
	2.058946e+001,
	-4.578074e+000,
	-1.145643e+000,
	3.160192e-001,
	1.420159e+000,
	5.860212e-001,
	// albedo 1, turbidity 8
	-1.371689e+000,
	-4.914196e-001,
	-1.076610e+001,
	1.107405e+001,
	-1.485077e-002,
	5.936218e-001,
	3.685482e-002,
	2.599968e+000,
	6.002204e-001,
	-2.436997e+000,
	-1.377939e+000,
	2.130141e-002,
	1.079593e+000,
	-1.796232e-002,
	-3.933248e-002,
	1.610711e-001,
	-6.901181e-001,
	1.206416e-001,
	-8.743368e-001,
	7.331370e-002,
	8.734259e-001,
	-3.743126e+000,
	-3.151167e-002,
	1.297596e+000,
	-7.634926e-002,
	6.532873e+000,
	1.435737e+000,
	-9.810197e-001,
	-3.521634e-001,
	-2.855205e-001,
	7.134674e+000,
	6.839748e-001,
	-1.394841e+000,
	6.952036e-001,
	-4.633104e+000,
	-2.173401e-002,
	-1.122958e+000,
	-1.691536e-001,
	1.382360e+000,
	-1.102913e+001,
	-2.608171e+000,
	1.865111e+000,
	-1.345154e-001,
	3.112342e+000,
	1.094134e+000,
	-1.075586e+000,
	-2.077415e-001,
	1.171477e+000,
	1.793270e+001,
	-4.656858e+000,
	-1.036839e+000,
	3.338295e-001,
	1.042793e+000,
	5.739374e-001,
	// albedo 1, turbidity 9
	-1.465871e+000,
	-6.364486e-001,
	-8.833718e+000,
	9.343650e+000,
	-3.223600e-002,
	7.552848e-001,
	-3.121341e-006,
	2.249164e+000,
	8.094662e-001,
	-2.448924e+000,
	-1.270878e+000,
	-4.823703e+000,
	5.853058e+000,
	-2.149127e-002,
	3.581132e-002,
	-1.230276e-003,
	4.892553e-001,
	-1.597657e-001,
	-2.419809e+000,
	-1.071337e+000,
	1.575648e+000,
	-4.983580e+000,
	9.545185e-003,
	5.032615e-001,
	4.186266e-001,
	4.634147e+000,
	1.433517e+000,
	-1.383278e-001,
	-2.797095e-002,
	-1.943067e-001,
	6.679623e+000,
	4.118280e-001,
	-2.744289e-001,
	-2.118722e-002,
	-4.337025e+000,
	1.505072e-001,
	-1.341872e+000,
	-2.518572e-001,
	1.027009e+000,
	-6.527103e+000,
	-1.081271e+000,
	1.015465e+000,
	2.845789e-001,
	2.470371e+000,
	9.278120e-001,
	-1.040640e+000,
	-2.367454e-001,
	1.100744e+000,
	8.827253e+000,
	-4.560794e+000,
	-7.287017e-001,
	2.842503e-001,
	6.336593e-001,
	6.327335e-001,
	// albedo 1, turbidity 10
	-1.877993e+000,
	-1.025135e+000,
	-4.311037e+000,
	4.715016e+000,
	-4.711631e-002,
	6.335844e-001,
	-7.665398e-006,
	1.788017e+000,
	9.001409e-001,
	-2.281540e+000,
	-1.137668e+000,
	-1.036869e+001,
	1.136254e+001,
	1.961739e-002,
	-9.836174e-002,
	-6.734567e-003,
	1.320918e+000,
	-2.400807e-001,
	-4.904054e+000,
	-2.315781e+000,
	5.735999e+000,
	-8.626257e+000,
	-1.255643e-001,
	1.545446e+000,
	1.396860e-001,
	2.972897e+000,
	1.429934e+000,
	4.077067e-001,
	-1.833688e-001,
	-2.450939e+000,
	9.119433e+000,
	4.505361e-001,
	-1.340828e+000,
	3.973690e-001,
	-1.785370e+000,
	9.628711e-002,
	-1.296052e+000,
	-3.250526e-001,
	1.813294e+000,
	-1.031485e+001,
	-1.388690e+000,
	1.239733e+000,
	-8.989196e-002,
	-3.389637e-001,
	9.639560e-001,
	-1.062181e+000,
	-2.423444e-001,
	7.577592e-001,
	1.566938e+001,
	-4.462264e+000,
	-5.742810e-001,
	3.262259e-001,
	9.461672e-001,
	6.232887e-001,
};

// [albedo][turbidity][quinticCoeffs]
float kHosekRadX[2][10][6] =
{
	// albedo 0, turbidity 1
	1.560219e+000,
	1.417388e+000,
	1.206927e+000,
	1.091949e+001,
	5.931416e+000,
	7.304788e+000,
	// albedo 0, turbidity 2
	1.533049e+000,
	1.560532e+000,
	3.685059e-001,
	1.355040e+001,
	5.543711e+000,
	7.792189e+000,
	// albedo 0, turbidity 3
	1.471043e+000,
	1.746088e+000,
	-9.299697e-001,
	1.720362e+001,
	5.473384e+000,
	8.336416e+000,
	// albedo 0, turbidity 4
	1.355991e+000,
	2.109348e+000,
	-3.295855e+000,
	2.264843e+001,
	5.454607e+000,
	9.304656e+000,
	// albedo 0, turbidity 5
	1.244963e+000,
	2.547533e+000,
	-5.841485e+000,
	2.756879e+001,
	5.576104e+000,
	1.043287e+001,
	// albedo 0, turbidity 6
	1.175532e+000,
	2.784634e+000,
	-7.212225e+000,
	2.975347e+001,
	6.472980e+000,
	1.092331e+001,
	// albedo 0, turbidity 7
	1.082973e+000,
	3.118094e+000,
	-8.934293e+000,
	3.186879e+001,
	8.473885e+000,
	1.174019e+001,
	// albedo 0, turbidity 8
	9.692500e-001,
	3.349574e+000,
	-1.003810e+001,
	3.147654e+001,
	1.338931e+001,
	1.272547e+001,
	// albedo 0, turbidity 9
	8.547044e-001,
	3.151538e+000,
	-9.095567e+000,
	2.554995e+001,
	2.273219e+001,
	1.410398e+001,
	// albedo 0, turbidity 10
	7.580340e-001,
	2.311153e+000,
	-5.170814e+000,
	1.229669e+001,
	3.686529e+001,
	1.598882e+001,
	// albedo 1, turbidity 1
	1.664273e+000,
	1.574468e+000,
	1.422078e+000,
	9.768247e+000,
	1.447338e+001,
	1.644988e+001,
	// albedo 1, turbidity 2
	1.638295e+000,
	1.719586e+000,
	5.786675e-001,
	1.239846e+001,
	1.415419e+001,
	1.728605e+001,
	// albedo 1, turbidity 3
	1.572623e+000,
	1.921559e+000,
	-7.714802e-001,
	1.609246e+001,
	1.420954e+001,
	1.825908e+001,
	// albedo 1, turbidity 4
	1.468395e+000,
	2.211970e+000,
	-2.845869e+000,
	2.075027e+001,
	1.524822e+001,
	1.937622e+001,
	// albedo 1, turbidity 5
	1.355047e+000,
	2.556469e+000,
	-4.960920e+000,
	2.460237e+001,
	1.648360e+001,
	2.065648e+001,
	// albedo 1, turbidity 6
	1.291642e+000,
	2.742036e+000,
	-6.061967e+000,
	2.602002e+001,
	1.819144e+001,
	2.116712e+001,
	// albedo 1, turbidity 7
	1.194565e+000,
	2.972120e+000,
	-7.295779e+000,
	2.691805e+001,
	2.124880e+001,
	2.201819e+001,
	// albedo 1, turbidity 8
	1.083631e+000,
	3.047021e+000,
	-7.766096e+000,
	2.496261e+001,
	2.744264e+001,
	2.291875e+001,
	// albedo 1, turbidity 9
	9.707994e-001,
	2.736459e+000,
	-6.308284e+000,
	1.760860e+001,
	3.776291e+001,
	2.392150e+001,
	// albedo 1, turbidity 10
	8.574294e-001,
	1.865155e+000,
	-2.364707e+000,
	4.337793e+000,
	5.092831e+001,
	2.523432e+001,
};

float kHosekCoeffsY[2][10][6][9] =
{
	// albedo 0, turbidity 1
	-1.127942e+000,
	-1.905548e-001,
	-1.252356e+001,
	1.375799e+001,
	-3.624732e-002,
	1.055453e+000,
	1.385036e-002,
	4.176970e+000,
	5.928345e-001,
	-1.155260e+000,
	-1.778135e-001,
	6.216056e+000,
	-5.254116e+000,
	-8.787445e-002,
	8.434621e-001,
	4.025734e-002,
	6.195322e+000,
	3.111856e-001,
	-1.125624e+000,
	-3.217593e-001,
	5.043919e-001,
	1.686284e+000,
	-3.536071e-001,
	1.476321e+000,
	-7.899019e-002,
	-4.522531e+000,
	1.271691e+000,
	-1.081801e+000,
	-1.033234e-001,
	9.995550e-001,
	7.482946e-003,
	-6.776018e-002,
	1.463141e+000,
	9.492021e-002,
	5.612723e+000,
	1.298846e-001,
	-1.075320e+000,
	-2.402711e-001,
	2.141284e+000,
	-1.203359e+000,
	-4.945188e+000,
	1.437221e+000,
	-8.096750e-002,
	-1.028378e+000,
	1.004164e+000,
	-1.073337e+000,
	-1.516517e-001,
	1.639379e+000,
	2.304669e+000,
	-3.214244e+000,
	1.286245e+000,
	5.613957e-002,
	2.480902e+000,
	4.999363e-001,
	// albedo 0, turbidity 2
	-1.128399e+000,
	-1.857793e-001,
	-1.089863e+001,
	1.172984e+001,
	-3.768099e-002,
	9.439285e-001,
	4.869335e-002,
	4.845114e+000,
	6.119211e-001,
	-1.114002e+000,
	-1.399280e-001,
	4.963800e+000,
	-4.685500e+000,
	-7.780879e-002,
	4.049736e-001,
	1.586297e-001,
	7.770264e+000,
	3.449006e-001,
	-1.185472e+000,
	-3.403543e-001,
	6.588322e-001,
	1.133713e+000,
	-4.118674e-001,
	2.061191e+000,
	-1.882768e-001,
	-4.372586e+000,
	1.223530e+000,
	-1.002272e+000,
	2.000703e-002,
	7.073269e-002,
	1.485075e+000,
	5.005589e-001,
	4.301494e-001,
	3.626541e-001,
	7.921098e+000,
	1.574766e-001,
	-1.121006e+000,
	-3.007777e-001,
	2.242051e+000,
	-4.571561e+000,
	-7.761071e+000,
	2.053404e+000,
	-1.524018e-001,
	-1.886162e+000,
	1.018208e+000,
	-1.058864e+000,
	-1.358673e-001,
	1.389667e+000,
	8.633409e+000,
	-3.437249e+000,
	7.295429e-001,
	1.514700e-001,
	2.842513e+000,
	5.014325e-001,
	// albedo 0, turbidity 3
	-1.144464e+000,
	-2.043799e-001,
	-1.020188e+001,
	1.071247e+001,
	-3.256693e-002,
	7.860205e-001,
	6.872719e-002,
	4.824771e+000,
	6.259836e-001,
	-1.170104e+000,
	-2.118626e-001,
	4.391405e+000,
	-4.198900e+000,
	-7.111559e-002,
	3.890442e-001,
	1.024831e-001,
	6.282535e+000,
	5.365688e-001,
	-1.129171e+000,
	-2.552880e-001,
	2.238298e-001,
	7.314295e-001,
	-3.562730e-001,
	1.881931e+000,
	-3.078716e-002,
	-1.039120e+000,
	9.096301e-001,
	-1.042294e+000,
	4.450203e-003,
	-5.116033e-001,
	2.627589e+000,
	6.098996e-001,
	-1.264638e-001,
	4.325281e-001,
	7.080503e+000,
	4.583646e-001,
	-1.082293e+000,
	-2.723056e-001,
	2.065076e+000,
	-8.143133e+000,
	-7.892212e+000,
	2.142231e+000,
	-7.106240e-002,
	-1.122398e+000,
	8.338505e-001,
	-1.071715e+000,
	-1.426568e-001,
	1.095351e+000,
	1.729783e+001,
	-3.851931e+000,
	4.360514e-001,
	2.114440e-001,
	2.970832e+000,
	5.944389e-001,
	// albedo 0, turbidity 4
	-1.195909e+000,
	-2.590449e-001,
	-1.191037e+001,
	1.207947e+001,
	-1.589842e-002,
	6.297846e-001,
	9.054772e-002,
	4.285959e+000,
	5.933752e-001,
	-1.245763e+000,
	-3.316637e-001,
	4.293660e+000,
	-3.694011e+000,
	-4.699947e-002,
	4.843684e-001,
	2.130425e-002,
	4.097549e+000,
	6.530809e-001,
	-1.148742e+000,
	-1.902509e-001,
	-2.393233e-001,
	-2.441254e-001,
	-2.610918e-001,
	1.846988e+000,
	3.532866e-002,
	2.660106e+000,
	8.358294e-001,
	-1.016080e+000,
	-7.444960e-002,
	-5.053436e-001,
	4.388855e+000,
	6.054987e-001,
	-1.208300e+000,
	5.817215e-001,
	2.543570e+000,
	4.726568e-001,
	-1.072027e+000,
	-2.101440e-001,
	1.518378e+000,
	-1.060119e+001,
	-6.016546e+000,
	2.649475e+000,
	-5.166992e-002,
	1.571269e+000,
	8.344622e-001,
	-1.072365e+000,
	-1.511201e-001,
	7.478010e-001,
	1.900732e+001,
	-3.950387e+000,
	-3.473907e-001,
	3.797211e-001,
	2.782949e+000,
	6.296808e-001,
	// albedo 0, turbidity 5
	-1.239423e+000,
	-3.136289e-001,
	-1.351100e+001,
	1.349468e+001,
	-7.070423e-003,
	5.012315e-001,
	1.106008e-001,
	3.803619e+000,
	5.577948e-001,
	-1.452524e+000,
	-5.676944e-001,
	2.993153e+000,
	-2.277288e+000,
	-2.168954e-002,
	3.056720e-001,
	1.152338e-002,
	1.852697e+000,
	6.427228e-001,
	-1.061421e+000,
	-4.590521e-002,
	6.057022e-001,
	-1.096835e+000,
	-1.504952e-001,
	2.344921e+000,
	-5.491832e-002,
	5.268322e+000,
	9.082253e-001,
	-1.042373e+000,
	-1.769498e-001,
	-1.075388e+000,
	3.831712e+000,
	3.154140e-001,
	-2.416458e+000,
	7.909032e-001,
	-1.492892e-002,
	3.854049e-001,
	-1.064159e+000,
	-1.892684e-001,
	1.438685e+000,
	-8.166362e+000,
	-3.616364e+000,
	3.275206e+000,
	-1.203825e-001,
	2.039491e+000,
	8.688057e-001,
	-1.070120e+000,
	-1.569508e-001,
	4.124760e-001,
	1.399683e+001,
	-3.547085e+000,
	-1.046326e+000,
	4.973825e-001,
	2.791231e+000,
	6.503286e-001,
	// albedo 0, turbidity 6
	-1.283579e+000,
	-3.609518e-001,
	-1.335397e+001,
	1.315248e+001,
	-4.431938e-004,
	3.769526e-001,
	1.429824e-001,
	3.573613e+000,
	4.998696e-001,
	-1.657952e+000,
	-7.627948e-001,
	1.958222e+000,
	-7.949816e-001,
	-2.882837e-002,
	5.356149e-001,
	-5.191946e-002,
	8.869955e-001,
	6.263320e-001,
	-9.527600e-001,
	6.494189e-002,
	5.361303e-001,
	-2.129590e+000,
	-9.258630e-002,
	1.604776e+000,
	5.067770e-002,
	6.376055e+000,
	9.138052e-001,
	-1.080827e+000,
	-2.523120e-001,
	-7.154262e-001,
	4.120085e+000,
	1.878228e-001,
	-1.492158e+000,
	6.881655e-001,
	-1.446611e+000,
	4.040631e-001,
	-1.054075e+000,
	-1.665498e-001,
	9.191052e-001,
	-6.636943e+000,
	-1.894826e+000,
	2.107810e+000,
	-3.680499e-002,
	2.655452e+000,
	8.413840e-001,
	-1.061127e+000,
	-1.448849e-001,
	2.667493e-001,
	1.034103e+001,
	-4.285769e+000,
	-3.874504e-001,
	5.998752e-001,
	3.132426e+000,
	6.652753e-001,
	// albedo 0, turbidity 7
	-1.347345e+000,
	-4.287832e-001,
	-9.305553e+000,
	9.133813e+000,
	-3.173527e-003,
	3.977564e-001,
	1.151420e-001,
	3.320564e+000,
	4.998134e-001,
	-1.927296e+000,
	-9.901372e-001,
	-2.593499e+000,
	4.087421e+000,
	-5.833993e-002,
	8.158929e-001,
	-4.681279e-002,
	2.423716e-001,
	4.938052e-001,
	-9.470092e-001,
	7.325237e-002,
	2.064735e+000,
	-5.167540e+000,
	-1.313751e-002,
	4.832169e-001,
	1.126295e-001,
	6.970522e+000,
	1.035022e+000,
	-1.022557e+000,
	-2.762616e-001,
	-9.375748e-001,
	6.696739e+000,
	2.200765e-001,
	-1.133253e-001,
	5.492505e-001,
	-3.109391e+000,
	3.321914e-001,
	-1.087444e+000,
	-1.836263e-001,
	6.225024e-001,
	-8.576765e+000,
	-1.107637e+000,
	7.859427e-001,
	9.910909e-002,
	3.112938e+000,
	8.596261e-001,
	-1.051544e+000,
	-1.546262e-001,
	2.371731e-001,
	1.200502e+001,
	-4.527291e+000,
	7.268862e-002,
	5.571478e-001,
	2.532873e+000,
	6.662000e-001,
	// albedo 0, turbidity 8
	-1.375576e+000,
	-4.840019e-001,
	-8.121290e+000,
	8.058140e+000,
	-1.445661e-002,
	5.123314e-001,
	5.813321e-002,
	3.203219e+000,
	5.442318e-001,
	-2.325221e+000,
	-1.241463e+000,
	-7.063430e+000,
	8.741369e+000,
	-7.829950e-002,
	8.844273e-001,
	-3.471106e-002,
	1.740583e-001,
	2.814079e-001,
	-1.228700e+000,
	-2.013412e-001,
	2.949042e+000,
	-7.371945e+000,
	1.071753e-001,
	-2.491970e-001,
	2.265223e-001,
	6.391504e+000,
	1.172389e+000,
	-7.601786e-001,
	-1.680631e-001,
	-7.584444e-001,
	8.541356e+000,
	8.222291e-002,
	6.729633e-001,
	3.206615e-001,
	-3.700940e+000,
	2.710054e-001,
	-1.191166e+000,
	-2.672347e-001,
	2.927498e-001,
	-9.713613e+000,
	-4.783721e-001,
	2.352803e-001,
	2.161949e-001,
	2.691481e+000,
	8.745447e-001,
	-1.030135e+000,
	-1.653301e-001,
	2.263443e-001,
	1.296157e+001,
	-4.650644e+000,
	7.055709e-003,
	5.091975e-001,
	2.000370e+000,
	6.603839e-001,
	// albedo 0, turbidity 9
	-1.508018e+000,
	-6.460933e-001,
	-6.402745e+000,
	6.545995e+000,
	-3.750320e-002,
	6.921803e-001,
	3.309819e-003,
	2.797527e+000,
	6.978446e-001,
	-2.333308e+000,
	-1.167837e+000,
	-1.746787e+001,
	1.868630e+001,
	-8.948229e-003,
	5.621946e-001,
	-3.402626e-002,
	1.217943e+000,
	1.149865e-002,
	-2.665953e+000,
	-1.226307e+000,
	7.169725e+000,
	-1.159434e+001,
	3.583420e-002,
	-3.074378e-001,
	3.412248e-001,
	4.422122e+000,
	1.283791e+000,
	-9.705116e-002,
	8.312991e-002,
	-2.160462e+000,
	1.028235e+001,
	3.543357e-002,
	1.032049e+000,
	1.058310e-001,
	-2.972898e+000,
	2.418628e-001,
	-1.329617e+000,
	-3.699557e-001,
	5.560117e-001,
	-9.730113e+000,
	9.938865e-002,
	-3.071488e-001,
	2.510691e-001,
	1.777111e+000,
	8.705142e-001,
	-1.019387e+000,
	-1.893247e-001,
	1.194079e-001,
	1.239436e+001,
	-4.799224e+000,
	2.940213e-001,
	4.841268e-001,
	1.529724e+000,
	6.582615e-001,
	// albedo 0, turbidity 10
	-1.896737e+000,
	-1.005442e+000,
	-6.411032e+000,
	6.548220e+000,
	-3.227596e-002,
	5.717262e-001,
	-8.115192e-006,
	2.296704e+000,
	9.000749e-001,
	-2.411116e+000,
	-1.225587e+000,
	-1.753629e+001,
	1.829393e+001,
	1.247555e-002,
	2.364616e-001,
	-5.114637e-003,
	1.603778e+000,
	-2.224156e-001,
	-4.707121e+000,
	-2.074977e+000,
	7.942300e+000,
	-1.132407e+001,
	-5.415654e-002,
	5.446811e-001,
	1.032493e-001,
	4.010235e+000,
	1.369802e+000,
	1.010482e-001,
	-4.013305e-001,
	-2.674579e+000,
	9.779409e+000,
	1.782506e-001,
	7.053045e-001,
	4.200002e-001,
	-2.400671e+000,
	1.953165e-001,
	-1.243526e+000,
	-3.391255e-001,
	8.848882e-001,
	-9.789025e+000,
	-3.997324e-001,
	-9.546227e-001,
	-1.044017e-001,
	6.010593e-001,
	8.714462e-001,
	-1.014633e+000,
	-1.730009e-001,
	-7.738934e-002,
	1.390903e+001,
	-4.847307e+000,
	1.076059e+000,
	5.685743e-001,
	1.572992e+000,
	6.561432e-001,
	// albedo 1, turbidity 1
	-1.122998e+000,
	-1.881183e-001,
	-1.030709e+001,
	1.158932e+001,
	-4.079495e-002,
	9.603774e-001,
	3.079436e-002,
	4.009235e+000,
	5.060745e-001,
	-1.134790e+000,
	-1.539688e-001,
	5.478405e+000,
	-4.217270e+000,
	-1.043858e-001,
	7.165008e-001,
	1.524765e-002,
	6.473623e+000,
	4.207882e-001,
	-1.134957e+000,
	-3.513318e-001,
	7.393837e-001,
	1.354415e+000,
	-4.764078e-001,
	1.690441e+000,
	-5.492640e-002,
	-5.563523e+000,
	1.145743e+000,
	-1.058344e+000,
	-5.758503e-002,
	1.168230e+000,
	3.269824e-001,
	1.795193e-001,
	7.849011e-001,
	7.441853e-002,
	6.904804e+000,
	2.818790e-001,
	-1.075194e+000,
	-2.355813e-001,
	2.463685e+000,
	-1.536505e+000,
	-7.505771e+000,
	9.619712e-001,
	-6.465851e-002,
	-1.355492e+000,
	8.489847e-001,
	-1.079030e+000,
	-1.465328e-001,
	1.773838e+000,
	2.310131e+000,
	-3.136065e+000,
	3.507952e-001,
	4.435014e-002,
	2.819225e+000,
	5.689008e-001,
	// albedo 1, turbidity 2
	-1.125833e+000,
	-1.870849e-001,
	-9.555833e+000,
	1.059713e+001,
	-4.225402e-002,
	9.164663e-001,
	4.338796e-002,
	4.400980e+000,
	6.056119e-001,
	-1.127440e+000,
	-1.551891e-001,
	4.755621e+000,
	-4.408806e+000,
	-7.851763e-002,
	2.268284e-001,
	1.460070e-001,
	7.048003e+000,
	3.525997e-001,
	-1.143788e+000,
	-3.170178e-001,
	5.480669e-001,
	2.041830e+000,
	-4.532139e-001,
	2.302233e+000,
	-1.887419e-001,
	-4.489221e+000,
	1.250967e+000,
	-1.032849e+000,
	7.376031e-003,
	5.666073e-001,
	-2.312203e-001,
	4.862894e-001,
	-1.748294e-001,
	3.572870e-001,
	8.380522e+000,
	1.302333e-001,
	-1.093728e+000,
	-2.786977e-001,
	2.641272e+000,
	-1.507494e+000,
	-8.731243e+000,
	1.684055e+000,
	-2.023377e-001,
	-2.176398e+000,
	1.013249e+000,
	-1.076578e+000,
	-1.456205e-001,
	1.693935e+000,
	2.945003e+000,
	-2.822673e+000,
	-2.520033e-001,
	1.517034e-001,
	2.649109e+000,
	5.179094e-001,
	// albedo 1, turbidity 3
	-1.146417e+000,
	-2.119353e-001,
	-7.187525e+000,
	8.058599e+000,
	-5.256438e-002,
	8.375733e-001,
	3.887093e-002,
	4.222111e+000,
	6.695347e-001,
	-1.173674e+000,
	-2.067025e-001,
	2.899359e+000,
	-2.804918e+000,
	-8.473899e-002,
	3.944225e-003,
	1.340641e-001,
	6.160887e+000,
	4.527141e-001,
	-1.090098e+000,
	-2.599633e-001,
	9.180856e-001,
	1.092710e+000,
	-4.215019e-001,
	2.427660e+000,
	-9.277667e-002,
	-2.123523e+000,
	1.058159e+000,
	-1.084460e+000,
	8.056181e-003,
	-2.453510e-001,
	6.619567e-001,
	4.668118e-001,
	-9.526719e-001,
	4.648454e-001,
	8.001572e+000,
	3.054194e-001,
	-1.053728e+000,
	-2.765784e-001,
	2.792388e+000,
	-3.489517e+000,
	-8.150535e+000,
	2.195757e+000,
	-2.017234e-001,
	-2.128017e+000,
	9.326589e-001,
	-1.099348e+000,
	-1.593939e-001,
	1.568292e+000,
	7.247853e+000,
	-2.933000e+000,
	-5.890481e-001,
	1.724440e-001,
	2.433484e+000,
	5.736558e-001,
	// albedo 1, turbidity 4
	-1.185983e+000,
	-2.581184e-001,
	-7.761056e+000,
	8.317053e+000,
	-3.351773e-002,
	6.676667e-001,
	5.941733e-002,
	3.820727e+000,
	6.324032e-001,
	-1.268591e+000,
	-3.398067e-001,
	2.348503e+000,
	-2.023779e+000,
	-5.368458e-002,
	1.083282e-001,
	8.402858e-002,
	3.910254e+000,
	5.577481e-001,
	-1.071353e+000,
	-1.992459e-001,
	7.878387e-001,
	1.974702e-001,
	-3.033058e-001,
	2.335298e+000,
	-8.205259e-002,
	7.954454e-001,
	9.972312e-001,
	-1.089513e+000,
	-3.104364e-002,
	-5.995746e-001,
	2.330281e+000,
	6.581939e-001,
	-1.821467e+000,
	6.679973e-001,
	5.090195e+000,
	3.125161e-001,
	-1.040214e+000,
	-2.570934e-001,
	2.660489e+000,
	-6.506045e+000,
	-7.053586e+000,
	2.763153e+000,
	-2.433632e-001,
	-7.648176e-001,
	9.452937e-001,
	-1.116052e+000,
	-1.831993e-001,
	1.457694e+000,
	1.163608e+001,
	-3.216426e+000,
	-1.045594e+000,
	2.285002e-001,
	1.817407e+000,
	5.810396e-001,
	// albedo 1, turbidity 5
	-1.230134e+000,
	-3.136264e-001,
	-8.909301e+000,
	9.145006e+000,
	-1.055387e-002,
	4.467317e-001,
	1.016826e-001,
	3.342964e+000,
	5.633840e-001,
	-1.442907e+000,
	-5.593147e-001,
	2.156447e+000,
	-1.241657e+000,
	-3.512130e-002,
	3.050274e-001,
	1.797175e-002,
	1.742358e+000,
	5.977153e-001,
	-1.027627e+000,
	-6.481539e-002,
	4.351975e-001,
	-1.051677e+000,
	-2.030672e-001,
	1.942684e+000,
	-3.615993e-002,
	4.050266e+000,
	9.801624e-001,
	-1.082110e+000,
	-1.578209e-001,
	-3.397511e-001,
	4.163851e+000,
	6.650368e-001,
	-1.841730e+000,
	7.062544e-001,
	6.789881e-001,
	3.172623e-001,
	-1.047447e+000,
	-1.977560e-001,
	2.183364e+000,
	-8.805249e+000,
	-5.483962e+000,
	2.551309e+000,
	-1.779640e-001,
	1.519501e+000,
	9.212536e-001,
	-1.111853e+000,
	-1.935736e-001,
	1.394408e+000,
	1.392405e+001,
	-3.465430e+000,
	-1.068432e+000,
	2.388671e-001,
	1.455336e+000,
	6.233425e-001,
	// albedo 1, turbidity 6
	-1.262238e+000,
	-3.546341e-001,
	-1.008703e+001,
	1.020084e+001,
	-1.852187e-003,
	3.537580e-001,
	1.239199e-001,
	3.056093e+000,
	5.132052e-001,
	-1.613810e+000,
	-7.355585e-001,
	2.760123e+000,
	-1.685253e+000,
	-2.517552e-002,
	2.914258e-001,
	4.743448e-003,
	8.689596e-001,
	5.674192e-001,
	-9.462336e-001,
	2.950767e-002,
	-2.613816e-001,
	-7.398653e-001,
	-1.315558e-001,
	1.901042e+000,
	-6.447844e-002,
	4.969341e+000,
	1.027342e+000,
	-1.111481e+000,
	-2.194054e-001,
	-9.004538e-002,
	3.983442e+000,
	4.871278e-001,
	-1.965315e+000,
	7.956121e-001,
	-2.363225e-001,
	2.718037e-001,
	-1.036397e+000,
	-1.827106e-001,
	1.964747e+000,
	-8.870759e+000,
	-4.208011e+000,
	2.461215e+000,
	-2.158905e-001,
	1.561676e+000,
	9.436866e-001,
	-1.113769e+000,
	-1.947819e-001,
	1.300720e+000,
	1.516476e+001,
	-4.088732e+000,
	-1.069384e+000,
	2.836434e-001,
	1.671451e+000,
	6.229612e-001,
	// albedo 1, turbidity 7
	-1.328069e+000,
	-4.244047e-001,
	-8.417040e+000,
	8.552244e+000,
	-6.813504e-003,
	4.127422e-001,
	9.619897e-002,
	2.854227e+000,
	5.059880e-001,
	-1.927552e+000,
	-1.025290e+000,
	9.529576e-001,
	4.255950e-001,
	-3.738779e-002,
	2.584586e-001,
	4.911004e-002,
	-2.640913e-001,
	4.138626e-001,
	-8.488094e-001,
	1.435988e-001,
	6.356807e-001,
	-2.895732e+000,
	-8.473961e-002,
	1.701305e+000,
	-1.323908e-001,
	6.499338e+000,
	1.210928e+000,
	-1.128313e+000,
	-3.397048e-001,
	-4.043140e-001,
	6.265097e+000,
	5.482395e-001,
	-2.057614e+000,
	8.884087e-001,
	-2.943879e+000,
	9.760301e-002,
	-1.039764e+000,
	-1.494772e-001,
	1.781915e+000,
	-1.153012e+001,
	-3.379232e+000,
	2.517231e+000,
	-2.764393e-001,
	2.588849e+000,
	1.052120e+000,
	-1.108447e+000,
	-2.012251e-001,
	1.198640e+000,
	1.925331e+001,
	-4.423892e+000,
	-1.257122e+000,
	3.395690e-001,
	1.481220e+000,
	5.880175e-001,
	// albedo 1, turbidity 8
	-1.374185e+000,
	-4.967434e-001,
	-7.401318e+000,
	7.724021e+000,
	-2.345723e-002,
	5.979653e-001,
	2.436346e-002,
	2.658970e+000,
	6.014891e-001,
	-2.310933e+000,
	-1.290290e+000,
	-1.301909e+000,
	2.557806e+000,
	-3.744449e-002,
	8.982861e-002,
	1.090613e-001,
	-4.398363e-001,
	1.184329e-001,
	-1.124730e+000,
	-9.921830e-002,
	1.366902e+000,
	-4.172489e+000,
	-5.078016e-002,
	1.393597e+000,
	-9.323843e-002,
	6.452721e+000,
	1.435913e+000,
	-8.468477e-001,
	-2.744819e-001,
	-4.347200e-001,
	6.713362e+000,
	6.127133e-001,
	-1.685634e+000,
	7.360941e-001,
	-4.535502e+000,
	-2.920866e-002,
	-1.165242e+000,
	-2.008697e-001,
	1.438778e+000,
	-1.008936e+001,
	-2.214771e+000,
	2.102909e+000,
	-1.763085e-001,
	2.859075e+000,
	1.093470e+000,
	-1.074614e+000,
	-2.066374e-001,
	1.131891e+000,
	1.630063e+001,
	-4.801441e+000,
	-1.112590e+000,
	3.595785e-001,
	1.122227e+000,
	5.794610e-001,
	// albedo 1, turbidity 9
	-1.521515e+000,
	-6.835604e-001,
	-5.571044e+000,
	6.028774e+000,
	-4.253715e-002,
	6.875746e-001,
	-5.279456e-006,
	2.180150e+000,
	8.487705e-001,
	-2.240415e+000,
	-1.171166e+000,
	-7.182771e+000,
	8.417068e+000,
	-1.932866e-002,
	1.101887e-001,
	-1.098862e-002,
	6.242195e-001,
	-2.393875e-001,
	-2.712354e+000,
	-1.198830e+000,
	3.180200e+000,
	-6.768130e+000,
	-2.563386e-003,
	7.984607e-001,
	2.764376e-001,
	4.695358e+000,
	1.557045e+000,
	-3.655172e-002,
	-2.142321e-002,
	-9.138120e-001,
	7.932786e+000,
	3.516542e-001,
	-7.994343e-001,
	1.786761e-001,
	-4.208399e+000,
	1.820576e-002,
	-1.368610e+000,
	-2.656212e-001,
	1.249397e+000,
	-8.317818e+000,
	-8.962772e-001,
	1.423249e+000,
	1.478381e-001,
	2.191660e+000,
	1.007748e+000,
	-1.041753e+000,
	-2.453366e-001,
	1.061102e+000,
	1.130172e+001,
	-4.739312e+000,
	-9.223334e-001,
	2.982776e-001,
	6.162931e-001,
	6.080302e-001,
	// albedo 1, turbidity 10
	-1.989159e+000,
	-1.095160e+000,
	-2.915550e+000,
	3.275339e+000,
	-5.735765e-002,
	5.742174e-001,
	-7.683288e-006,
	1.763400e+000,
	9.001342e-001,
	-2.070020e+000,
	-1.086338e+000,
	-1.095898e+001,
	1.206960e+001,
	3.780123e-002,
	-1.774699e-002,
	-5.881348e-004,
	1.333819e+000,
	-2.605423e-001,
	-5.249653e+000,
	-2.383040e+000,
	6.160406e+000,
	-9.097138e+000,
	-1.955319e-001,
	1.651785e+000,
	6.016463e-004,
	3.021824e+000,
	1.493574e+000,
	4.685432e-001,
	-2.358662e-001,
	-2.666433e+000,
	9.685763e+000,
	5.804928e-001,
	-1.521875e+000,
	5.668989e-001,
	-1.548136e+000,
	1.688642e-002,
	-1.296891e+000,
	-3.449031e-001,
	1.928548e+000,
	-1.167560e+001,
	-1.627615e+000,
	1.355603e+000,
	-1.929074e-001,
	-6.568952e-001,
	1.009774e+000,
	-1.067288e+000,
	-2.410392e-001,
	7.147961e-001,
	1.783840e+001,
	-4.374399e+000,
	-6.588777e-001,
	3.329831e-001,
	1.012066e+000,
	6.118645e-001,
};

float kHosekRadY[2][10][6] =
{
	// albedo 0, turbidity 1
	1.632341e+000,
	1.395230e+000,
	1.375634e+000,
	1.238193e+001,
	5.921102e+000,
	7.766508e+000,
	// albedo 0, turbidity 2
	1.597115e+000,
	1.554617e+000,
	3.932382e-001,
	1.505284e+001,
	5.725234e+000,
	8.158155e+000,
	// albedo 0, turbidity 3
	1.522034e+000,
	1.844545e+000,
	-1.322862e+000,
	1.918382e+001,
	5.440769e+000,
	8.837119e+000,
	// albedo 0, turbidity 4
	1.403048e+000,
	2.290852e+000,
	-4.013792e+000,
	2.485100e+001,
	5.521888e+000,
	9.845547e+000,
	// albedo 0, turbidity 5
	1.286364e+000,
	2.774498e+000,
	-6.648221e+000,
	2.964151e+001,
	5.923777e+000,
	1.097075e+001,
	// albedo 0, turbidity 6
	1.213544e+000,
	3.040195e+000,
	-8.092676e+000,
	3.186082e+001,
	6.789782e+000,
	1.158899e+001,
	// albedo 0, turbidity 7
	1.122622e+000,
	3.347465e+000,
	-9.649016e+000,
	3.343824e+001,
	9.347715e+000,
	1.231374e+001,
	// albedo 0, turbidity 8
	1.007356e+000,
	3.543858e+000,
	-1.053520e+001,
	3.239842e+001,
	1.483962e+001,
	1.331718e+001,
	// albedo 0, turbidity 9
	8.956642e-001,
	3.278700e+000,
	-9.254933e+000,
	2.557923e+001,
	2.489677e+001,
	1.476166e+001,
	// albedo 0, turbidity 10
	7.985143e-001,
	2.340404e+000,
	-4.928274e+000,
	1.141787e+001,
	3.961501e+001,
	1.682448e+001,
	// albedo 1, turbidity 1
	1.745162e+000,
	1.639467e+000,
	1.342721e+000,
	1.166033e+001,
	1.490124e+001,
	1.774031e+001,
	// albedo 1, turbidity 2
	1.708439e+000,
	1.819144e+000,
	2.834399e-001,
	1.448066e+001,
	1.459214e+001,
	1.858679e+001,
	// albedo 1, turbidity 3
	1.631720e+000,
	2.094799e+000,
	-1.378825e+000,
	1.843198e+001,
	1.463173e+001,
	1.962881e+001,
	// albedo 1, turbidity 4
	1.516536e+000,
	2.438729e+000,
	-3.624121e+000,
	2.298621e+001,
	1.599782e+001,
	2.070027e+001,
	// albedo 1, turbidity 5
	1.405863e+000,
	2.785191e+000,
	-5.705236e+000,
	2.645121e+001,
	1.768330e+001,
	2.191903e+001,
	// albedo 1, turbidity 6
	1.344052e+000,
	2.951807e+000,
	-6.683851e+000,
	2.744271e+001,
	1.985706e+001,
	2.229452e+001,
	// albedo 1, turbidity 7
	1.245827e+000,
	3.182923e+000,
	-7.822960e+000,
	2.791395e+001,
	2.327254e+001,
	2.315910e+001,
	// albedo 1, turbidity 8
	1.132305e+000,
	3.202593e+000,
	-8.008429e+000,
	2.521093e+001,
	3.000014e+001,
	2.405306e+001,
	// albedo 1, turbidity 9
	1.020330e+000,
	2.820556e+000,
	-6.238704e+000,
	1.709276e+001,
	4.077916e+001,
	2.509949e+001,
	// albedo 1, turbidity 10
	9.031570e-001,
	1.863917e+000,
	-1.955738e+000,
	3.032665e+000,
	5.434290e+001,
	2.641780e+001,
};

float kHosekCoeffsZ[2][10][6][9] =
{
	// albedo 0, turbidity 1
	-1.310023e+000,
	-4.407658e-001,
	-3.640340e+001,
	3.683292e+001,
	-8.124762e-003,
	5.297961e-001,
	1.188633e-002,
	3.138320e+000,
	5.134778e-001,
	-1.424100e+000,
	-5.501606e-001,
	-1.753510e+001,
	1.822769e+001,
	-1.539272e-002,
	6.366826e-001,
	2.661996e-003,
	2.659915e+000,
	4.071138e-001,
	-1.103436e+000,
	-1.884105e-001,
	6.425322e+000,
	-6.910579e+000,
	-2.019861e-002,
	3.553271e-001,
	-1.589061e-002,
	5.345985e+000,
	8.790218e-001,
	-1.186200e+000,
	-4.307514e-001,
	-3.957947e+000,
	5.979352e+000,
	-5.348869e-002,
	1.736117e+000,
	3.491346e-002,
	-2.692261e+000,
	5.610506e-001,
	-1.006038e+000,
	-1.305995e-001,
	4.473513e+000,
	-3.806719e+000,
	1.419407e-001,
	-2.148238e-002,
	-5.081185e-002,
	3.735362e+000,
	5.358280e-001,
	-1.078507e+000,
	-1.633754e-001,
	-3.812368e+000,
	4.381700e+000,
	2.988122e-002,
	1.754224e+000,
	1.472376e-001,
	3.722798e+000,
	4.999157e-001,
	// albedo 0, turbidity 2
	-1.333582e+000,
	-4.649908e-001,
	-3.359528e+001,
	3.404375e+001,
	-9.384242e-003,
	5.587511e-001,
	5.726310e-003,
	3.073145e+000,
	5.425529e-001,
	-1.562624e+000,
	-7.107068e-001,
	-1.478170e+001,
	1.559839e+001,
	-1.462375e-002,
	5.050133e-001,
	2.516017e-002,
	1.604696e+000,
	2.902403e-001,
	-8.930158e-001,
	4.068077e-002,
	1.373481e+000,
	-2.342752e+000,
	-2.098058e-002,
	6.248686e-001,
	-5.258363e-002,
	7.058214e+000,
	1.150373e+000,
	-1.262823e+000,
	-4.818353e-001,
	8.892610e-004,
	1.923120e+000,
	-4.979718e-002,
	1.040693e+000,
	1.558103e-001,
	-2.852480e+000,
	2.420691e-001,
	-9.968383e-001,
	-1.200648e-001,
	1.324342e+000,
	-9.430889e-001,
	1.931098e-001,
	4.436916e-001,
	-7.320456e-002,
	4.215931e+000,
	7.898019e-001,
	-1.078185e+000,
	-1.718192e-001,
	-1.720191e+000,
	2.358918e+000,
	2.765637e-002,
	1.260245e+000,
	2.021941e-001,
	3.395483e+000,
	5.173628e-001,
	// albedo 0, turbidity 3
	-1.353023e+000,
	-4.813523e-001,
	-3.104920e+001,
	3.140156e+001,
	-9.510741e-003,
	5.542030e-001,
	8.135471e-003,
	3.136646e+000,
	5.215989e-001,
	-1.624704e+000,
	-7.990201e-001,
	-2.167125e+001,
	2.246341e+001,
	-1.163533e-002,
	5.415746e-001,
	2.618378e-002,
	1.139214e+000,
	3.444357e-001,
	-7.983610e-001,
	1.417476e-001,
	9.914841e+000,
	-1.081503e+001,
	-1.218845e-002,
	3.411392e-001,
	-6.137698e-002,
	7.445848e+000,
	1.180080e+000,
	-1.266679e+000,
	-4.288977e-001,
	-5.818701e+000,
	6.986437e+000,
	-8.180711e-002,
	1.397403e+000,
	2.016916e-001,
	-1.275731e+000,
	2.592773e-001,
	-1.009707e+000,
	-1.537754e-001,
	3.496378e+000,
	-3.013726e+000,
	2.421150e-001,
	-2.831925e-001,
	3.003395e-002,
	3.702862e+000,
	7.746320e-001,
	-1.075646e+000,
	-1.768747e-001,
	-1.347762e+000,
	1.989004e+000,
	1.375836e-002,
	1.764810e+000,
	1.330018e-001,
	3.230864e+000,
	6.626210e-001,
	// albedo 0, turbidity 4
	-1.375269e+000,
	-5.103569e-001,
	-3.442661e+001,
	3.478703e+001,
	-8.460009e-003,
	5.408643e-001,
	4.813323e-003,
	3.016078e+000,
	5.062069e-001,
	-1.821679e+000,
	-9.766461e-001,
	-1.926488e+001,
	1.997912e+001,
	-9.822567e-003,
	3.649556e-001,
	4.316092e-002,
	8.930190e-001,
	4.166527e-001,
	-6.633542e-001,
	1.997841e-001,
	2.395592e+000,
	-3.117175e+000,
	-1.080884e-002,
	8.983814e-001,
	-1.375825e-001,
	6.673463e+000,
	1.115663e+000,
	-1.303240e+000,
	-3.612712e-001,
	8.292959e-002,
	3.381364e-001,
	-6.078648e-002,
	3.229247e-001,
	3.680987e-001,
	7.046755e-001,
	3.144924e-001,
	-9.952598e-001,
	-2.039076e-001,
	4.026851e-001,
	2.686684e-001,
	1.640712e-001,
	5.186341e-001,
	-1.205520e-002,
	2.659613e+000,
	8.030394e-001,
	-1.098579e+000,
	-2.151992e-001,
	6.558198e-001,
	-7.436900e-004,
	-1.421817e-003,
	1.073701e+000,
	1.886875e-001,
	2.536857e+000,
	6.673923e-001,
	// albedo 0, turbidity 5
	-1.457986e+000,
	-5.906842e-001,
	-3.812464e+001,
	3.838539e+001,
	-6.024357e-003,
	4.741484e-001,
	1.209223e-002,
	2.818432e+000,
	5.012433e-001,
	-1.835728e+000,
	-1.003405e+000,
	-6.848129e+000,
	7.601943e+000,
	-1.277375e-002,
	4.785598e-001,
	3.366853e-002,
	1.097701e+000,
	4.636635e-001,
	-8.491348e-001,
	9.466365e-003,
	-2.685226e+000,
	2.004060e+000,
	-1.168708e-002,
	6.752316e-001,
	-1.543371e-001,
	5.674759e+000,
	1.039534e+000,
	-1.083379e+000,
	-1.506790e-001,
	7.328236e-001,
	-5.095568e-001,
	-8.609153e-002,
	4.448820e-001,
	4.174662e-001,
	1.481556e+000,
	3.942551e-001,
	-1.117089e+000,
	-3.337605e-001,
	2.502281e-001,
	4.036323e-001,
	2.673899e-001,
	2.829817e-001,
	2.242450e-002,
	2.043207e+000,
	7.706902e-001,
	-1.071648e+000,
	-2.126200e-001,
	6.069466e-001,
	-1.456290e-003,
	-5.515960e-001,
	1.046755e+000,
	1.985021e-001,
	2.290245e+000,
	6.876058e-001,
	// albedo 0, turbidity 6
	-1.483903e+000,
	-6.309647e-001,
	-4.380213e+001,
	4.410537e+001,
	-5.712161e-003,
	5.195992e-001,
	2.028428e-003,
	2.687114e+000,
	5.098321e-001,
	-2.053976e+000,
	-1.141473e+000,
	5.109183e-001,
	8.060391e-002,
	-1.033983e-002,
	4.066532e-001,
	4.869627e-002,
	1.161722e+000,
	4.039525e-001,
	-6.348185e-001,
	7.651292e-002,
	-1.031327e+001,
	1.007598e+001,
	-2.083688e-002,
	7.359516e-001,
	-2.029459e-001,
	5.013257e+000,
	1.077649e+000,
	-1.228630e+000,
	-1.650496e-001,
	4.077157e-002,
	-7.189167e-001,
	-5.092220e-002,
	2.959814e-001,
	5.111496e-001,
	2.540433e+000,
	3.615330e-001,
	-1.041883e+000,
	-3.278413e-001,
	-6.691911e-002,
	1.307364e+000,
	2.166663e-001,
	3.000595e-001,
	-3.157136e-003,
	1.389208e+000,
	7.999026e-001,
	-1.103556e+000,
	-2.443602e-001,
	4.705347e-001,
	-9.296482e-004,
	-5.309920e-001,
	9.654511e-001,
	2.142587e-001,
	2.244723e+000,
	6.839976e-001,
	// albedo 0, turbidity 7
	-1.555684e+000,
	-6.962113e-001,
	-4.647983e+001,
	4.674270e+001,
	-5.034895e-003,
	4.755090e-001,
	-9.502561e-007,
	2.626569e+000,
	5.056194e-001,
	-1.998288e+000,
	-1.124720e+000,
	-1.629586e+000,
	2.187993e+000,
	-8.284384e-003,
	3.845258e-001,
	5.726240e-002,
	1.185644e+000,
	4.255812e-001,
	-1.032570e+000,
	-2.513850e-001,
	-3.721112e+000,
	3.506967e+000,
	-2.186561e-002,
	9.436049e-001,
	-2.451412e-001,
	4.725724e+000,
	1.039256e+000,
	-8.597532e-001,
	9.073332e-002,
	-2.553741e+000,
	1.993237e+000,
	-4.390891e-002,
	-2.046928e-001,
	5.515623e-001,
	1.909127e+000,
	3.948212e-001,
	-1.210482e+000,
	-4.477622e-001,
	-2.267805e-001,
	1.219488e+000,
	1.336186e-001,
	6.866897e-001,
	2.808997e-002,
	1.600403e+000,
	7.816409e-001,
	-1.078168e+000,
	-2.699261e-001,
	2.537282e-001,
	3.820684e-001,
	-4.425103e-001,
	5.298235e-001,
	2.185217e-001,
	1.728679e+000,
	6.882743e-001,
	// albedo 0, turbidity 8
	-1.697968e+000,
	-8.391488e-001,
	-5.790105e+001,
	5.814120e+001,
	-3.404760e-003,
	4.265140e-001,
	-1.796301e-006,
	2.368442e+000,
	5.324429e-001,
	-2.141552e+000,
	-1.172230e+000,
	1.677872e+001,
	-1.641470e+001,
	-5.732425e-003,
	2.002199e-001,
	6.841834e-002,
	1.485338e+000,
	3.215763e-001,
	-1.442946e+000,
	-7.264245e-001,
	-9.503706e+000,
	9.650462e+000,
	-2.120995e-002,
	1.419263e+000,
	-2.893098e-001,
	3.860731e+000,
	1.120857e+000,
	-5.696752e-001,
	3.411279e-001,
	-2.931035e-001,
	-6.512552e-001,
	-1.068437e-001,
	-1.085661e+000,
	6.107549e-001,
	1.459503e+000,
	3.210336e-001,
	-1.313839e+000,
	-5.921371e-001,
	-2.332222e-001,
	1.648196e+000,
	2.492787e-001,
	1.381033e+000,
	-1.993392e-002,
	9.812560e-001,
	8.316329e-001,
	-1.087464e+000,
	-3.195534e-001,
	2.902095e-001,
	3.383709e-001,
	-8.798482e-001,
	1.494668e-002,
	2.529703e-001,
	1.452644e+000,
	6.693870e-001,
	// albedo 0, turbidity 9
	-2.068582e+000,
	-1.118605e+000,
	-5.081598e+001,
	5.097486e+001,
	-3.280669e-003,
	4.067371e-001,
	-2.544951e-006,
	2.179497e+000,
	5.778017e-001,
	-1.744693e+000,
	-8.537207e-001,
	2.234361e+001,
	-2.208318e+001,
	-5.932616e-003,
	1.035049e-001,
	5.742772e-002,
	1.977880e+000,
	2.124846e-001,
	-3.287515e+000,
	-2.140268e+000,
	-1.249566e+001,
	1.240091e+001,
	-2.409349e-002,
	1.397821e+000,
	-2.371627e-001,
	2.771192e+000,
	1.170496e+000,
	5.502311e-001,
	1.046630e+000,
	2.193517e+000,
	-2.220400e+000,
	-1.064394e-001,
	-1.017926e+000,
	4.795457e-001,
	1.030644e+000,
	3.177516e-001,
	-1.719734e+000,
	-9.536198e-001,
	-6.586821e-001,
	1.386361e+000,
	-2.513065e-002,
	1.187011e+000,
	6.542539e-002,
	5.296055e-001,
	8.082660e-001,
	-1.005700e+000,
	-3.028096e-001,
	4.470957e-002,
	1.007760e+000,
	-8.119016e-001,
	3.153338e-002,
	2.311321e-001,
	1.182208e+000,
	6.824758e-001,
	// albedo 0, turbidity 10
	-2.728867e+000,
	-1.580388e+000,
	-3.079627e+001,
	3.092586e+001,
	-4.197673e-003,
	3.154759e-001,
	-3.897675e-006,
	1.920567e+000,
	6.664791e-001,
	-1.322495e+000,
	-7.249275e-001,
	1.477660e+001,
	-1.468154e+001,
	-9.044857e-003,
	5.624314e-002,
	6.498392e-002,
	2.047389e+000,
	6.367540e-002,
	-6.102376e+000,
	-3.473018e+000,
	-9.926071e+000,
	9.637797e+000,
	-1.097909e-002,
	1.103498e+000,
	-2.424521e-001,
	2.520748e+000,
	1.240260e+000,
	1.351796e+000,
	1.018588e+000,
	2.009081e+000,
	-1.333394e+000,
	-1.979125e-001,
	-3.318292e-001,
	4.476624e-001,
	9.095235e-001,
	2.955611e-001,
	-1.774467e+000,
	-1.079880e+000,
	-8.084680e-002,
	2.577697e-001,
	-1.149295e-001,
	4.975303e-001,
	2.931611e-003,
	-3.803171e-001,
	8.002794e-001,
	-9.898401e-001,
	-2.542513e-001,
	-7.530911e-002,
	1.870355e+000,
	-1.521918e+000,
	2.405164e-001,
	2.964615e-001,
	1.334800e+000,
	6.789053e-001,
	// albedo 1, turbidity 1
	-1.279730e+000,
	-4.290674e-001,
	-4.277972e+001,
	4.343305e+001,
	-6.541826e-003,
	4.945086e-001,
	1.425338e-002,
	2.685244e+000,
	5.011313e-001,
	-1.449506e+000,
	-5.766374e-001,
	-1.688496e+001,
	1.781118e+001,
	-1.121649e-002,
	3.545020e-001,
	2.287338e-002,
	1.904281e+000,
	4.936998e-001,
	-1.021980e+000,
	-1.897574e-001,
	2.482462e+000,
	-2.941725e+000,
	-1.570448e-002,
	7.532578e-001,
	-4.256800e-002,
	5.239660e+000,
	4.983116e-001,
	-1.162608e+000,
	-3.428049e-001,
	3.974358e+000,
	-1.527935e+000,
	-3.919201e-002,
	8.758593e-001,
	7.291363e-002,
	-3.455257e+000,
	8.007426e-001,
	-9.929985e-001,
	-8.712006e-002,
	-7.397313e-001,
	1.348372e+000,
	9.511685e-002,
	3.233584e-001,
	-7.549148e-002,
	5.806452e+000,
	4.990042e-001,
	-1.084996e+000,
	-1.739767e-001,
	1.580475e-001,
	9.088180e-001,
	6.871433e-002,
	5.933079e-001,
	1.188921e-001,
	3.074079e+000,
	4.999327e-001,
	// albedo 1, turbidity 2
	-1.317009e+000,
	-4.661946e-001,
	-4.255347e+001,
	4.312782e+001,
	-5.727235e-003,
	4.285447e-001,
	2.189854e-002,
	2.608310e+000,
	5.190700e-001,
	-1.469236e+000,
	-6.282139e-001,
	-1.241404e+001,
	1.348765e+001,
	-1.204770e-002,
	5.070285e-001,
	-7.280216e-004,
	1.491533e+000,
	3.635064e-001,
	-9.713808e-001,
	-8.138038e-002,
	3.709854e-001,
	-1.041174e+000,
	-1.814075e-002,
	5.060860e-001,
	-2.053756e-002,
	6.161431e+000,
	1.093736e+000,
	-1.159057e+000,
	-3.698074e-001,
	2.711209e+000,
	-6.006479e-001,
	-4.896926e-002,
	9.273957e-001,
	1.137712e-001,
	-3.496828e+000,
	2.867109e-001,
	-1.011601e+000,
	-8.201890e-002,
	2.105725e-001,
	4.597520e-001,
	1.478925e-001,
	2.138940e-001,
	-5.660670e-002,
	6.057755e+000,
	7.859121e-001,
	-1.078020e+000,
	-1.811580e-001,
	1.646622e-001,
	8.348426e-001,
	1.149064e-001,
	4.985738e-001,
	1.376605e-001,
	2.746607e+000,
	4.999626e-001,
	// albedo 1, turbidity 3
	-1.325672e+000,
	-4.769313e-001,
	-4.111215e+001,
	4.168293e+001,
	-6.274997e-003,
	4.649469e-001,
	1.119411e-002,
	2.631267e+000,
	5.234546e-001,
	-1.619391e+000,
	-8.000253e-001,
	-1.534098e+001,
	1.632706e+001,
	-1.012023e-002,
	4.242255e-001,
	2.931597e-002,
	8.925807e-001,
	3.314765e-001,
	-7.356979e-001,
	1.368406e-001,
	2.972579e+000,
	-3.535359e+000,
	-1.318948e-002,
	4.607620e-001,
	-7.182778e-002,
	6.254100e+000,
	1.236299e+000,
	-1.316217e+000,
	-4.194427e-001,
	3.489902e-002,
	1.289849e+000,
	-4.755960e-002,
	1.138222e+000,
	1.975992e-001,
	-8.991542e-001,
	2.290572e-001,
	-9.502188e-001,
	-1.172703e-001,
	1.405202e+000,
	-3.061919e-001,
	1.058772e-001,
	-3.760592e-001,
	-1.983179e-002,
	3.562353e+000,
	7.895959e-001,
	-1.100117e+000,
	-1.900567e-001,
	4.925030e-001,
	5.250225e-001,
	1.576804e-001,
	1.042701e+000,
	7.330743e-002,
	2.796064e+000,
	6.749783e-001,
	// albedo 1, turbidity 4
	-1.354183e+000,
	-5.130625e-001,
	-4.219268e+001,
	4.271772e+001,
	-5.365373e-003,
	4.136743e-001,
	1.235172e-002,
	2.520122e+000,
	5.187269e-001,
	-1.741434e+000,
	-9.589761e-001,
	-8.230339e+000,
	9.296799e+000,
	-9.600162e-003,
	4.994969e-001,
	2.955452e-002,
	3.667099e-001,
	3.526999e-001,
	-6.917347e-001,
	2.154887e-001,
	-8.760264e-001,
	2.334121e-001,
	-1.909621e-002,
	4.748033e-001,
	-1.138514e-001,
	6.515360e+000,
	1.225097e+000,
	-1.293189e+000,
	-4.218700e-001,
	1.620952e+000,
	-7.858597e-001,
	-3.769410e-002,
	6.636786e-001,
	3.364945e-001,
	-5.341017e-001,
	2.128347e-001,
	-9.735521e-001,
	-1.325495e-001,
	1.007517e+000,
	2.598258e-001,
	6.762169e-002,
	1.421018e-003,
	-6.915987e-002,
	3.185897e+000,
	8.641956e-001,
	-1.094800e+000,
	-1.962062e-001,
	5.755591e-001,
	2.906259e-001,
	2.625748e-001,
	7.644049e-001,
	1.347492e-001,
	2.677126e+000,
	6.465460e-001,
	// albedo 1, turbidity 5
	-1.393063e+000,
	-5.578338e-001,
	-4.185249e+001,
	4.233504e+001,
	-5.435640e-003,
	4.743765e-001,
	7.422477e-003,
	2.442801e+000,
	5.211707e-001,
	-1.939487e+000,
	-1.128509e+000,
	-8.974257e+000,
	9.978383e+000,
	-7.965597e-003,
	2.948830e-001,
	4.436763e-002,
	2.839868e-001,
	3.440424e-001,
	-6.011562e-001,
	2.354877e-001,
	-3.079820e+000,
	2.585094e+000,
	-2.002701e-002,
	7.793909e-001,
	-1.598414e-001,
	5.834678e+000,
	1.202856e+000,
	-1.315676e+000,
	-3.903446e-001,
	1.701900e+000,
	-1.304609e+000,
	-1.045121e-002,
	2.747707e-001,
	4.143967e-001,
	3.197102e-001,
	2.637580e-001,
	-9.618628e-001,
	-1.625841e-001,
	1.187138e+000,
	1.497802e-001,
	-5.590954e-006,
	3.178475e-002,
	-4.153145e-002,
	2.496096e+000,
	8.195082e-001,
	-1.111554e+000,
	-2.365546e-001,
	7.831875e-001,
	2.018684e-001,
	2.074369e-001,
	7.395978e-001,
	1.225730e-001,
	1.876478e+000,
	6.821167e-001,
	// albedo 1, turbidity 6
	-1.427879e+000,
	-5.994879e-001,
	-3.531016e+001,
	3.581581e+001,
	-6.431497e-003,
	4.554192e-001,
	7.348731e-004,
	2.334619e+000,
	5.233377e-001,
	-1.998177e+000,
	-1.206633e+000,
	-2.146510e+001,
	2.242237e+001,
	-5.857596e-003,
	2.755663e-001,
	6.384795e-002,
	1.358244e-001,
	3.328437e-001,
	-6.440630e-001,
	2.058571e-001,
	2.155499e+000,
	-2.587968e+000,
	-1.840023e-002,
	8.826555e-001,
	-2.222452e-001,
	5.847073e+000,
	1.228387e+000,
	-1.229071e+000,
	-3.360441e-001,
	-3.429599e-001,
	6.179469e-001,
	2.029610e-003,
	8.899319e-002,
	5.041624e-001,
	1.882964e-001,
	2.252040e-001,
	-1.022905e+000,
	-2.101621e-001,
	1.915689e+000,
	-6.498794e-001,
	-3.463651e-002,
	8.954605e-002,
	-6.797854e-002,
	2.417705e+000,
	8.568618e-001,
	-1.082538e+000,
	-2.007723e-001,
	4.731009e-001,
	4.077267e-001,
	1.324289e-001,
	6.514880e-001,
	1.702912e-001,
	2.309383e+000,
	6.600895e-001,
	// albedo 1, turbidity 7
	-1.472139e+000,
	-6.499815e-001,
	-3.428465e+001,
	3.469659e+001,
	-5.747023e-003,
	4.174167e-001,
	1.688597e-003,
	2.323046e+000,
	5.395191e-001,
	-2.161176e+000,
	-1.353089e+000,
	-2.226827e+001,
	2.329138e+001,
	-5.583808e-003,
	2.364793e-001,
	6.096656e-002,
	1.944666e-003,
	2.861624e-001,
	-6.593044e-001,
	1.393558e-001,
	4.698373e+000,
	-5.193883e+000,
	-1.998390e-002,
	1.095635e+000,
	-2.391254e-001,
	5.598103e+000,
	1.236193e+000,
	-1.195717e+000,
	-2.972715e-001,
	4.648953e-002,
	3.024588e-001,
	5.003313e-003,
	-3.754741e-001,
	5.247265e-001,
	-1.381312e-001,
	2.493896e-001,
	-1.020139e+000,
	-2.253524e-001,
	3.548437e-001,
	7.030485e-001,
	-2.107076e-002,
	4.581395e-001,
	-3.243757e-002,
	2.453259e+000,
	8.323623e-001,
	-1.098770e+000,
	-2.435780e-001,
	8.761614e-001,
	1.941613e-001,
	-1.990692e-001,
	3.761139e-001,
	1.657412e-001,
	1.590503e+000,
	6.741417e-001,
	// albedo 1, turbidity 8
	-1.648007e+000,
	-8.205121e-001,
	-4.435106e+001,
	4.479801e+001,
	-4.181353e-003,
	3.854830e-001,
	-1.842385e-006,
	2.000281e+000,
	5.518363e-001,
	-2.140986e+000,
	-1.282239e+000,
	-3.979213e+000,
	4.672459e+000,
	-5.008582e-003,
	2.421920e-001,
	6.253602e-002,
	6.612713e-001,
	2.555851e-001,
	-1.300502e+000,
	-5.137898e-001,
	5.179821e-001,
	-4.032341e-001,
	-2.066785e-002,
	1.087929e+000,
	-2.615309e-001,
	4.225887e+000,
	1.229237e+000,
	-6.963340e-001,
	9.241060e-002,
	6.936356e-002,
	-3.588571e-001,
	-5.461843e-002,
	-5.616643e-001,
	5.484166e-001,
	-4.776267e-002,
	2.414935e-001,
	-1.233179e+000,
	-4.325498e-001,
	6.479813e-001,
	8.368356e-001,
	2.458875e-001,
	6.464752e-001,
	-2.897097e-002,
	1.561773e+000,
	8.518598e-001,
	-1.051023e+000,
	-2.533690e-001,
	1.004294e+000,
	3.028083e-001,
	-1.520108e+000,
	1.607013e-001,
	1.619975e-001,
	1.131094e+000,
	6.706655e-001,
	// albedo 1, turbidity 9
	-1.948249e+000,
	-1.097383e+000,
	-4.453697e+001,
	4.494902e+001,
	-3.579939e-003,
	3.491605e-001,
	-2.500253e-006,
	1.740442e+000,
	6.188022e-001,
	-2.154253e+000,
	-1.209559e+000,
	4.144894e+000,
	-3.562411e+000,
	-5.638843e-003,
	1.067169e-001,
	7.594858e-002,
	1.005280e+000,
	1.072543e-001,
	-2.513259e+000,
	-1.507208e+000,
	-1.602979e+000,
	1.404154e+000,
	-5.560750e-003,
	1.240490e+000,
	-2.852117e-001,
	3.485252e+000,
	1.349321e+000,
	-7.832214e-002,
	3.655626e-001,
	3.856288e-001,
	6.867894e-001,
	-1.609523e-001,
	-6.704306e-001,
	5.357301e-001,
	-6.457935e-001,
	1.479503e-001,
	-1.354784e+000,
	-5.454375e-001,
	8.797469e-001,
	-1.466514e+000,
	7.134420e-001,
	5.934903e-001,
	-2.911178e-002,
	8.643737e-001,
	9.030724e-001,
	-1.048324e+000,
	-2.738736e-001,
	8.783074e-001,
	3.246188e+000,
	-4.435369e+000,
	1.251791e-001,
	1.783486e-001,
	1.064657e+000,
	6.522878e-001,
	// albedo 1, turbidity 10
	-2.770408e+000,
	-1.618911e+000,
	-2.504031e+001,
	2.531674e+001,
	-4.239279e-003,
	3.241013e-001,
	-3.764484e-006,
	1.586843e+000,
	7.035906e-001,
	-1.913500e+000,
	-1.144014e+000,
	-1.080587e+001,
	1.153677e+001,
	-1.003197e-002,
	1.577515e-001,
	5.217789e-002,
	1.225278e+000,
	5.172771e-003,
	-5.293208e+000,
	-2.876463e+000,
	2.087053e+000,
	-3.201552e+000,
	3.892964e-003,
	5.323930e-001,
	-2.034512e-001,
	2.617760e+000,
	1.273597e+000,
	9.060340e-001,
	3.773409e-001,
	-6.399945e-001,
	3.213979e+000,
	-9.112172e-002,
	6.494055e-001,
	3.953280e-001,
	5.047796e-001,
	2.998695e-001,
	-1.482179e+000,
	-6.778310e-001,
	1.161775e+000,
	-3.004872e+000,
	4.774797e-001,
	-4.969248e-001,
	-3.512074e-003,
	-1.307190e+000,
	7.927378e-001,
	-9.863181e-001,
	-1.803364e-001,
	5.810824e-001,
	4.580570e+000,
	-3.863454e+000,
	5.328174e-001,
	2.272821e-001,
	1.771114e+000,
	6.791814e-001,
};

float kHosekRadZ[2][10][6] =
{
	// albedo 0, turbidity 1
	1.168084e+000,
	2.156455e+000,
	-3.980314e+000,
	1.989302e+001,
	1.328335e+001,
	1.435621e+001,
	// albedo 0, turbidity 2
	1.135488e+000,
	2.294701e+000,
	-4.585886e+000,
	2.090208e+001,
	1.347840e+001,
	1.467658e+001,
	// albedo 0, turbidity 3
	1.107408e+000,
	2.382765e+000,
	-5.112357e+000,
	2.147823e+001,
	1.493128e+001,
	1.460882e+001,
	// albedo 0, turbidity 4
	1.054193e+000,
	2.592891e+000,
	-6.115000e+000,
	2.268967e+001,
	1.635672e+001,
	1.518999e+001,
	// albedo 0, turbidity 5
	1.006946e+000,
	2.705420e+000,
	-6.698930e+000,
	2.291830e+001,
	1.834324e+001,
	1.570651e+001,
	// albedo 0, turbidity 6
	9.794044e-001,
	2.742440e+000,
	-6.805283e+000,
	2.225271e+001,
	2.050797e+001,
	1.563130e+001,
	// albedo 0, turbidity 7
	9.413577e-001,
	2.722009e+000,
	-6.760707e+000,
	2.098242e+001,
	2.342588e+001,
	1.605011e+001,
	// albedo 0, turbidity 8
	8.917923e-001,
	2.592780e+000,
	-6.152635e+000,
	1.774141e+001,
	2.858324e+001,
	1.657910e+001,
	// albedo 0, turbidity 9
	8.288391e-001,
	2.153434e+000,
	-4.118327e+000,
	1.078118e+001,
	3.681710e+001,
	1.738139e+001,
	// albedo 0, turbidity 10
	7.623528e-001,
	1.418187e+000,
	-8.845235e-001,
	7.590129e-001,
	4.629859e+001,
	1.921657e+001,
	// albedo 1, turbidity 1
	1.352858e+000,
	2.048862e+000,
	-2.053393e+000,
	1.405874e+001,
	3.045344e+001,
	3.044430e+001,
	// albedo 1, turbidity 2
	1.330497e+000,
	2.126497e+000,
	-2.466296e+000,
	1.467559e+001,
	3.090738e+001,
	3.069707e+001,
	// albedo 1, turbidity 3
	1.286344e+000,
	2.200436e+000,
	-2.877228e+000,
	1.492701e+001,
	3.236288e+001,
	3.077223e+001,
	// albedo 1, turbidity 4
	1.234428e+000,
	2.289628e+000,
	-3.404699e+000,
	1.499436e+001,
	3.468390e+001,
	3.084842e+001,
	// albedo 1, turbidity 5
	1.178660e+000,
	2.306071e+000,
	-3.549159e+000,
	1.411006e+001,
	3.754188e+001,
	3.079730e+001,
	// albedo 1, turbidity 6
	1.151366e+000,
	2.333005e+000,
	-3.728627e+000,
	1.363374e+001,
	3.905894e+001,
	3.092599e+001,
	// albedo 1, turbidity 7
	1.101593e+000,
	2.299422e+000,
	-3.565787e+000,
	1.196745e+001,
	4.188472e+001,
	3.102755e+001,
	// albedo 1, turbidity 8
	1.038322e+000,
	2.083539e+000,
	-2.649585e+000,
	8.037389e+000,
	4.700869e+001,
	3.065948e+001,
	// albedo 1, turbidity 9
	9.596146e-001,
	1.671470e+000,
	-8.751538e-001,
	1.679772e+000,
	5.345784e+001,
	3.054520e+001,
	// albedo 1, turbidity 10
	8.640731e-001,
	9.858301e-001,
	1.854956e+000,
	-6.798097e+000,
	5.936468e+001,
	3.110255e+001,
};

#if defined(__clang__)
#pragma clang diagnostic pop
#endif


================================================
FILE: LICENSE.md
================================================
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org>


================================================
FILE: Makefile
================================================
CXXFLAGS = -std=c++11 -O3

sunsky: SunSky.cpp SunSky.hpp SunSkyTool.cpp
	$(CXX) $(CXXFLAGS) -o $@ SunSky.cpp SunSkyTool.cpp

clean:
	$(RM) sunsky


================================================
FILE: README.md
================================================
Overview
========

The code in SunSky.* is an implementation of several sky models:

* The Preetham Clear Sky model. (From ["A Practical Analytic Model for
  Daylight"](https://www.cs.utah.edu/~shirley/papers/sunsky/), Preetham, Shirley
  & Smits.)

* The Hosek Clear Sky model. (From ["An Analytic Model for Full Spectral Sky-Dome
  Radiance"](https://cgg.mff.cuni.cz/projects/SkylightModelling/), Hosek &
  Wilkie.)

* Various luminance-only CIE models:
  * The Clear Sky model
  * The Overcast Sky model
  * The Partly(!) Cloudy Sky model
  * The more recent fifteen CIE sky models from, e.g., "CIE general sky standard
    defining luminance distributions", Darula & Kittler. (There are many variants
    of this paper!)

* The sun model from Preetham (also used by Hosek), and helpers for converting
  from time and place to local sun direction.

In addition, for the Preetham and Hosek models, I have added:

* Table-driven versions for fast evaluation on the GPU (or indeed CPU) via
  64 x 2 lookup table.

* Extended table-driven versions that use zonal harmonics to produce approximate
  mirror-to-diffuse BRDF power convolutions. These use 64 x 8 or 64 x 16
  tables.

* Proper handling of night transitions. The original models assume the sun
  is above the horizon. The supplied code transitions to a dark blue sky as
  the sun fully sets, and then to black towards the end of twilight.

* An extension to allow mixing the clear sky models with an overcast sky
  via a simple linear 'overcast' factor. Generally it's best to use this
  to simulate high cloud, while low-lying clouds are represented explicitly
  in the shader. However, when used for shading rather than skybox display,
  it can also represent low-lying cloud cover.

The Preetham code is an old standby, and has been shipped in several games.

The Hosek code is newer, but has also been shipped several times, and is what I
recommend now, particularly as it includes an integrated ground albedo factor
that feeds into the sky model. It is a re-implementation of the Hosek paper
using floats, with some minor optimisations, and an attempt to make the
structure a bit more obvious.

See [sky.sh](sky.sh) for shader routines to evaluate the Hosek sky model,
optionally with a roughness value, and some notes on how to set up the
corresponding uniforms. The file [skybox_fs.sc](skybox_fs.sc) is an example of
how to use these in a skybox context -- it also adds a sun disc.


Results
=======

Preetham
--------

Clear Sky:

![](images/preetham-1.png)
![](images/preetham-2.png)
![](images/preetham-3.png)
![](images/preetham-4.png)
![](images/preetham-5.png)
![](images/preetham-6.png)

Overcast 50%/Day

![](images/preetham-oc-1.png)
![](images/preetham-oc-2.png)
![](images/preetham-oc-3.png)
![](images/preetham-oc-4.png)
![](images/preetham-oc-5.png)
![](images/preetham-oc-6.png)
<br>
![](images/preetham-ocd-1.png)
![](images/preetham-ocd-2.png)
![](images/preetham-ocd-3.png)
![](images/preetham-ocd-4.png)
![](images/preetham-ocd-5.png)
![](images/preetham-ocd-6.png)

BRDF Day/Sunset:

![](images/preethamBRDF-rd-1.png)
![](images/preethamBRDF-rd-2.png)
![](images/preethamBRDF-rd-3.png)
![](images/preethamBRDF-rd-4.png)
![](images/preethamBRDF-rd-5.png)
![](images/preethamBRDF-rd-6.png)
<br>
![](images/preethamBRDF-rs-1.png)
![](images/preethamBRDF-rs-2.png)
![](images/preethamBRDF-rs-3.png)
![](images/preethamBRDF-rs-4.png)
![](images/preethamBRDF-rs-5.png)
![](images/preethamBRDF-rs-6.png)

Hosek
-----

Clear Sky:

![](images/hosek-1.png)
![](images/hosek-2.png)
![](images/hosek-3.png)
![](images/hosek-4.png)
![](images/hosek-5.png)
![](images/hosek-6.png)

Overcast 50%/Day:

![](images/hosek-oc-1.png)
![](images/hosek-oc-2.png)
![](images/hosek-oc-3.png)
![](images/hosek-oc-4.png)
![](images/hosek-oc-5.png)
![](images/hosek-oc-6.png)
<br>
![](images/hosek-ocd-1.png)
![](images/hosek-ocd-2.png)
![](images/hosek-ocd-3.png)
![](images/hosek-ocd-4.png)
![](images/hosek-ocd-5.png)
![](images/hosek-ocd-6.png)

BRDF Day/Sunset:

![](images/hosekBRDF-rd-1.png)
![](images/hosekBRDF-rd-2.png)
![](images/hosekBRDF-rd-3.png)
![](images/hosekBRDF-rd-4.png)
![](images/hosekBRDF-rd-5.png)
![](images/hosekBRDF-rd-6.png)
<br>
![](images/hosekBRDF-rs-1.png)
![](images/hosekBRDF-rs-2.png)
![](images/hosekBRDF-rs-3.png)
![](images/hosekBRDF-rs-4.png)
![](images/hosekBRDF-rs-5.png)
![](images/hosekBRDF-rs-6.png)


SunSky Tool
===========

Included in SunSkyTool.cpp is a tool exercising most of the sky model
functionality. Current options are below. It can be used to generate top-down
'hemisphere' views with or without fisheye projection, panoramic views, and
cube maps, with various forms of tonemapping. Both LDR (png) and HDR (pfm)
versions are output.

Building
--------

To build this tool, use 'make', or

    c++ --std=c++11 -O3 SunSky.cpp SunSkyTool.cpp -o sunsky

Or add those files to your favourite IDE.

Options
-------

    sunsky <options>

    Options:
      -h : this help
      -t <time>          : 0 - 24
      -d <day of year>   : 0 - 365
      -b <tubidity>      : 2 - 30
      -x <ground_bounce> : 0 - 1
      -l <latitude> <longitude>
      -w <normalisation weight>
      -g <gamma>
      -e <tonemapType> : use given tonemap operator (default: linear)
      -a : autoscale intensity
      -i : invert hemisphere
      -f : fisheye rather than cos projection
      -c : output cubemap instead
      -p : output panorama instead
      -m : output movie, record day as sky.mp4, requires ffmpeg
      -v : verbose
      -s <skyType> : use given sky type
      -r <roughness:float> : specify roughness for PreethamBRDF

    skyType:
      Preetham         (pt)
      PreethamTable    (ptt)
      PreethamBRDF     (ptb)
      Hosek            (hk)
      HosekTable       (hkt)
      HosekBRDF        (hkb)
      cieClear         (cc)
      cieOvercast      (co)
      ciePartlyCloudy  (cp)

    toneMapType:
      linear           (l)
      exponential      (ex)
      reinhard         (rh)

Examples
--------

Show noon sky for the current time of year using Preetham:

    sunsky -t 12

Glossy version of the same sky:

    sunsky -t 12 -s preethamBRDF -r 0.3

Hosek sky at 4pm with greenish albedo, high turbidity, and exponential tone
mapping, saved to a cube map:

    sunsky -t 16 -s hosek -x 0.2 0.5 0.2 -b 6 -e ex -c



================================================
FILE: SunSky.cpp
================================================
//
// SunSky.cpp
//
// Implements SunSky.hpp
//
// Andrew Willmott, Preetham sun/sky based on code by Brian Smits,
// Hosek code used L. Hosek & A. Wilkie code Version 1.4a as reference
//

#include "SunSky.hpp"

#include "HosekDataXYZ.h"
#include "HosekCubic.h"

#include <stdint.h>
#include <float.h>

#ifdef _MSC_VER
    // double->float
    #pragma warning(disable : 4305)
#endif

using namespace SSLib;

// #define SIM_CLAMP                // emulate normalised integer texture, for CPU-side checking
// #define SUPPORT_OVERCAST_CLAMP   // support overcast functionality via tables. (Without overcast, the theta table is within 0-1 anyway.)
#define REMAP_THETA                 // remap cos theta to concentrate table around horizon
#define HOSEK_G_FIX                 // fixes hue ringing during sunset/sunrise with BRDF version of Hosek, causing blue spots opposite
// #define HOSEK_BRDF_ANALYTIC_H    // for A/B'ing H/FH tables vs. direct ZH evaluation
// #define LOCAL_DEBUG              // dump debug/tuning info

#ifdef LOCAL_DEBUG
    #include <stdio.h>
#endif

namespace
{
    // XYZ/RGB for sRGB primaries + D65 white point
    const Vec3f kXYZToR( 3.2404542f, -1.5371385f, -0.4985314f);
    const Vec3f kXYZToG(-0.9692660f,  1.8760108f,  0.0415560f);
    const Vec3f kXYZToB( 0.0556434f, -0.2040259f,  1.0572252f);

    const Vec3f kRGBToX(0.4124564f,  0.3575761f,  0.1804375f);
    const Vec3f kRGBToY(0.2126729f,  0.7151522f,  0.0721750f);
    const Vec3f kRGBToZ(0.0193339f,  0.1191920f,  0.9503041f);

    const Vec2f kClearChroma       (2.0f / 3.0f, 1.0f / 3.0f);
    const Vec2f kOvercastChroma    (1.0f / 3.0f, 1.0f / 3.0f);
    const Vec2f kPartlyCloudyChroma(1.0f / 3.0f, 1.0f / 3.0f);

    inline Vec3f xyYToXYZ(const Vec3f& c)
    {
        return Vec3f(c.x, c.y, 1.0f - c.x - c.y) * c.z / (c.y + 1e-8f);
    }

    inline Vec3f xyYToRGB(const Vec3f& xyY)
    {
        Vec3f XYZ = xyYToXYZ(xyY);

        return Vec3f
        (
            dot(kXYZToR, XYZ),
            dot(kXYZToG, XYZ),
            dot(kXYZToB, XYZ)
        );
    }

    inline Vec3f XYZToRGB(const Vec3f& XYZ)
    {
        return Vec3f
        (
            dot(kXYZToR, XYZ),
            dot(kXYZToG, XYZ),
            dot(kXYZToB, XYZ)
        );
    }

    inline Vec3f RGBToXYZ(const Vec3f& rgb)
    {
        return Vec3f
        (
            dot(kRGBToX, rgb),
            dot(kRGBToY, rgb),
            dot(kRGBToZ, rgb)
        );
    }

    inline float RGBToLuminance(const Vec3f& rgb)
    {
        return dot(kRGBToY, rgb);
    }

    inline Vec2f XYZToChroma(Vec3f XYZ)
    {
        XYZ += Vec3f(1e-8f);  // avoid divide-by-zero in a way that XYZ=0 -> chroma 1/3, 1/3 (white point)
        float sum = XYZ.x + XYZ.y + XYZ.z;

        return Vec2f(XYZ.x / sum, XYZ.y / sum);
    }

    inline uint8_t ToU8(float f)
    {
        if (f <= 0.0f)
            return 0;
        if (f >= 1.0f)
            return 255;

        return uint8_t(f * 255.0f + 0.5f);
    }

    inline float DegreesToRadians(float d)
    {
        return d * (vlf_twoPi / 360.0f);
    }

    inline float ClampUnit(float s)
    {
        if (s <= 0.0f)
            return 0.0f;
        if (s >= 1.0f)
            return 1.0f;
        return s;
    }

    inline float ClampPositive(float s)
    {
        if (s <= 0.0f)
            return 0.0f;
        return s;
    }
    inline Vec3f ClampPositive3(Vec3f v)
    {
        return { ClampPositive(v.x), ClampPositive(v.y), ClampPositive(v.z) };
    }

#ifdef SIM_CLAMP
    inline Vec3f ClampUnit(Vec3f v)
    {
        return { ClampUnit(v.x), ClampUnit(v.y), ClampUnit(v.z) };
    }
#endif

    const float    kFToIBiasF32 = (float)(uint32_t(3) << 22);
    const int32_t  kFToIBiasS32 = 0x4B400000;

    inline int32_t FloorToSInt32(float s) // force int 'fast' version of this
    {
        union
        {
            float f;
            int32_t i;
        } fi;

        fi.f = floorf(s) + kFToIBiasF32;
        return fi.i - kFToIBiasS32;
    }

    inline float LerpClamp(float s)
    {
        if (s <= 0.0f)         return 0.0f;
        if (s >= 1.0f - 1e-6f) return 1.0f - 1e-6f;
        return s;
    }

    template<class T_V> T_V LerpSample(float s, int n, const T_V c[])
    {
        s = LerpClamp(s);
        VL_ASSERT(s >= 0.0f && s < 1.0f);

        s *= n - 1;

        int si0 = FloorToSInt32(s);
        int si1 = (si0 + 1);

        float sf = s - si0;

        return c[si0] * (1 - sf)
             + c[si1] *    sf   ;
    }

    template<class T_V> T_V BiLerpSample(float s, float t, int w, int h, const T_V c[])
    {
        s = LerpClamp(s);
        t = LerpClamp(t);
        VL_ASSERT(s >= 0.0f && t >= 0.0f);
        VL_ASSERT(s  < 1.0f && t  < 1.0f);

        s *= w - 1;
        t *= h - 1;

        int si0 = FloorToSInt32(s);
        int ti0 = FloorToSInt32(t);

        int si1 = (si0 + 1);
        int ti1 = (ti0 + 1);

        float sf = s - si0;
        float tf = t - ti0;

        ti0 *= w;
        ti1 *= w;

        return c[si0 + ti0] * (1 - sf) * (1 - tf)
             + c[si1 + ti0] *    sf    * (1 - tf)
             + c[si0 + ti1] * (1 - sf) *    tf
             + c[si1 + ti1] *    sf    *    tf;
    }
}

Vec3f SSLib::SunDirection(float timeOfDay, float timeZone, int julianDay, float latitude, float longitude)
{
    float solarTime = timeOfDay
        +  (0.170f * sinf(4 * vlf_pi * (julianDay - 80) / 373)
          - 0.129f * sinf(2 * vlf_pi * (julianDay -  8) / 355))
        + (longitude / 15 - timeZone);

    float solarDeclination = (0.4093f * sinf(2 * vlf_pi * (julianDay - 81) / 368));

    float latRads = DegreesToRadians(latitude);

    float sx = cosf(solarDeclination)                 * sinf(vlf_pi * solarTime / 12);
    float sy = cosf(latRads) * sinf(solarDeclination)
             + sinf(latRads) * cosf(solarDeclination) * cosf(vlf_pi * solarTime / 12);
    float sz = sinf(latRads) * sinf(solarDeclination)
             - cosf(latRads) * cosf(solarDeclination) * cosf(vlf_pi * solarTime / 12);

    return Vec3f(sx, sy, sz);
}

Vec2f SSLib::SunriseAndSunset(float timeZone, int julianDay, float latitude, float longitude)
{
    float solarTime =
        +  (0.170f * sinf(4 * vlf_pi * (julianDay - 80) / 373)
          - 0.129f * sinf(2 * vlf_pi * (julianDay -  8) / 355))
        + (longitude / 15 - timeZone);

    float solarDeclination = (0.4093f * sinf(vlf_twoPi * (julianDay - 81) / 368));
    float latRads = DegreesToRadians(latitude);

    float lhs = tanf(latRads) * tanf(solarDeclination);

    // x = cos(theta) has two solutions for theta: acos(x) and 2pi - acos(x)
    float h = 24.0f * acosf(lhs) / vlf_twoPi;
    float sunrise =    h      - solarTime;
    float sunset  = 24.0f - h - solarTime;

    return Vec2f(sunrise, sunset);
}

const float SSLib::kSunDiameter   = 1.392f;
const float SSLib::kSunDistance   = 149.6f;
const float SSLib::kSunCosAngle   = sqrtf(1.0f - sqr(0.5f * kSunDiameter / kSunDistance));   // = 0.999989
const float SSLib::kSunSolidAngle = vlf_twoPi * (1.0f - kSunCosAngle);  // = 6.79998e-05 steradians


//------------------------------------------------------------------------------
// Preetham sun model
//------------------------------------------------------------------------------

namespace
{
    // From Preetham. Was used by Hosek as sun source, so can be used in conjunction
    // with either.
    const float kSunRadiance[16][16][3] =
    {
        { {39.4028f, 1.98004f, 5.96046e-08f}, {68821.4f, 29221.3f, 3969.28f}, {189745, 116333, 43283.4f}, {284101, 199843, 103207}, {351488, 265139, 161944}, {400584, 315075, 213163}, {437555, 353806, 256435}, {466261, 384480, 292823}, {489140, 409270, 323569}, {507776, 429675, 349757}, {523235, 446739, 372260}, {536260, 461207, 391767}, {547379, 473621, 408815}, {556978, 484385, 423827}, {565348, 493805, 437137}, {572701, 502106, 449002} },
        { {34.9717f, 0.0775114f, 0}, {33531, 11971.9f, 875.627f}, {127295, 71095, 22201.3f}, {216301, 142827, 66113.9f}, {285954, 205687, 115900}, {339388, 256990, 163080}, {380973, 298478, 205124}, {414008, 332299, 241816}, {440780, 360220, 273675}, {462869, 383578, 301382}, {481379, 403364, 325586}, {497102, 420314, 346848}, {510615, 434983, 365635}, {522348, 447795, 382333}, {532628, 459074, 397255}, {541698, 469067, 410647} },
        { {10.0422f, 0, 0.318865f}, {16312.8f, 4886.47f, 84.98f}, {85310.4f, 43421.5f, 11226.2f}, {164586, 102046, 42200.5f}, {232559, 159531, 82822.4f}, {287476, 209581, 124663}, {331656, 251771, 163999}, {367569, 287173, 199628}, {397168, 317025, 231420}, {421906, 342405, 259652}, {442848, 364181, 284724}, {460784, 383030, 307045}, {476303, 399483, 326987}, {489856, 413955, 344876}, {501789, 426774, 360988}, {512360, 438191, 375548} },
        { {2.3477f, 5.96046e-08f, 0.129991f}, {117.185f, 30.0648f, 0}, {57123.3f, 26502.1f, 5565.4f}, {125170, 72886.2f, 26819.8f}, {189071, 123708, 59081.9f}, {243452, 170892, 95209.2f}, {288680, 212350, 131047}, {326303, 248153, 164740}, {357842, 278989, 195638}, {384544, 305634, 223657}, {407381, 328788, 248954}, {427101, 349038, 271779}, {444282, 366866, 292397}, {459372, 382660, 311064}, {472723, 396734, 328012}, {484602, 409337, 343430} },
        { {0.383395f, 0, 0.027703f}, {58.0534f, 12.8383f, 0}, {38221.6f, 16163.6f, 2681.55f}, {95147.4f, 52043, 16954.8f}, {153669, 95910.9f, 42062}, {206127, 139327, 72640.8f}, {251236, 179082, 104653}, {289639, 214417, 135896}, {322383, 245500, 165343}, {350467, 272796, 192613}, {374734, 296820, 217644}, {395864, 318050, 240533}, {414400, 336900, 261440}, {430773, 353719, 280544}, {445330, 368800, 298027}, {458337, 382374, 314041} },
        { {0.0560895f, 0, 0.00474608f}, {44.0061f, 8.32402f, 0}, {25559, 9849.99f, 1237.01f}, {72294.8f, 37148.7f, 10649}, {124859, 74345.6f, 29875.8f}, {174489, 113576, 55359.1f}, {218617, 151011, 83520.3f}, {257067, 185252, 112054}, {290413, 216016, 139698}, {319390, 243473, 165842}, {344686, 267948, 190241}, {366896, 289801, 212852}, {386513, 309371, 233736}, {403942, 326957, 252998}, {419513, 342823, 270764}, {433487, 357178, 287149} },
        { {0.00811136f, 0, 0.000761211f}, {38.0318f, 6.09287f, 0}, {17083.4f, 5996.83f, 530.476f}, {54909.7f, 26508.7f, 6634.5f}, {101423, 57618.7f, 21163.3f}, {147679, 92573, 42135.2f}, {190207, 127327, 66606.4f}, {228134, 160042, 92352.6f}, {261593, 190061, 117993}, {291049, 217290, 142758}, {317031, 241874, 166258}, {340033, 264051, 188331}, {360490, 284081, 208945}, {378771, 302212, 228135}, {395184, 318667, 245976}, {409974, 333634, 262543} },
        { {0.00118321f, 0, 0.000119328f}, {34.5228f, 4.62524f, 0}, {11414.1f, 3646.94f, 196.889f}, {41690.9f, 18909.8f, 4091.39f}, {82364.6f, 44646.9f, 14944.8f}, {124966, 75444.4f, 32024.3f}, {165467, 107347, 53075.4f}, {202437, 138252, 76076.7f}, {235615, 167214, 99627}, {265208, 193912, 122858}, {291580, 218327, 145272}, {315124, 240580, 166611}, {336208, 260851, 186761}, {355158, 279331, 205696}, {372256, 296206, 223440}, {387729, 311636, 240030} },
        { {0.000174701f, 0, 1.84774e-05f}, {31.4054f, 3.4608f, 0}, {7624.24f, 2215.02f, 48.0059f}, {31644.8f, 13484.4f, 2490.1f}, {66872.4f, 34589.1f, 10515}, {105728, 61477.4f, 24300.5f}, {143926, 90494.6f, 42256.1f}, {179617, 119420, 62635.3f}, {212200, 147105, 84088.4f}, {241645, 173041, 105704}, {268159, 197064, 126911}, {292028, 219187, 147374}, {313550, 239512, 166913}, {333008, 258175, 185447}, {350650, 275321, 202953}, {366683, 291081, 219433} },
        { {2.61664e-05f, 0, 2.86102e-06f}, {27.3995f, 2.42835f, 5.96046e-08f}, {391.889f, 104.066f, 0}, {24013.1f, 9611.97f, 1489.37f}, {54282.4f, 26792.1f, 7366.53f}, {89437, 50090, 18406.3f}, {125174, 76280.7f, 33609.8f}, {159354, 103145, 51538.2f}, {191098, 129407, 70945.4f}, {220163, 154409, 90919.4f}, {246607, 177864, 110847}, {270613, 199690, 130337}, {292410, 219912, 149156}, {312229, 238614, 167173}, {330289, 255902, 184328}, {346771, 271876, 200589} },
        { {3.93391e-06f, 0, 4.76837e-07f}, {21.8815f, 1.51091f, 0}, {106.645f, 26.2423f, 0}, {18217.8f, 6848.77f, 869.811f}, {44054, 20748.7f, 5134.5f}, {75644.5f, 40807, 13913.2f}, {108852, 64293.6f, 26704.2f}, {141364, 89082.8f, 42380.1f}, {172081, 113831, 59831.4f}, {200579, 137777, 78179.7f}, {226776, 160529, 96794.7f}, {250759, 181920, 115250}, {272686, 201910, 133270}, {292739, 220530, 150685}, {311103, 237847, 167398}, {327934, 253933, 183349} },
        { {6.55651e-07f, 0, 1.19209e-07f}, {15.4347f, 0.791314f, 0}, {67.98f, 15.4685f, 0}, {13818.5f, 4877.71f, 490.832f}, {35746.5f, 16065.3f, 3556.94f}, {63969.8f, 33240.3f, 10492.5f}, {94648, 54185.5f, 21192.5f}, {125394, 76932.4f, 34825.1f}, {154946, 100125, 50435.6f}, {182726, 122930, 67203.7f}, {208530, 144877, 84504.4f}, {232352, 165726, 101891}, {254283, 185376, 119059}, {274458, 203811, 135807}, {293024, 221062, 152009}, {310113, 237169, 167579} },
        { {5.96046e-08f, 0, 0}, {9.57723f, 0.336247f, 0}, {52.9113f, 11.1074f, 0}, {10479.8f, 3472.19f, 262.637f}, {29000.9f, 12436.5f, 2445.87f}, {54089.5f, 27073.4f, 7891.84f}, {82288.3f, 45662.7f, 16796.5f}, {111218, 66434.7f, 28595.3f}, {139508, 88064, 42494.5f}, {166453, 109678, 57749.2f}, {191743, 130747, 73756.6f}, {215288, 150968, 90064.3f}, {237114, 170191, 106348}, {257311, 188355, 122384}, {275989, 205455, 138022}, {293255, 221507, 153152} },
        { {0, 0, 0}, {5.37425f, 0.109694f, 0}, {44.9811f, 8.68891f, 5.96046e-08f}, {7946.76f, 2470.32f, 128.128f}, {23524.7f, 9625.27f, 1666.58f}, {45729.5f, 22047.9f, 5917.85f}, {71535.2f, 38477.1f, 13293.2f}, {98636.4f, 57365.7f, 23460.6f}, {125598, 77452, 35785}, {151620, 97851, 49607}, {176299, 117990, 64359}, {199469, 137520, 79594.4f}, {221098, 156245, 94979.6f}, {241228, 174066, 110274}, {259937, 190947, 125309}, {277307, 206875, 139956} },
        { {0, 0, 0}, {2.83079f, 0.0199037f, 0}, {40.0718f, 7.10214f, 0}, {6025.35f, 1756.45f, 51.1916f}, {19080.1f, 7447.79f, 1122.67f}, {38657, 17952.9f, 4422.16f}, {62181.1f, 32419.5f, 10503.8f}, {87471.2f, 49531.4f, 19230.6f}, {113069, 68115.1f, 30117.9f}, {138102, 87295.1f, 42596.4f}, {162092, 106474, 56143.2f}, {184805, 125266, 70327.1f}, {206156, 143438, 84812.9f}, {226144, 160857, 99349.8f}, {244814, 177459, 113755}, {262220, 193206, 127887} },
        { {0, 0, 0}, {1.43779f, 0, 0.00738072f}, {36.6245f, 5.93644f, 0}, {4568.17f, 1248.02f, 9.13028f}, {15473.4f, 5761.51f, 745.266f}, {32674.7f, 14616.6f, 3291.16f}, {54045.1f, 27313.1f, 8284.85f}, {77563.8f, 42764.4f, 15747.9f}, {101783, 59900.8f, 25332.8f}, {125782, 77874.7f, 36561.6f}, {149022, 96078.4f, 48962}, {171213, 114101, 62125.3f}, {192218, 131678, 75721.7f}, {211998, 148648, 89495.8f}, {230564, 164920, 103255}, {247950, 180437, 116847}}
    };
}

Vec3f SSLib::SunRGB(float cosTheta, float turbidity)
{
    if (cosTheta < 0.0f)
        return vl_0;

    float s = cosTheta;
    float t = (turbidity - 2.0f) / 10.0f; // useful range is 2-12
    Vec3f sun = BiLerpSample(s, t, 16, 16, (const Vec3f*) kSunRadiance);

    // 683 converts from watts to candela at 540e12 hz. Really, we should be weighting by luminous efficiency curve rather than CIE_Y
    sun *=  683.0f;
    sun *= kSunSolidAngle;

    return sun;
}

Vec2f SSLib::SunChroma(float cosTheta, float turbidity)
{
    return XYZToChroma(RGBToXYZ(SunRGB(cosTheta, turbidity)));
}

float SSLib::SunLuminance(float cosTheta, float turbidity)
{
    return RGBToLuminance(SunRGB(cosTheta, turbidity));
}


//------------------------------------------------------------------------------
// CIE models
//------------------------------------------------------------------------------

float SSLib::CIEOvercastSkyLuminance(const Vec3f& v, float Lz)
{
    float cosTheta = v.z;

    return Lz * (1.0f + 2.0f * cosTheta) / 3.0f;
}

float SSLib::CIEClearSkyLuminance(const Vec3f& v, const Vec3f& toSun, float Lz)
{
    float cosThetaV = v.z;

    if (cosThetaV < 0.0f)
        cosThetaV = 0.0f;

    float cosThetaS = toSun.z;
    float thetaS    = acosf(cosThetaS);

    float cosGamma = dot(toSun, v);
    float gamma    = acosf(cosGamma);

    float top1 = 0.91f + 10 * expf(-3 * gamma ) + 0.45f * sqr(cosGamma);
    float bot1 = 0.91f + 10 * expf(-3 * thetaS) + 0.45f * sqr(cosThetaS);

    float top2 = 1 - expf(-0.32f / (cosThetaV + 1e-6f));
    float bot2 = 1 - expf(-0.32f);

    return Lz * (top1 * top2) / (bot1 * bot2);
}

float SSLib::CIEPartlyCloudySkyLuminance(const Vec3f& v, const Vec3f& toSun, float Lz)
{
    float cosThetaV = v.z;

    float cosThetaS = toSun.z;
    float thetaS    = acosf(cosThetaS);

    float cosGamma = dot(toSun, v);
    float gamma    = acosf(cosGamma);

    float top1 = 0.526f + 5 * expf(-1.5f * gamma);
    float bot1 = 0.526f + 5 * expf(-1.5f * thetaS);

    float top2 = 1 - expf(-0.8f / (cosThetaV + 1e-6f));
    float bot2 = 1 - expf(-0.8f);

    return Lz * (top1 * top2) / (bot1 * bot2);
}

// More modern setup, see Darula & Kittler 2002
namespace
{
    const float kCIEStandardSkyCoeffs[15][5] =
    {
        {  4.0, -0.70,  0, -1.0, 0    },   // Overcast. When normalised this is a fit of the older (1 + 2cos(theta)) / 3 formula found in CIEOvercastSkyLuminance.
        {  4.0, -0.70,  2, -1.5, 0.15 },   // Overcast, with steep luminance gradation and slight brightening towards the sun

        {  1.1, -0.80,  0, -1.0, 0    },   // Overcast, moderately graded with azimuthal uniformity
        {  1.1, -0.80,  2, -1.5, 0.15 },   // Overcast, moderately graded and slight brightening towards the sun

        {  0.0, -1.00,  0, -1.0, 0    },   // Sky of uniform luminance
        {  0.0, -1.00,  2, -1.5, 0.15 },   // Partly cloudy sky, no gradation towards zenith, slight brightening towards the sun
        {  0.0, -1.00,  5, -2.5, 0.30 },   // Partly cloudy sky, no gradation towards zenith, brighter circumsolar region
        {  0.0, -1.00, 10, -3.0, 0.45 },   // Partly cloudy sky, no gradation towards zenith, distinct solar corona

        { -1.0, -0.55,  2, -1.5, 0.15 },   // Partly cloudy, with the obscured sun
        { -1.0, -0.55,  5, -2.5, 0.30 },   // Partly cloudy, with brighter circumsolar region
        { -1.0, -0.55, 10, -3.0, 0.45 },   // White-blue sky with distinct solar corona

        { -1.0, -0.32, 10, -3.0, 0.45 },   // CIE Standard Clear Sky, low illuminance turbidity. T <= 2.45
        { -1.0, -0.32, 16, -3.0, 0.30 },   // CIE Standard Clear Sky, polluted atmosphere

        { -1.0, -0.15, 16, -3.0, 0.30 },   // Cloudless turbid sky with broad solar corona
        { -1.0, -0.15, 24, -2.8, 0.15 },   // White-blue turbid sky with broad solar corona
    };

    inline float CIELumRatio(const Vec3f& v, const Vec3f& toSun, float a, float b, float c, float d, float e)
    {
        float cosThetaV = v.z;

        float cosThetaS = toSun.z;
        float thetaS    = acosf(cosThetaS);

        float cosGamma = dot(toSun, v);
        float gamma    = acosf(cosGamma);

        float top1 = 1.0f + a * expf(b / (cosThetaV + 1e-6f));
        float bot1 = 1.0f + a * expf(b);

        float top2 = 1.0f + c * expf(d * gamma ) + e * sqr(cosGamma);
        float bot2 = 1.0f + c * expf(d * thetaS) + e * sqr(cosThetaS);

        return (top1 * top2) / (bot1 * bot2);
    }
}

float SSLib::CIEStandardSky(int type, const Vec3f& v, const Vec3f& toSun, float Lz)
{
    VL_ASSERT(type >= 0 && type < 15);
    const float* c = kCIEStandardSkyCoeffs[type];
    return CIELumRatio(v, toSun, c[0], c[1], c[2], c[3], c[4]) * Lz;
}

float SSLib::ZenithLuminance(float thetaS, float T)
{
    float chi = (4.0f / 9.0f - T / 120.0f) * (vlf_pi - 2.0f * thetaS);
    float Lz = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
    Lz *= 1000.0;   // conversion from kcd/m^2 to cd/m^2
    return Lz;
}





//------------------------------------------------------------------------------
// - Preetham
//------------------------------------------------------------------------------

namespace
{
    //
    // The Perez function is as follows:
    //
    //    P(t, g) =  (1 + A e ^ (B / cos(t))) (1 + C e ^ (D g)  + E cos(g)  ^ 2)
    //               --------------------------------------------------------
    //               (1 + A e ^ B)            (1 + C e ^ (D ts) + E cos(ts) ^ 2)
    //

    // A: sky
    // B: sky tightness
    // C: sun
    // D: sun tightness, higher = tighter
    // E: rosy hue around sun

    inline float PerezUpper(const float* lambdas, float cosTheta, float gamma, float cosGamma)
    {
        return  (1.0f + lambdas[0] * expf(lambdas[1] / (cosTheta + 1e-6f)))
              * (1.0f + lambdas[2] * expf(lambdas[3] * gamma) + lambdas[4] * sqr(cosGamma));
    }

    inline float PerezLower(const float* lambdas, float cosThetaS, float thetaS)
    {
        return  (1.0f + lambdas[0] * expf(lambdas[1]))
              * (1.0f + lambdas[2] * expf(lambdas[3] * thetaS) + lambdas[4] * sqr(cosThetaS));
    }
}

SkyPreetham::SkyPreetham() :
    mToSun(vl_z),

    // mPerez_x[5] = {}
    // mPerez_y[5] = {}
    // mPerez_Y[5] = {}
    mZenith(vl_0),
    mPerezInvDen(vl_1)
{
}

void SkyPreetham::Update(const Vec3f& sun, float turbidity, float overcast, float horizCrush)
{
    mToSun = sun;

    float   T = turbidity;

    mPerez_Y[0] =  0.17872f * T - 1.46303f;
    mPerez_Y[1] = -0.35540f * T + 0.42749f;
    mPerez_Y[2] = -0.02266f * T + 5.32505f;
    mPerez_Y[3] =  0.12064f * T - 2.57705f;
    mPerez_Y[4] = -0.06696f * T + 0.37027f;

    mPerez_x[0] = -0.01925f * T - 0.25922f;
    mPerez_x[1] = -0.06651f * T + 0.00081f;
    mPerez_x[2] = -0.00041f * T + 0.21247f;
    mPerez_x[3] = -0.06409f * T - 0.89887f;
    mPerez_x[4] = -0.00325f * T + 0.04517f;

    mPerez_y[0] = -0.01669f * T - 0.26078f;
    mPerez_y[1] = -0.09495f * T + 0.00921f;
    mPerez_y[2] = -0.00792f * T + 0.21023f;
    mPerez_y[3] = -0.04405f * T - 1.65369f;
    mPerez_y[4] = -0.01092f * T + 0.05291f;

    float cosTheta = mToSun.z;
    float theta  = acosf(cosTheta);    // angle from zenith rather than horizon
    float theta2 = sqr(theta);
    float theta3 = theta2 * theta;
    float T2 = sqr(T);

    // mZenith stored as xyY
    mZenith.z = ZenithLuminance(theta, T);

    mZenith.x =
        ( 0.00165f * theta3 - 0.00374f * theta2 + 0.00208f * theta + 0.00000f) * T2 +
        (-0.02902f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) * T  +
        ( 0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25885f);

    mZenith.y =
        ( 0.00275f * theta3 - 0.00610f * theta2 + 0.00316f * theta + 0.00000f) * T2 +
        (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00515f) * T  +
        ( 0.15346f * theta3 - 0.26756f * theta2 + 0.06669f * theta + 0.26688f);

    // Adjustments (extensions)

    if (cosTheta < 0.0f)    // Handle sun going below the horizon
    {
        float s = ClampUnit(1.0f + cosTheta * 50.0f);   // goes from 1 to 0 as the sun sets

        // Take C/E which control sun term to zero
        mPerez_x[2] *= s;
        mPerez_y[2] *= s;
        mPerez_Y[2] *= s;
        mPerez_x[4] *= s;
        mPerez_y[4] *= s;
        mPerez_Y[4] *= s;
    }

    if (overcast != 0.0f)      // Handle overcast term
    {
        float invOvercast = 1.0f - overcast;

        // lerp back towards unity
        mPerez_x[0] *= invOvercast;  // main sky chroma -> base
        mPerez_y[0] *= invOvercast;

        // sun flare -> 0 strength/base chroma
        mPerez_x[2] *= invOvercast;
        mPerez_y[2] *= invOvercast;
        mPerez_Y[2] *= invOvercast;
        mPerez_x[4] *= invOvercast;
        mPerez_y[4] *= invOvercast;
        mPerez_Y[4] *= invOvercast;

        // lerp towards a fit of the CIE cloudy sky model: 4, -0.7
        mPerez_Y[0] = lerp(mPerez_Y[0],  4.0f, overcast);
        mPerez_Y[1] = lerp(mPerez_Y[1], -0.7f, overcast);

        // lerp base colour towards white point
        mZenith.x = mZenith.x * invOvercast + 0.333f * overcast;
        mZenith.y = mZenith.y * invOvercast + 0.333f * overcast;
    }

    if (horizCrush != 0.0f)
    {
        // The Preetham sky model has a "muddy" horizon, which can be objectionable in
        // typical game views. We allow artistic control over it.
        mPerez_Y[1] *= horizCrush;
        mPerez_x[1] *= horizCrush;
        mPerez_y[1] *= horizCrush;
    }

    // initialize sun-constant parts of the Perez functions

    Vec3f perezLower    // denominator terms are dependent on sun angle only
    (
        PerezLower(mPerez_x, cosTheta, theta),
        PerezLower(mPerez_y, cosTheta, theta),
        PerezLower(mPerez_Y, cosTheta, theta)
    );

    mPerezInvDen = mZenith / perezLower;
}

Vec3f SkyPreetham::SkyRGB(const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(mToSun, v);
    float gamma    = acosf(cosGamma);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    Vec3f xyY
    (
        PerezUpper(mPerez_x, cosTheta, gamma, cosGamma),
        PerezUpper(mPerez_y, cosTheta, gamma, cosGamma),
        PerezUpper(mPerez_Y, cosTheta, gamma, cosGamma)
    );

    xyY *= mPerezInvDen;

    return xyYToRGB(xyY);
}

float SkyPreetham::SkyLuminance(const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(mToSun, v);
    float gamma    = acosf(cosGamma);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    return PerezUpper(mPerez_Y, cosTheta, gamma, cosGamma) * mPerezInvDen.z;
}

Vec2f SkyPreetham::SkyChroma(const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(mToSun, v);
    float gamma    = acosf(cosGamma);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    return Vec2f
    (
        PerezUpper(mPerez_x, cosTheta, gamma, cosGamma) * mPerezInvDen.x,
        PerezUpper(mPerez_y, cosTheta, gamma, cosGamma) * mPerezInvDen.y
    );
}


//------------------------------------------------------------------------------
// SkyHosek
//------------------------------------------------------------------------------

namespace
{
    inline float EvalQuintic(const float w[6], const float data[6])
    {
        return    w[0] * data[0]
                + w[1] * data[1]
                + w[2] * data[2]
                + w[3] * data[3]
                + w[4] * data[4]
                + w[5] * data[5];
    }

    inline void EvalQuintic(const float w[6], const float data[6][9], float coeffs[9])
    {
        for (int i = 0; i < 9; i++)
        {
            coeffs[i] =   w[0] * data[0][i]
                        + w[1] * data[1][i]
                        + w[2] * data[2][i]
                        + w[3] * data[3][i]
                        + w[4] * data[4][i]
                        + w[5] * data[5][i];
        }
    }

    inline void FindQuinticWeights(float s, float w[6])
    {
        float s1 = s;
        float s2 = s1 * s1;
        float s3 = s1 * s2;
        float s4 = s2 * s2;
        float s5 = s2 * s3;

        float is1 = 1.0f - s1;
        float is2 = is1 * is1;
        float is3 = is1 * is2;
        float is4 = is2 * is2;
        float is5 = is2 * is3;

        w[0] = is5;
        w[1] = is4 * s1 *  5.0f;
        w[2] = is3 * s2 * 10.0f;
        w[3] = is2 * s3 * 10.0f;
        w[4] = is1 * s4 *  5.0f;
        w[5] =       s5;
    }


    float FindHosekCoeffs
    (
        const float   dataset9[2][10][6][9],    // albedo x 2, turbidity x 10, quintics x 6, weights x 9
        const float   datasetR[2][10][6],       // albedo x 2, turbidity x 10, quintics x 6
        float         turbidity,
        float         albedo,
        float         solarElevation,
        float         coeffs[9]
    )
    {
        int tbi = int(floorf(turbidity));

        if (tbi < 1)
            tbi = 1;
        else if (tbi > 9)
            tbi = 9;

        float tbf = turbidity - tbi;

        const float s = powf(solarElevation / vlf_halfPi, (1.0f / 3.0f));

        float quinticWeights[6];
        FindQuinticWeights(s, quinticWeights);

        float ic[4][9];

        EvalQuintic(quinticWeights, dataset9[0][tbi - 1], ic[0]);
        EvalQuintic(quinticWeights, dataset9[1][tbi - 1], ic[1]);
        EvalQuintic(quinticWeights, dataset9[0][tbi    ], ic[2]);
        EvalQuintic(quinticWeights, dataset9[1][tbi    ], ic[3]);

        float ir[4] =
        {
            EvalQuintic(quinticWeights, datasetR[0][tbi - 1]),
            EvalQuintic(quinticWeights, datasetR[1][tbi - 1]),
            EvalQuintic(quinticWeights, datasetR[0][tbi    ]),
            EvalQuintic(quinticWeights, datasetR[1][tbi    ]),
        };

        float cw[4] =
        {
            (1.0f - albedo) * (1.0f - tbf),
            albedo          * (1.0f - tbf),
            (1.0f - albedo) * tbf,
            albedo          * tbf,
        };

        for (int i = 0; i < 9; i++)
            coeffs[i] = cw[0] * ic[0][i]
                      + cw[1] * ic[1][i]
                      + cw[2] * ic[2][i]
                      + cw[3] * ic[3][i];

        return cw[0] * ir[0] + cw[1] * ir[1] + cw[2] * ir[2] + cw[3] * ir[3];
    }

    inline Vec4f CubicWeights(float s)
    {
        float s1 = s;
        float s2 = s1 * s1;
        float s3 = s1 * s2;

        float is1 = 1.0f - s1;
        float is2 = is1 * is1;
        float is3 = is1 * is2;

        return Vec4f(is3, is2 * s1 * 3.0f, is1 * s2 * 3.0f, s3);
    }

    float FindHosekCoeffs
    (
        const float   datasetC[10][4][2][4],
        float         turbidity,
        float         albedo,
        float         solarElevation,
        float         coeffs[9]
    )
    {
        const float t = (turbidity - 1.0f) / 9.0f;
        const float s = powf(solarElevation / vlf_halfPi, (1.0f / 3.0f));

        Vec4f wt = CubicWeights(t);
        Vec4f ws = CubicWeights(s);

        float rad;

        for (int ic = 0; ic < 10; ic++)
        {
            Vec4f cs;

            for (int iz = 0; iz < 4; iz++)
            {
                const Vec4f& ct0 = (Vec4f&) *datasetC[ic][iz][0];
                const Vec4f& ct1 = (Vec4f&) *datasetC[ic][iz][1];

                cs[iz] = dot(wt, lerp(ct0, ct1, albedo));
            }

            if (ic == 0)
                rad = dot(ws, cs);
            else
                coeffs[ic - 1] = dot(ws, cs);
        }

        return rad;
    }

    // Hosek:
    // (1 + A e ^ (B / cos(t))) (1 + C e ^ (D g) + E cos(g) ^ 2   + F mieM(g, G)  + H cos(t)^1/2 + (I - 1))
    //
    // These bits are the same as Preetham, but do different jobs in some cases
    // A: sky gradient, carries white -> blue gradient
    // B: sky tightness
    // C: sun, carries most of sun-centred blue term
    // D: sun tightness, higher = tighter
    // E: rosy hue around sun
    //
    // Hosek-specific:
    // F: mie term, does most of the heavy lifting for sunset glow
    // G: mie tuning
    // H: zenith gradient
    // I: constant term balanced with H

    // Notes:
    // A/B still carries some of the "blue" base of sky, but much comes from C/D
    // C/E minimal effect in sunset situations, carry bulk of sun halo in sun-overhead
    // F/G sunset glow, but also takes sun halo from yellowish to white overhead

    float EvalHosekCoeffs(const float coeffs[9], float cosTheta, float gamma, float cosGamma)
    {
        // Current coeffs ordering is AB I CDEF HG
        //                            01 2 3456 78
        const float expM   = expf(coeffs[4] * gamma);   // D g
        const float rayM   = cosGamma * cosGamma;       // Rayleigh scattering
        const float mieM   = (1.0f + rayM) / powf((1.0f + coeffs[8] * coeffs[8] - 2.0f * coeffs[8] * cosGamma), 1.5f);  // G
        const float zenith = sqrtf(cosTheta);           // vertical zenith gradient

        return (1.0f
                     + coeffs[0] * expf(coeffs[1] / (cosTheta + 0.01f))     // A, B
               )
             * (1.0f
                     + coeffs[3] * expM     // C
                     + coeffs[5] * rayM     // E
                     + coeffs[6] * mieM     // F
                     + coeffs[7] * zenith   // H
                     + (coeffs[2] - 1.0f)   // I
               );
    }
}


SkyHosek::SkyHosek() :
    mToSun(vl_z)
{
}

void SkyHosek::Update(const Vec3f& sun, float turbidity, Vec3f rgbAlbedo, float overcast)
{
    mToSun = sun;

    float solarElevation = mToSun.z > 0.0f ? asinf(mToSun.z) : 0.0f;    // altitude rather than zenith, so sin rather than cos

    mAlbedo = RGBToXYZ(rgbAlbedo);

    // Note that the hosek coefficients change with time of day, vs. Preetham where the 'upper' coefficients stay the same,
    // and only the scaler mPerezInvDen, consisting of time-dependent normalisation and zenith luminnce factors, changes.

    if (!mUseCubic)
    {
        mRadXYZ[0] = FindHosekCoeffs(kHosekCoeffsX, kHosekRadX, turbidity, mAlbedo.x, solarElevation, mCoeffsXYZ[0]);
        mRadXYZ[1] = FindHosekCoeffs(kHosekCoeffsY, kHosekRadY, turbidity, mAlbedo.y, solarElevation, mCoeffsXYZ[1]);
        mRadXYZ[2] = FindHosekCoeffs(kHosekCoeffsZ, kHosekRadZ, turbidity, mAlbedo.z, solarElevation, mCoeffsXYZ[2]);
    }
    else
    {
        mRadXYZ[0] = FindHosekCoeffs(kHCX, turbidity, mAlbedo.x, solarElevation, mCoeffsXYZ[0]);
        mRadXYZ[1] = FindHosekCoeffs(kHCY, turbidity, mAlbedo.y, solarElevation, mCoeffsXYZ[1]);
        mRadXYZ[2] = FindHosekCoeffs(kHCZ, turbidity, mAlbedo.z, solarElevation, mCoeffsXYZ[2]);
    }

    mRadXYZ *= 683; // convert to luminance in lumens

    if (mToSun.z < 0.0f)   // sun below horizon?
    {
        float s = ClampUnit(1.0f + mToSun.z * 50.0f);   // goes from 1 to 0 as the sun sets
        float is = 1.0f - s;

        // Emulate Preetham's zenith darkening
        float darken = ZenithLuminance(acosf(mToSun.z), turbidity) / ZenithLuminance(vlf_halfPi, turbidity);

        // Take C/E/F which control sun term to zero
        for (int j = 0; j < 3; j++)
        {
            mCoeffsXYZ[j][3] *= s;
            mCoeffsXYZ[j][5] *= s;
            mCoeffsXYZ[j][6] *= s;

            // Take horizon term H to zero, as it's an orange glow at this point
            mCoeffsXYZ[j][7] *= s;

            // Take I term back to 1
            mCoeffsXYZ[j][2] *= s;
            mCoeffsXYZ[j][2] += is;
        }

        mRadXYZ *= darken;
    }

    if (overcast != 0.0f)      // Handle overcast term
    {
        float is = overcast;
        float s = 1.0f - overcast;     // goes to 0 as we go to overcast

        // Hosek isn't self-normalising, unlike Preetham/CIE, which divides by PreethamLower().
        // Thus when we lerp to the CIE overcast model, we get some non-linearities.
        // We deal with this by using ratios of normalisation terms to balance.
        // Another difference is that Hosek is relative to the average radiance,
        // whereas CIE is the zenith radiance, so rather than taking the zenith
        // as normalising as in CIE, we average over the zenith and two horizon
        // points.
        float cosGammaZ = mToSun.z;
        float gammaZ    = acosf(cosGammaZ);
        float cosGammaH = mToSun.y;
        float gammaHP   = acosf(+mToSun.y);
        float gammaHN   = vlf_pi - gammaHP;

        float sc0 = EvalHosekCoeffs(mCoeffsXYZ[1], 1.0f, gammaZ,   cosGammaZ) * 2.0f
                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHP, +cosGammaH)
                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHN, -cosGammaH);

        for (int j = 0; j < 3; j++)
        {
            // sun flare -> 0 strength/base chroma
            // Take C/E/F which control sun term to zero
            mCoeffsXYZ[j][3] *= s;
            mCoeffsXYZ[j][5] *= s;
            mCoeffsXYZ[j][6] *= s;

            // Take H back to 0
            mCoeffsXYZ[j][7] *= s;

            // Take I term back to 1
            mCoeffsXYZ[j][2] *= s;
            mCoeffsXYZ[j][2] += is;

            // Take A/B to  CIE cloudy sky model: 4, -0.7
            mCoeffsXYZ[j][0] = lerp(mCoeffsXYZ[j][0],  4.0f, is);
            mCoeffsXYZ[j][1] = lerp(mCoeffsXYZ[j][1], -0.7f, is);
        }

        float sc1 = EvalHosekCoeffs(mCoeffsXYZ[1], 1.0f, gammaZ,   cosGammaZ) * 2.0f
                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHP, +cosGammaH)
                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHN, -cosGammaH);

        float rescale = sc0 / sc1;
        mRadXYZ *= rescale;

        // move back to white point
        mRadXYZ.x = lerp(mRadXYZ.x, mRadXYZ.y, is);
        mRadXYZ.z = lerp(mRadXYZ.z, mRadXYZ.y, is);
    }

#ifdef LOCAL_DEBUG
    for (int j = 0; j < 3; j++)
    {
        printf("%c: Rad = %8.2f, C[9] = ", 'X' + j, mRadXYZ[j]);
        for (int i = 0; i < 9; i++)
            printf("%5.2f ", mCoeffsXYZ[j][i]);
        printf("\n");
    }
#endif
}

float SkyHosek::SkyLuminance(const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(mToSun, v);
    float gamma    = acosf(cosGamma);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    return EvalHosekCoeffs(mCoeffsXYZ[1], cosTheta, gamma, cosGamma) * mRadXYZ.y;
}

Vec3f SkyHosek::SkyXYZ(const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(mToSun, v);
    float gamma    = acosf(cosGamma);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    Vec3f XYZ =
    {
        EvalHosekCoeffs(mCoeffsXYZ[0], cosTheta, gamma, cosGamma),
        EvalHosekCoeffs(mCoeffsXYZ[1], cosTheta, gamma, cosGamma),
        EvalHosekCoeffs(mCoeffsXYZ[2], cosTheta, gamma, cosGamma)
    };

    XYZ *= mRadXYZ;

    return XYZ;
}

Vec3f SkyHosek::SkyRGB(const Vec3f& v) const
{
    return XYZToRGB(SkyXYZ(v));
}


//------------------------------------------------------------------------------
// SkyTable
//------------------------------------------------------------------------------

// Pre-calculated table version, particularly useful for shader use

namespace
{
#ifdef REMAP_THETA
    // remap cos theta to concentrate table around horizon
    inline float MapTheta(float t)
    {
        return t >= 0.0f ? sqrtf(t) : -sqrtf(-t);
    }
    inline float UnmapTheta(float t)
    {
        return t >= 0.0f ? sqr(t) : -sqr(t);
    }
    inline float UnmapThetaWeight(float t)
    {
        return 2 * fabsf(t);
    }
#else
    inline float MapTheta(float t)
    {
        return t;
    }
    inline float UnmapTheta(float t)
    {
        return t;
    }
    inline float UnmapThetaWeight(float t)
    {
        return 1.0f;
    }
#endif

    // remap cosGamma to concentrate table around the sun location
    inline float MapGamma(float g)
    {
        return sqrtf(0.5f * (1.0f - g));
    }
    inline float UnmapGamma(float g)
    {
        return 1 - 2 * sqr(g);
    }
    inline float UnmapGammaWeight(float g)
    {
        // |UnmapGamma'|
        return 4 * g;
    }
}

void SkyTable::FindThetaGammaTables(const SkyPreetham& pt)
{
    float dt = 1.0f / (kTableSize - 1);
    float t = dt * 1e-6f;    // epsilon to avoid divide by 0, which can lead to NaN when m_perez_[1] = 0

    for (int i = 0; i < kTableSize; i++)
    {
        float cosTheta = UnmapTheta(t);

        mThetaTable[i][0] =  -pt.mPerez_x[0] * expf(pt.mPerez_x[1] / cosTheta);
        mThetaTable[i][1] =  -pt.mPerez_y[0] * expf(pt.mPerez_y[1] / cosTheta);
        mThetaTable[i][2] =  -pt.mPerez_Y[0] * expf(pt.mPerez_Y[1] / cosTheta);

        mMaxTheta = mMaxTheta >= mThetaTable[i][2] ? mMaxTheta : mThetaTable[i][2];

        float cosGamma = UnmapGamma(t);
        float gamma = acosf(cosGamma);

        mGammaTable[i][0] =  pt.mPerez_x[2] * expf(pt.mPerez_x[3] * gamma) + pt.mPerez_x[4] * sqr(cosGamma);
        mGammaTable[i][1] =  pt.mPerez_y[2] * expf(pt.mPerez_y[3] * gamma) + pt.mPerez_y[4] * sqr(cosGamma);
        mGammaTable[i][2] =  pt.mPerez_Y[2] * expf(pt.mPerez_Y[3] * gamma) + pt.mPerez_Y[4] * sqr(cosGamma);

        mMaxGamma = mMaxGamma >= mGammaTable[i][2] ? mMaxGamma : mGammaTable[i][2];

        t += dt;
    }

    mXYZ = false;

#ifdef SUPPORT_OVERCAST_CLAMP
    mMaxTheta = -pt.mPerez_Y[0] * expf(pt.mPerez_Y[1]);
#endif

#ifdef SIM_CLAMP
    for (int i = 0; i < kTableSize; i++)
    {
        mThetaTable[i].z /= mMaxTheta;
        mGammaTable[i].z /= mMaxGamma;
        mThetaTable[i] = ClampUnit(mThetaTable[i]);
        mGammaTable[i] = ClampUnit(mGammaTable[i]);
    }
#endif

#ifdef LOCAL_DEBUG
    printf("PTx: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mThetaTable[i].x);
    printf("\n");
    printf("PTy: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mThetaTable[i].y);
    printf("\n");
    printf("PTY: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mThetaTable[i].z);
    printf("\n");

    printf("PGx: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mGammaTable[i].x);
    printf("\n");
    printf("PGy: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mGammaTable[i].y);
    printf("\n");
    printf("PGY: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mGammaTable[i].z);
    printf("\n");
#endif
}

void SkyTable::FindThetaGammaTables(const SkyHosek& hk)
{
    const float (&coeffsXYZ)[3][9] = hk.mCoeffsXYZ;

    mMaxGamma = 1.0f;

    float dt = 1.0f / (kTableSize - 1);
    float t = 0.0f;

    for (int i = 0; i < kTableSize; i++)
    {
        float cosTheta = UnmapTheta(t);
        float cosGamma = UnmapGamma(t);
        float gamma = acosf(cosGamma);

        float rayM = cosGamma * cosGamma;

        for (int j = 0; j < 3; j++)
        {
            const float (&coeffs)[9] = coeffsXYZ[j];

            mThetaTable[i][j] = -coeffs[0] * expf(coeffs[1] / (cosTheta + 0.01f));

            float expM   = expf(coeffs[4] * gamma);
            float mieM   = (1.0f + rayM) / powf((1.0f + coeffs[8] * coeffs[8] - 2.0f * coeffs[8] * cosGamma), 1.5f);

            mGammaTable[i][j] = coeffs[3] * expM + coeffs[5] * rayM + coeffs[6] * mieM;

            mMaxGamma = mMaxGamma >= mGammaTable[i][j] ? mMaxGamma : mGammaTable[i][j];
        }

        t += dt;
    }

    mXYZ = true;

#ifdef SUPPORT_OVERCAST_CLAMP
    mMaxTheta = -coeffsXYZ[1][0] * expf(coeffsXYZ[1][1]);
#endif

#ifdef SIM_CLAMP
    for (int i = 0; i < kTableSize; i++)
    {
        mThetaTable[i] = ClampUnit(mThetaTable[i] / mMaxTheta);
        mGammaTable[i] = ClampUnit(mGammaTable[i] / mMaxGamma);
    }
#endif

#ifdef LOCAL_DEBUG
    printf("HTY: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mThetaTable[i].x);
    printf("\n");
    printf("HTY: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mThetaTable[i].y);
    printf("\n");
    printf("HTY: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mThetaTable[i].z);
    printf("\n");

    printf("HGX: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mGammaTable[i].x);
    printf("\n");
    printf("HGY: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mGammaTable[i].y);
    printf("\n");
    printf("HGZ: ");
    for (int i = 0; i < kTableSize; i += 4)
        printf("%5.2f ", mGammaTable[i].z);
    printf("\n");
#endif
}

Vec3f SkyTable::SkyRGB(const SkyPreetham& pt, const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(pt.mToSun, v);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    float t = MapTheta(cosTheta);
    float g = MapGamma(ClampUnit(cosGamma));

    Vec3f F = LerpSample(t, kTableSize, mThetaTable);
    Vec3f G = LerpSample(g, kTableSize, mGammaTable);

#ifdef SIM_CLAMP
    F.z *= mMaxTheta;
    G.z *= mMaxGamma;
#endif

    Vec3f xyY = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G);

    xyY *= pt.mPerezInvDen;

    return xyYToRGB(xyY);
}

Vec3f SkyTable::SkyRGB(const SkyHosek& hk, const Vec3f& v) const
{
    float cosTheta = v.z;
    float cosGamma = dot(hk.mToSun, v);

    if (cosTheta < 0.0f)
        cosTheta = 0.0f;

    float t = MapTheta(cosTheta);
    float g = MapGamma(cosGamma);

    Vec3f F = LerpSample(t, kTableSize, mThetaTable);
    Vec3f G = LerpSample(g, kTableSize, mGammaTable);

    const float zenith = sqrtf(cosTheta);
    Vec3f H
    (
        hk.mCoeffsXYZ[0][7] * zenith + (hk.mCoeffsXYZ[0][2] - 1.0f),
        hk.mCoeffsXYZ[1][7] * zenith + (hk.mCoeffsXYZ[1][2] - 1.0f),
        hk.mCoeffsXYZ[2][7] * zenith + (hk.mCoeffsXYZ[2][2] - 1.0f)
    );

#ifdef SIM_CLAMP
    F *= mMaxTheta;
    G *= mMaxGamma;
#endif

    // (1 - F(theta)) * (1 + G(phi) + H(theta))
    Vec3f XYZ = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G + H);

    XYZ *= hk.mRadXYZ;

    return XYZToRGB(XYZ);
}

void SkyTable::FillTexture(int width, int height, uint8_t image[][4]) const
{
    VL_ASSERT(width == kTableSize);
    VL_ASSERT(height == 2);

    (void) width;
    (void) height;

    for (int i = 0; i < width; i++)
    {
        Vec3f c = mThetaTable[i];

        if (mXYZ)
            c /= mMaxTheta;
        else
            c.z /= mMaxTheta;

        image[i][2] = ToU8(c.x);
        image[i][1] = ToU8(c.y);
        image[i][0] = ToU8(c.z);
        image[i][3] = 255;
    }

    image += width;

    for (int i = 0; i < width; i++)
    {
        Vec3f c = mGammaTable[i];

        if (mXYZ)
            c /= mMaxGamma;
        else
            c.z /= mMaxGamma;

        image[i][2] = ToU8(c.x);
        image[i][1] = ToU8(c.y);
        image[i][0] = ToU8(c.z);
        image[i][3] = 255;
    }
}

void SkyTable::FillTexture(int width, int height, float image[][4]) const
{
    VL_ASSERT(width == kTableSize);
    VL_ASSERT(height == 2);

    (void) width;
    (void) height;

    for (int i = 0; i < width; i++)
        *(Vec4f*) image[i] = Vec4f(mThetaTable[i], 1.0f);

    image += width;

    for (int i = 0; i < width; i++)
        *(Vec4f*) image[i] = Vec4f(mGammaTable[i], 1.0f);
}

//------------------------------------------------------------------------------
// SkyBRDF
//------------------------------------------------------------------------------

// Extended version that enables lerping between perfect mirror and fully
// diffuse.

namespace
{
    // ZH routines from SHLib
    const float kZH_Y_0 = sqrtf( 1 / (   4 * vlf_pi));  //         1
    const float kZH_Y_1 = sqrtf( 3 / (   4 * vlf_pi));  //         z
    const float kZH_Y_2 = sqrtf( 5 / (  16 * vlf_pi));  // 1/2     (3 z^2 - 1)
    const float kZH_Y_3 = sqrtf( 7 / (  16 * vlf_pi));  // 1/2     (5 z^3 - 3 z)
    const float kZH_Y_4 = sqrtf( 9 / ( 256 * vlf_pi));  // 1/8     (35 z^4 - 30 z^2 + 3)
    const float kZH_Y_5 = sqrtf(11 / ( 256 * vlf_pi));  // 1/8     (63 z^5 - 70 z^3 + 15 z)
    const float kZH_Y_6 = sqrtf(13 / (1024 * vlf_pi));  // 1/16    (231 z^6 - 315 z^4 + 105 z^2 - 5)

    void CalcCosPowerSatZH7(float n, float zcoeffs[7])
    {
        zcoeffs[0] =   1.0f / (n + 1);
        zcoeffs[1] =   1.0f / (n + 2);
        zcoeffs[2] =   3.0f / (n + 3) -   1.0f / (n + 1);
        zcoeffs[3] =   5.0f / (n + 4) -   3.0f / (n + 2);
        zcoeffs[4] =  35.0f / (n + 5) -  30.0f / (n + 3) +   3.0f / (n + 1);
        zcoeffs[5] =  63.0f / (n + 6) -  70.0f / (n + 4) +  15.0f / (n + 2);
        zcoeffs[6] = 231.0f / (n + 7) - 315.0f / (n + 5) + 105.0f / (n + 3) - 5.0f / (n + 1);

        // apply norm constants
        zcoeffs[0] *= vlf_twoPi * kZH_Y_0;
        zcoeffs[1] *= vlf_twoPi * kZH_Y_1;
        zcoeffs[2] *= vlf_twoPi * kZH_Y_2;
        zcoeffs[3] *= vlf_twoPi * kZH_Y_3;
        zcoeffs[4] *= vlf_twoPi * kZH_Y_4;
        zcoeffs[5] *= vlf_twoPi * kZH_Y_5;
        zcoeffs[6] *= vlf_twoPi * kZH_Y_6;

        // [0]: 2pi sqrtf( 1 / (   4 * vlf_pi)) / 1
        // we'll multiply by alpha = sqrtf(4.0f * vlf_pi / (2 * i + 1)) in convolution, leaving 2pi.
    }

    void CalcZH7Weights(float z, float w[7])
    {
        float z2 = z * z;
        float z3 = z2 * z;
        float z4 = z2 * z2;
        float z5 = z2 * z3;
        float z6 = z3 * z3;

        w[0] = kZH_Y_0;
        w[1] = kZH_Y_1 * z;
        w[2] = kZH_Y_2 * (3 * z2 - 1);
        w[3] = kZH_Y_3 * (5 * z3 - 3 * z);
        w[4] = kZH_Y_4 * (35 * z4 - 30 * z2 + 3);
        w[5] = kZH_Y_5 * (63 * z5 - 70 * z3 + 15 * z);
        w[6] = kZH_Y_6 * (231 * z6 - 315 * z4 + 105 * z2 - 5);
    }

    inline float WindowScale(int n, float gamma)
    {
        float nt = float(n * (n + 1));
        return 1.0f / (1.0f + gamma * nt * nt);
    }
    // End ZH routines from SHLib

    template<class T> T SampleZH7(float z, const T zhCoeffs[7])
    {
        float weights[7];
        CalcZH7Weights(z, weights);

        T c = zhCoeffs[0] * weights[0];

        for (int i = 1; i < 7; i++)
            c += zhCoeffs[i] * weights[i];

        return c;
    }

    template<class T> void AddZH7Sample(float z, T c, T zhCoeffs[7])
    {
        float weights[7];
        CalcZH7Weights(z, weights);

        for (int i = 0; i < 7; i++)
            zhCoeffs[i] += c * weights[i];
    }

    template<class T> void ApplyZH7Windowing(float gamma, T coeffs[7])
    {
        for (int i = 0; i < 7; i++)
            coeffs[i] *= WindowScale(i, gamma);
    }

    // ConvolveZHWithZH with Vec3 and divides result through by the first coefficient.
    template<class T> void ConvolveZH7WithZH7Norm(const float brdfCoeffs[7], const T zhCoeffsIn[7], T zhCoeffsOut[7])
    {
        zhCoeffsOut[0] = zhCoeffsIn[0];

        for (int i = 1; i < 7; i++)
        {
            float invAlpha = sqrtf(2.0f * i + 1);

            zhCoeffsOut[i] = zhCoeffsIn[i] * (brdfCoeffs[i] / (invAlpha * brdfCoeffs[0]));
        }
    }

    template<class T> void FindZH7FromThetaTable(int tableSize, const T table[], T zhCoeffs[7])
    {
        float dt = 1.0f / (tableSize - 1);
        float t = 0.0f;

        for (int i = 0; i < 7; i++)
            zhCoeffs[i] = vl_0;

        for (int i = 0; i < tableSize; i++)
        {
            float tt = 2 * t - 1;
            float z = UnmapTheta(tt);
            float dz = vlf_twoPi * UnmapThetaWeight(tt) * (2.0f * dt);
            AddZH7Sample(z, table[i] * dz, zhCoeffs);

            t += dt;
        }
    }

    template<class T> void GenerateThetaTableFromZH7(const T zhCoeffs[7], int tableSize, T table[])
    {
        float dt = 1.0f / (tableSize - 1);
        float t = 0.0f;

        for (int i = 0; i < tableSize; i++)
        {
            table[i] = SampleZH7(UnmapTheta(2 * t - 1), zhCoeffs);

            t += dt;
        }
    }

    template<class T> void FindZH7FromGammaTable(int tableSize, const T table[], T zhCoeffs[7])
    {
        float dg = 1.0f / (tableSize - 1);
        float g = 0.0f;

        for (int i = 0; i < 7; i++)
            zhCoeffs[i] = vl_0;

        for (int i = 0; i < tableSize; i++)
        {
            // Works better for gamma to effectively importance-sample sun area
            float z = UnmapGamma(g);
            float dz = vlf_twoPi * UnmapGammaWeight(g) * dg;

            AddZH7Sample(z, table[i] * dz, zhCoeffs);

            g += dg;
        }
    }

    template<class T> void GenerateGammaTableFromZH7(T zhCoeffs[7], int tableSize, T table[])
    {
        float dg = 1.0f / (tableSize - 1);
        float g = 0.0f;

        for (int i = 0; i < tableSize; i++)
        {
            table[i] = SampleZH7(UnmapGamma(g), zhCoeffs);
            g += dg;
        }
    }

    inline Vec3f Bias_xyY(Vec3f c)  // effectively make delta lum proportional to real lum, and scale xy by lum
    {
        c.z += 1.0f;
        c.x *= c.z;
        c.y *= c.z;
        return c;
    }

    inline Vec3f Unbias_xyY(Vec3f c)  // Return to delta xyY form
    {
        float y = c.z;
        if (y < 1e-2f)
            y = 1e-2f;

        c.x /= y;
        c.y /= y;
        c.z -= 1.0f;
        return c;
    }

    const float kThetaW = 0.01f;    // windowing gamma to use for theta table
    const float kGammaW = 0.002f;   // windowing gamma to use for gamma table

    float kThetaWHosek  = 0.11f;   // windowing gamma to use for Hosek theta table
    float kGammaWHosek  = 0.002f;  // windowing gamma to use for Hosek gamma table
    float kThetaWHosekH = 0.01f;   // windowing gamma to use for Hosek theta/H table

    constexpr float RowPower(float i, float n)
    {
        // use N = 2 / r^2 - 2, and then +1 for base cos power
        // float r = i / (n - 1);  requires C++14, amazing
        // return 2.0f / (r * r + 1e-8f) - 1.0f;
        return 2.0f / ((i / (n - 1)) * (i / (n - 1)) + 1e-8f) - 1.0f;
    }

#ifdef COMPACT_BRDF_TABLE
    const float kRowPowers[SkyBRDF::kBRDFSamples] = { RowPower(0, 4), RowPower(1, 4), RowPower(2, 4), RowPower(3, 4) };
#else
    const float kRowPowers[SkyBRDF::kBRDFSamples] = { RowPower(0, 8), RowPower(1, 8), RowPower(2, 8), RowPower(3, 8), RowPower(4, 8), RowPower(5, 8), RowPower(6, 8), RowPower(7, 8) };
#endif
}

void SkyBRDF::FindBRDFTables(const SkyTable& table, const SkyPreetham&)
{
    // The BRDF tables cover the entire sphere, so we must resample theta from the Perez/Hosek tables which cover a hemisphere.
    Vec3f thetaTable[kTableSize];
    const Vec3f* gammaTable = table.mGammaTable;

    // Fill top hemisphere of table
    for (int i = 0; i < kHalfTableSize; i++)
        thetaTable[kHalfTableSize + i] = table.mThetaTable[2 * i];

    // Fill lower hemisphere with term that evaluates close to 0, to avoid below-ground luminance leaking in.
    // TODO: modify this using ground albedo
    Vec3f lowerHemi = Vec3f(0.0f, 0.0f, 0.999f);  // 0.999 as theta table is used as (1 - F)

    for (int i = 0; i < kHalfTableSize; i++)
        thetaTable[i] = lowerHemi;

    // Project tables into ZH coefficients
    Vec3f zhCoeffsTheta[7];
    Vec3f zhCoeffsGamma[7];

    // theta table works better if we operate on something proportional to real luminance
    Vec3f biasedThetaTable[kTableSize];
    for (int i = 0; i < kTableSize; i++)
        biasedThetaTable[i] = Bias_xyY(thetaTable[i]);

    FindZH7FromThetaTable(kTableSize, biasedThetaTable, zhCoeffsTheta);
    FindZH7FromGammaTable(kTableSize,       gammaTable, zhCoeffsGamma);

    ApplyZH7Windowing(kThetaW, zhCoeffsTheta);
    ApplyZH7Windowing(kGammaW, zhCoeffsGamma);

    // row 0 is the original unconvolved signal

    // Firstly, fill -ve z with reflected +ve z, ramped to 0 at z = -1. This avoids discontinuities at the horizon.
    for (int i = 0; i < kHalfTableSize; i++)
    {
        thetaTable[i] = table.mThetaTable[kTableSize - 1 - 2 * i];

        // Ramp luminance down
        float s = (i + 0.5f) / kHalfTableSize;
        s = sqrtf(s);

        thetaTable[i].z = thetaTable[i].z * s + (1 - s);
    }

    for (int i = 0; i < kTableSize; i++)
    {
        mBRDFThetaTable[0][i] = thetaTable[i];
        mBRDFGammaTable[0][i] = gammaTable[i];
    }

    // rows 1..n-1 are successive convolutions
    Vec3f zhCoeffsThetaConv[7];
    Vec3f zhCoeffsGammaConv[7];

    for (int r = 1; r < kBRDFSamples; r++)
    {
        float s = kRowPowers[r];

        float csCoeffs[7];
        CalcCosPowerSatZH7(s, csCoeffs);

        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsTheta, zhCoeffsThetaConv);
        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsGamma, zhCoeffsGammaConv);

        GenerateThetaTableFromZH7(zhCoeffsThetaConv, kTableSize, mBRDFThetaTable[r]);
        GenerateGammaTableFromZH7(zhCoeffsGammaConv, kTableSize, mBRDFGammaTable[r]);

        for (int i = 0; i < kTableSize; i++)
            mBRDFThetaTable[r][i] = Unbias_xyY(mBRDFThetaTable[r][i]);
    }

    mMaxTheta = table.mMaxTheta;
    mMaxGamma = table.mMaxGamma;
    mXYZ = false;
    mHasHTerm = false;

#ifdef SIM_CLAMP
    for (int r = 1; r < kBRDFSamples; r++)
        for (int i = 0; i < kTableSize; i++)
        {
            mBRDFThetaTable  [r][i] = ClampUnit(mBRDFThetaTable  [r][i]);
            mBRDFGammaTable  [r][i] = ClampUnit(mBRDFGammaTable  [r][i]);
        }
#endif
}

void SkyBRDF::FindBRDFTables(const SkyTable& table, const SkyHosek& hk)
{
    // The BRDF tables cover the entire sphere, so we must resample theta from the Perez/Hosek tables which cover a hemisphere.
    Vec3f thetaTable[SkyTable::kTableSize];
    const Vec3f* gammaTable = table.mGammaTable;

    // Fill top hemisphere of table
    for (int i = 0; i < kHalfTableSize; i++)
        thetaTable[kHalfTableSize + i] = table.mThetaTable[2 * i];

    // Fill lower hemisphere with albedo-weighted sky.

    for (int i = 0; i < kHalfTableSize; i++)
    {
        thetaTable[i] = table.mThetaTable[kTableSize - 1 - 2 * i];
        // Table is F, C = (1 - F)
        thetaTable[i] = Vec3f(vl_1) - hk.mAlbedo * (Vec3f(vl_1) - thetaTable[i]);
    }

    // Project tables into ZH coefficients
    Vec3f zhCoeffsTheta[7];
    Vec3f zhCoeffsGamma[7];

    // theta table works better if we operate on something proportional to real luminance
    Vec3f biasedThetaTable[kTableSize];

    for (int i = 0; i < kTableSize; i++)
        biasedThetaTable[i] = thetaTable[i] + vl_one;

    FindZH7FromThetaTable(kTableSize, biasedThetaTable, zhCoeffsTheta);
    FindZH7FromGammaTable(kTableSize,       gammaTable, zhCoeffsGamma);

    ApplyZH7Windowing(kThetaWHosek, zhCoeffsTheta);
    ApplyZH7Windowing(kGammaWHosek, zhCoeffsGamma);

    // row 0 is the original unconvolved signal, just copy it
    for (int i = 0; i < kTableSize; i++)
    {
        mBRDFThetaTable[0][i] = thetaTable[i];
        mBRDFGammaTable[0][i] = gammaTable[i];
    }

    // Construct H term table -- just the zenith part as we can potentially store as 4th component
    for (int i = 0; i < kHalfTableSize; i++)
    {
        float cosTheta = UnmapTheta(i / float(kHalfTableSize - 1));
        float zenith = sqrtf(cosTheta);

        mBRDFThetaTableH[0][kHalfTableSize + i   ] = zenith;
        mBRDFThetaTableH[0][kHalfTableSize -1 - i] = hk.mAlbedo.y * zenith;  // see mirror comment above
    }

    // Calculate FH as we get slightly better results convolving the full term
    // rather than approximating, at the cost of an extra table
    for (int i = 0; i < kTableSize; i++)
        mBRDFThetaTableFH[0][i] = mBRDFThetaTableH[0][i] * thetaTable[i];

    float zhCoeffsH[7];
    Vec3f zhCoeffsFH[7];

    FindZH7FromThetaTable(kTableSize, mBRDFThetaTableH [0], zhCoeffsH);
    FindZH7FromThetaTable(kTableSize, mBRDFThetaTableFH[0], zhCoeffsFH);

    ApplyZH7Windowing(kThetaWHosekH, zhCoeffsH);
    ApplyZH7Windowing(kThetaWHosekH, zhCoeffsFH);

    // Rows 1..n-1 are successive convolutions

    for (int r = 1; r < kBRDFSamples; r++)
    {
        Vec3f zhCoeffsThetaConv  [7];
        Vec3f zhCoeffsGammaConv  [7];
        float zhCoeffsThetaConvH [7];
        Vec3f zhCoeffsThetaConvFH[7];

        // Scale up to full windowing at full spec power...
        float rw = sqrtf(1.0f - r / (kBRDFSamples - 1));

        float s = kRowPowers[r];

        float csCoeffs[7];
        CalcCosPowerSatZH7(s, csCoeffs);

        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsTheta, zhCoeffsThetaConv);
        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsGamma, zhCoeffsGammaConv);
        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsH    , zhCoeffsThetaConvH);
        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsFH   , zhCoeffsThetaConvFH);

        // Generate convolved tables from ZH
        GenerateThetaTableFromZH7(zhCoeffsThetaConv,   kTableSize, mBRDFThetaTable  [r]);
        GenerateGammaTableFromZH7(zhCoeffsGammaConv,   kTableSize, mBRDFGammaTable  [r]);
        GenerateThetaTableFromZH7(zhCoeffsThetaConvH,  kTableSize, mBRDFThetaTableH [r]);
        GenerateThetaTableFromZH7(zhCoeffsThetaConvFH, kTableSize, mBRDFThetaTableFH[r]);

        for (int i = 0; i < kTableSize; i++)
        {
            mBRDFThetaTable[r][i] -= vl_one;

        #ifdef HOSEK_G_FIX
            // Ringing on the Hosek G term leads to blue spots opposite the sun
            // in sunset situatons. Trying to solve this completely via windowing
            // leads to excessive blurring, so we also compensate by scaling down
            // the far pole in this situation.
            float g = i / float(kTableSize - 1);
            float cosGamma = UnmapGamma(g);
            if (cosGamma < -0.6f)
                mBRDFGammaTable[r][i] *= ClampUnit(1.0f - sqr(-0.6f - cosGamma) * 1.5f * rw);
        #endif
        }
    }

    mMaxTheta = table.mMaxTheta;
    mMaxGamma = table.mMaxGamma;
    mXYZ = true;
    mHasHTerm = true;

#ifdef SIM_CLAMP
    for (int r = 1; r < kBRDFSamples; r++)
        for (int i = 0; i < kTableSize; i++)
        {
            mBRDFThetaTable  [r][i] = ClampUnit(mBRDFThetaTable  [r][i]);
            mBRDFGammaTable  [r][i] = ClampUnit(mBRDFGammaTable  [r][i]);
            mBRDFThetaTableH [r][i] = ClampUnit(mBRDFThetaTableH [r][i]);
            mBRDFThetaTableFH[r][i] = ClampUnit(mBRDFThetaTableFH[r][i]);
        }
#endif
}

Vec3f SkyBRDF::ConvolvedSkyRGB(const SkyPreetham& pt, const Vec3f& v, float r) const
{
    VL_ASSERT(!mXYZ);

    float cosTheta = v.z;
    float cosGamma = dot(pt.mToSun, v);

    float t = 0.5f * (MapTheta(cosTheta) + 1);
    float g = MapGamma(ClampUnit(cosGamma));

    Vec3f F = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTable[0]);
    Vec3f G = BiLerpSample(g, r, kTableSize, kBRDFSamples, mBRDFGammaTable[0]);

#ifdef SIM_CLAMP
    F.z *= mMaxTheta;
    G.z *= mMaxGamma;
#endif

    // (1 - F(theta)) * (1 + G(phi))
    Vec3f xyY = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G);

    xyY *= pt.mPerezInvDen;

    return xyYToRGB(xyY);
}

Vec3f SkyBRDF::ConvolvedSkyRGB(const SkyHosek& hk, const Vec3f& v, float r) const
{
    VL_ASSERT(mXYZ);

    float cosTheta = v.z;
    float cosGamma = dot(hk.mToSun, v);

    float t = 0.5f * (MapTheta(cosTheta) + 1);
    float g = MapGamma(ClampUnit(cosGamma));

    Vec3f F = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTable[0]);
    Vec3f G = BiLerpSample(g, r, kTableSize, kBRDFSamples, mBRDFGammaTable[0]);

#ifdef SIM_CLAMP
    F *= mMaxTheta;
    G *= mMaxGamma;
#endif

    Vec3f cH(hk.mCoeffsXYZ[0][7], hk.mCoeffsXYZ[1][7], hk.mCoeffsXYZ[2][7]);
    Vec3f cI(hk.mCoeffsXYZ[0][2], hk.mCoeffsXYZ[1][2], hk.mCoeffsXYZ[2][2]);
    cI -= vl_1;

#ifdef HOSEK_BRDF_ANALYTIC_H
    // Evaluate H term on the fly, for cross-checking.
    // This could be done in shader to avoid the additional table lookup,
    // but it's a fair bit of math, and using the table variant ensures better
    // consistency with the F/G parts.
    float zhZ[7];
    CalcCosPowerSatZH7(0.5f, zhZ);

    float zhR[7];
    float n = LerpSample(r, kBRDFSamples, kRowPowers);
    CalcCosPowerSatZH7(n, zhR);

    float zhZR[7];
    ConvolveZH7WithZH7Norm(zhR, zhZ, zhZR);

    float zenith = SampleZH7(cosTheta, zhZR);

    Vec3f H = zenith * cH + cI;
    Vec3f FH = F * H;
#else
    Vec3f H  = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTableH [0]) * cH;
    Vec3f FH = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTableFH[0]) * cH;

#ifdef SIM_CLAMP
    FH *= mMaxTheta;
#endif

    H  +=     cI;
    FH += F * cI;
#endif

    // (1 - F(theta)) * (1 + G(phi) + H(theta))
    Vec3f XYZ = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G) + H - FH;

    XYZ = ClampPositive3(XYZ);
    XYZ *= hk.mRadXYZ;

    return XYZToRGB(XYZ);
}

void SkyBRDF::FillBRDFTexture(int width, int height, uint8_t image[][4]) const
{
    VL_ASSERT(width == kTableSize);
    VL_ASSERT((height == 2 * kBRDFSamples) || (mHasHTerm && height >= 3 * kBRDFSamples));

    for (int j = 0; j < kBRDFSamples; j++)
    {
        for (int i = 0; i < width; i++)
        {
            Vec3f c = mBRDFThetaTable[j][i];

            if (mXYZ)
                c /= mMaxTheta;
            else
                c.z /= mMaxTheta;

            image[i][2] = ToU8(c.x);
            image[i][1] = ToU8(c.y);
            image[i][0] = ToU8(c.z);

            if (mHasHTerm)
                image[i][3] = ToU8(mBRDFThetaTableH[j][i]);
            else
                image[i][3] = 255;
        }

        image += width;
    }

    for (int j = 0; j < kBRDFSamples; j++)
    {
        for (int i = 0; i < width; i++)
        {
            Vec3f c = mBRDFGammaTable[j][i];

            if (mXYZ)
                c /= mMaxGamma;
            else
                c.z /= mMaxGamma;

            image[i][2] = ToU8(c.x);
            image[i][1] = ToU8(c.y);
            image[i][0] = ToU8(c.z);
            image[i][3] = 255;
        }

        image += width;
    }

    if (height < 3 * kBRDFSamples)
        return;

    // Want the H term too.
    for (int j = 0; j < kBRDFSamples; j++)
    {
        for (int i = 0; i < width; i++)
        {
            Vec3f c = mBRDFThetaTableFH[j][i];

            c /= mMaxTheta;

            image[i][2] = ToU8(c.x);
            image[i][1] = ToU8(c.y);
            image[i][0] = ToU8(c.z);
            image[i][3] = ToU8(mBRDFThetaTableH[j][i]);
        }

        image += width;
    }
}

void SkyBRDF::FillBRDFTexture(int width, int height, float image[][4]) const
{
    VL_ASSERT(width == kTableSize);
    VL_ASSERT((height == 2 * kBRDFSamples) || (mHasHTerm && height >= 3 * kBRDFSamples));

    for (int j = 0; j < kBRDFSamples; j++)
    {
        for (int i = 0; i < width; i++)
            *(Vec4f*) image[i] = Vec4f(mBRDFThetaTable[j][i], mHasHTerm ? mBRDFThetaTableH[j][i] : 1.0f);

        image += width;
    }

    for (int j = 0; j < kBRDFSamples; j++)
    {
        for (int i = 0; i < width; i++)
            *(Vec4f*) image[i] = Vec4f(mBRDFGammaTable[j][i], 1.0f);

        image += width;
    }

    if (height < 3 * kBRDFSamples)
        return;

    // Want the H term too.
    for (int j = 0; j < kBRDFSamples; j++)
    {
        for (int i = 0; i < width; i++)
            *(Vec4f*) image[i] = Vec4f(mBRDFThetaTableFH[j][i], mBRDFThetaTableH[j][i]);

        image += width;
    }
}


//------------------------------------------------------------------------------
// SunSky -- composite class for easier comparison
//------------------------------------------------------------------------------

SunSky::SunSky() :
    mSkyType(kPreetham),
    mToSun(vl_0),
    mTurbidity(2.5f),
    mAlbedo(vl_0),
    mOvercast(0.0f),
    mRoughness(0.0f),
    mZenithY(0.0f)
{
}

void SunSky::SetSkyType(tSkyType skyType)
{
    mSkyType = skyType;
}

tSkyType SunSky::SkyType() const
{
    return mSkyType;
}

void SunSky::SetSunDir(const Vec3f& v)
{
    mToSun = v;
}

void SunSky::SetTurbidity(float turbidity)
{
    mTurbidity = turbidity;
}

void SunSky::SetAlbedo(Vec3f rgb)
{
    mAlbedo = rgb;
}

void SunSky::SetOvercast(float overcast)
{
    mOvercast = overcast;
}

void SunSky::SetRoughness(float roughness)
{
    mRoughness = roughness;
}

void SunSky::Update()
{
    mZenithY = ZenithLuminance(acosf(mToSun.z), mTurbidity);

    mPreetham.Update(mToSun, mTurbidity, mOvercast);

    mHosek.mUseCubic = (kHosekCubic <= mSkyType && mSkyType <= kHosekCubicBRDF);
    mHosek.Update(mToSun, mTurbidity, mAlbedo, mOvercast);

    if (kPreethamTable <= mSkyType && mSkyType <= kPreethamBRDF)
        mTable.FindThetaGammaTables(mPreetham);
    else if (kHosekTable <= mSkyType && mSkyType <= kHosekBRDF)
        mTable.FindThetaGammaTables(mHosek);
    else if (kHosekCubicTable <= mSkyType && mSkyType <= kHosekCubicBRDF)
        mTable.FindThetaGammaTables(mHosek);

    if (mSkyType == kPreethamBRDF)
        mBRDF.FindBRDFTables(mTable, mPreetham);
    if (mSkyType == kHosekBRDF || mSkyType == kHosekCubicBRDF)
        mBRDF.FindBRDFTables(mTable, mHosek);
}

Vec3f SunSky::SkyRGB(const Vec3f& v) const
{
    switch (mSkyType)
    {
    case kPreetham:
        return mPreetham.SkyRGB(v);

    case kPreethamTable:
        return mTable.SkyRGB(mPreetham, v);
    case kPreethamBRDF:
        return mBRDF.ConvolvedSkyRGB(mPreetham, v, mRoughness);

    case kHosek:
    case kHosekCubic:
        return mHosek.SkyRGB(v);
    case kHosekTable:
    case kHosekCubicTable:
        return mTable.SkyRGB(mHosek, v);
    case kHosekBRDF:
    case kHosekCubicBRDF:
        return mBRDF.ConvolvedSkyRGB(mHosek, v, mRoughness);

    case kCIEClear:
        return Vec3f(CIEClearSkyLuminance       (v, mToSun, mZenithY));
    case kCIEOvercast:
        return Vec3f(CIEOvercastSkyLuminance    (v,         mZenithY));
    case kCIEPartlyCloudy:
        return Vec3f(CIEPartlyCloudySkyLuminance(v, mToSun, mZenithY));

    default:
        return vl_0;
    }
}

float SunSky::SkyLuminance(const Vec3f& v) const
{
    if (v.z < 0.0)
        return 0.0;

    switch (mSkyType)
    {
    case kPreetham:
        return mPreetham.SkyLuminance(v);
    case kCIEClear:
        return CIEClearSkyLuminance       (v, mToSun, mZenithY);
    case kCIEOvercast:
        return CIEOvercastSkyLuminance    (v,         mZenithY);
    case kCIEPartlyCloudy:
        return CIEPartlyCloudySkyLuminance(v, mToSun, mZenithY);
    case kHosek:
    case kHosekCubic:
        return mHosek.SkyLuminance(v);
    default:
        return 0;
    }
}

Vec2f SunSky::SkyChroma(const Vec3f& v) const
{
    if (v.z < 0.0)
        return Vec2f(0, 0);

    switch (mSkyType)
    {
    case kPreetham:
        return mPreetham.SkyChroma(v);
    case kCIEClear:
        return kClearChroma;
    case kCIEOvercast:
        return kOvercastChroma;
    case kCIEPartlyCloudy:
        return kPartlyCloudyChroma;
    case kHosek:
    case kHosekCubic:
        return XYZToChroma(mHosek.SkyXYZ(v));
    default:
        return kOvercastChroma;
    }
}

float SunSky::SkyAverageLuminance() const
{
    switch (mSkyType)
    {
    case kPreetham:
    case kPreethamTable:
    case kPreethamBRDF:
        return mPreetham.mPerezInvDen.z;

    case kCIEClear:
        return CIEClearSkyLuminance(vl_0, mToSun, mZenithY);
    case kCIEOvercast:
        return mZenithY;
    case kCIEPartlyCloudy:
        return CIEPartlyCloudySkyLuminance(Vec3f(0.0f, 0.0f, 0.0f), mToSun, mZenithY);

    case kHosek:
    case kHosekTable:
    case kHosekBRDF:
    case kHosekCubic:
    case kHosekCubicTable:
    case kHosekCubicBRDF:
        return mHosek.mRadXYZ.y;

    default:
        return 1.0f;
    }
}

Vec3f SunSky::SunRGB() const
{
    return ::SunRGB(mToSun.z, mTurbidity);
}

float SunSky::SunLuminance() const
{
    return ::SunLuminance(mToSun.z, mTurbidity);
}

Vec2f SunSky::SunChroma() const
{
    return ::SunChroma(mToSun.z, mTurbidity);
}

Vec3f SunSky::SunSkyRGB(const Vec3f& v, float overcast) const
{
    Vec3f result = SkyRGB(v);

    if (dot(v, mToSun) >= kSunCosAngle && overcast < 1.0f)
    {
        float sunWeight = 1.0f - sqr(overcast);
        result += ::SunRGB(mToSun.z, mTurbidity) * sunWeight;
    }

    return result;
}



================================================
FILE: SunSky.hpp
================================================
//
// SunSky.hpp
//
// Implements various sky models -- Preetham, Hosek, CIE* -- as
// well as a separable table-based model for fast BRDF convolutions
//
// Andrew Willmott but:
//   Preetham sun/sky based on code by Brian Smits,
//   Hosek code used L. Hosek & A. Wilkie code Version 1.4a as reference
//

#ifndef SUN_SKY_H
#define SUN_SKY_H

#include "VL234f.hpp"

namespace SSLib
{
    extern const float kSunDiameter;
    extern const float kSunDistance;
    extern const float kSunCosAngle;
    extern const float kSunSolidAngle;

    Vec3f SunDirection
    (
        float       timeOfDay,         // 24-hour, decimal: 0.0-23.99
        float       timeZone,          // relative to UTC: west -ve, east +ve
        int         julianDay,         // day of year: 1-365
        float       latitude,          // Degrees: north is positive
        float       longitude          // Degrees: east is positive
    );
    // Returns the local sun direction at the given time/location. +Y = north, +X = east, +Z = up.

    Vec2f SunriseAndSunset(float timeZone, int julianDay, float latitude, float longitude);
    // Returns sunrise and sunset times for the given day and location.

    // Utilities
    Vec3f SunRGB      (float cosTheta, float turbidity = 3.0f);  // Returns RGB for given sun elevation
    Vec2f SunChroma   (float cosTheta, float turbidity = 3.0f);  // Returns XYZ chroma for given sun elevation
    float SunLuminance(float cosTheta, float turbidity = 3.0f);  // Returns XYZ luminance for given sun elevation

    float ZenithLuminance(float thetaS, float T);           // Returns luminance estimate for given solar altitude and turbidity

    float CIEOvercastSkyLuminance    (const Vec3f& v, float Lz);                        // CIE standard overcast sky
    float CIEClearSkyLuminance       (const Vec3f& v, const Vec3f& toSun, float Lz);    // CIE standard clear sky
    float CIEPartlyCloudySkyLuminance(const Vec3f& v, const Vec3f& toSun, float Lz);    // CIE standard partly cloudy sky
    float CIEStandardSky   (int type, const Vec3f& v, const Vec3f& toSun, float Lz);    // Returns one of 15 standard skies: type = 0-14. See kCIEStandardSkyCoeffs


    //--------------------------------------------------------------------------
    // SkyPreetham
    //--------------------------------------------------------------------------

    class SkyPreetham
    {
    public:
        SkyPreetham();

        void        Update(const Vec3f& sun, float turbidity, float overcast = 0.0f, float hCrush = 0.0f); // update model with given settings

        Vec3f       SkyRGB      (const Vec3f& v) const;     // Returns luminance/chroma converted to RGB
        float       SkyLuminance(const Vec3f& v) const;     // Returns the luminance of the sky in direction v. v must be normalized. Luminance is in Nits = cd/m^2 = lumens/sr/m^2 */
        Vec2f       SkyChroma   (const Vec3f& v) const;     // Returns the chroma of the sky in direction v. v must be normalized.

        // Data
        Vec3f       mToSun;

        float       mPerez_x[5];
        float       mPerez_y[5];
        float       mPerez_Y[5];

        Vec3f       mZenith;
        Vec3f       mPerezInvDen;
    };


    //--------------------------------------------------------------------------
    // SkyHosek
    //--------------------------------------------------------------------------

    class SkyHosek
    {
    public:
        SkyHosek();

        void        Update(const Vec3f& sun, float turbidity, Vec3f albedo = vl_0, float overcast = 0.0f); // update model with given settings

        Vec3f       SkyXYZ      (const Vec3f& v) const;     // Returns CIE XYZ
        Vec3f       SkyRGB      (const Vec3f& v) const;     // Returns luminance/chroma converted to RGB
        float       SkyLuminance(const Vec3f& v) const;     // Returns CIE luminance

        // Data
        Vec3f       mToSun;
        float       mCoeffsXYZ[3][9];   // Hosek 9-term distribution coefficients
        Vec3f       mRadXYZ;            // Overall average radiance
        Vec3f       mAlbedo;            // Ground albedo
        bool        mUseCubic = false;  // Whether to use approximated cut-down Hosek
    };


    //--------------------------------------------------------------------------
    // SkyTable
    //--------------------------------------------------------------------------

    class SkyTable
    {
    public:
        // Table-based version - faster than per-sample Perez/Hosek function evaluation, suitable for shader use via 64 x 2 texture
        // For a fixed time, Preetham can be expressed in the form
        //   K (1 + F(theta))(1 + G(gamma))
        // where theta is the zenith angle of v, and gamma the angle between v and the sun direction.
        // Hosek can be expressed in the form
        //   K (1 + F(theta))(1 + G(gamma) + H(theta))
        // where H is trivial to evaluate in a shader, involving a constant term and sqrt(v.z).
        // Note: the F term is generally negative, so we use F' = -F in the tables.

        void        FindThetaGammaTables(const SkyPreetham& pt);
        void        FindThetaGammaTables(const SkyHosek& pt);

        Vec3f       SkyRGB(const SkyPreetham& pt, const Vec3f& v) const;  // Use precalculated table to return fast sky colour on CPU
        Vec3f       SkyRGB(const SkyHosek& hk,    const Vec3f& v) const;  // Use precalculated table to return fast sky colour on CPU

        void        FillTexture(int width, int height, uint8_t image[][4]) const;  // Fill kTableSize x 2 BGRA8 texture with tables
        void        FillTexture(int width, int height, float   image[][4]) const;  // Fill kTableSize x 2 RGBAF32 texture with tables

        // Table acceleration
        enum { kTableSize = 64, kHalfTableSize = kTableSize / 2 };
        Vec3f       mThetaTable[kTableSize];
        Vec3f       mGammaTable[kTableSize];
        float       mMaxTheta = 1.0f;       // To avoid clipping when using non-float textures. Currently only necessary if overcast is being used.
        float       mMaxGamma = 1.0f;       // To avoid clipping when using non-float textures.
        bool        mXYZ      = false;      // Whether tables are storing xyY (Preetham) or XYZ (Hosek)
    };


    //--------------------------------------------------------------------------
    // SkyBRDF
    //--------------------------------------------------------------------------

    // #define COMPACT_BRDF_TABLE   // enable for smaller half-size table

    class SkyBRDF
    {
    public:
        // Extended version of SkyTable that uses zonal harmonics to produce table rows
        // convolved with increasing cosine powers. This is an approximation, because
        // conv(AB) != conv(A) conv(B), but, because the Perez-form evaluation is
        // (1 + F(theta)) (1 + G(gamma)), the approximation is only for the small
        // order-2 FG term.
        void        FindBRDFTables(const SkyTable& table, const SkyPreetham& pt);
        void        FindBRDFTables(const SkyTable& table, const SkyHosek& hk);

        Vec3f       ConvolvedSkyRGB(const SkyPreetham& pt, const Vec3f& v, float roughness) const; // return sky term convolved with roughness, 1 = fully diffuse
        Vec3f       ConvolvedSkyRGB(const SkyHosek& pt,    const Vec3f& v, float roughness) const; // return sky term convolved with roughness, 1 = fully diffuse

        void        FillBRDFTexture(int width, int height, uint8_t image[][4]) const; // Fill kTableSize x (kBRDFSamples x 2|4) BGRA8 texture with tables
        void        FillBRDFTexture(int width, int height, float   image[][4]) const; // Fill kTableSize x (kBRDFSamples x 2|4) RGBAF32 texture with tables
                    // Note: for Hosek, the H term will be in the 'w' component of the theta section, and if a kBRDFSamples x 4 size texture is supplied,
                    // the two additional sections will contain the FH term table. (Using this improves accuracy but can be skipped.)

        enum { kTableSize = SkyTable::kTableSize, kHalfTableSize = SkyTable::kHalfTableSize };
    #ifdef COMPACT_BRDF_TABLE
        enum { kBRDFSamples = 4 };
    #else
        enum { kBRDFSamples = 8 };
    #endif
        Vec3f       mBRDFThetaTable[kBRDFSamples][kTableSize];
        Vec3f       mBRDFGammaTable[kBRDFSamples][kTableSize];

        // Additional tables for 'H' term in Hosek.
        float       mBRDFThetaTableH [kBRDFSamples][kTableSize];
        Vec3f       mBRDFThetaTableFH[kBRDFSamples][kTableSize];
        bool        mHasHTerm = false;

        float       mMaxTheta = 1.0f;       // To avoid clipping when using non-float textures. Currently only necessary if overcast is being used.
        float       mMaxGamma = 1.0f;       // To avoid clipping when using non-float textures.
        bool        mXYZ      = false;      // Whether tables are storing xyY (Preetham) or XYZ (Hosek)
    };


    //--------------------------------------------------------------------------
    // SunSky
    // Composite sun/sky model for easy comparisons
    //--------------------------------------------------------------------------

    enum tSkyType
    {
        kPreetham,
        kPreethamTable, // Table accelerated version, for sanity checking -- really designed for texture/shader use
        kPreethamBRDF,  // Sky model convolved with roughness (sat(cos^n) brdf)
        kHosek,
        kHosekTable,
        kHosekBRDF,
        kHosekCubic,
        kHosekCubicTable,
        kHosekCubicBRDF,
        kCIEClear,
        kCIEOvercast,
        kCIEPartlyCloudy,
        kNumSkyTypes
    };

    class SunSky
    {
    public:
        SunSky();

        void        SetSkyType(tSkyType skyType);
        tSkyType    SkyType() const;

        void        SetSunDir   (const Vec3f& sun);
        void        SetTurbidity(float turbidity);
        void        SetAlbedo   (Vec3f rgb);        // Set ground-bounce factor
        void        SetOvercast (float overcast);   // 0 = clear, 1 = completely overcast
        void        SetRoughness(float roughness);  // Set roughness for BRDF tables

        void        Update();                       // update model given above settings

        Vec3f       SkyRGB      (const Vec3f& v) const;  // Returns sky luminance/chroma converted to RGB
        float       SkyLuminance(const Vec3f& v) const;  // Returns the luminance of the sky in direction v. v must be normalized. Luminance is in Nits = cd/m^2 = lumens/sr/m^2 */
        Vec2f       SkyChroma   (const Vec3f& v) const;  // Returns the chroma of the sky in direction v. v must be normalized.
        float       SkyAverageLuminance() const;         // Average over sky

        Vec3f       SunRGB      () const;  // Returns direct sun RGB
        float       SunLuminance() const;  // Returns direct sun's luminance
        Vec2f       SunChroma   () const;  // Returns direct sun's chroma

        Vec3f       SunSkyRGB(const Vec3f& v, float overcast = 0.0f) const;  // Returns combined sun + sky RGB. But generally you're better off treating these as separate light sources.

    protected:
        // Data
        tSkyType    mSkyType;

        Vec3f       mToSun;
        float       mTurbidity;
        Vec3f       mAlbedo;
        float       mOvercast;
        float       mRoughness;

        // Various models
        float       mZenithY;   // for CIE functions

        SkyPreetham mPreetham;
        SkyHosek    mHosek;
        SkyTable    mTable;
        SkyBRDF     mBRDF;
    };
}

#endif


================================================
FILE: SunSkyTool.cpp
================================================
//
//  SunSkyTool.cpp
//
//  Test tool for SunSky.*
//
//  Andrew Willmott
//

#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES

#include "SunSky.hpp"

#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _MSC_VER
    #include <unistd.h>
    #include <strings.h>
#else
    #include <string.h>
    #define strcasecmp _stricmp
#endif

#include "stb_image_mini.h"

// #define EDGE_FILL

using namespace SSLib;

//------------------------------------------------------------------------------
// Utilities
//------------------------------------------------------------------------------

namespace
{
    // latitude, longitude
    const Vec2f kLondon       (+51.5f,  0.0f);
    const Vec2f kAuckland     (-37.0f,  174.8f);
    const Vec2f kPittsburgh   ( 40.5f, -80.22f);
    const Vec2f kOakland      ( 37.8f, -122.2f);
    const Vec2f kSanFrancisco ( 37.8f, -122.4f);
    const Vec2f kJakarta      (-6.21f,  106.85f);

    inline float saturate(float s)
    {
        if (s < 0.0f)
            return 0.0f;
        if (s >= 1.0f)
            return 1.0f;
        return s;
    }

    inline float Max(float a, float b)
    {
        return b < a ? a : b;
    }

    inline Vec3f MaxElts(const Vec3f& a, const Vec3f& b)
    {
        return Vec3f
        (
            Max(a.x, b.x),
            Max(a.y, b.y),
            Max(a.z, b.z)
        );
    }

    inline uint32_t RGBFToU32(Vec3f rgb)
    {
        return
              0xFF000000
            | (int) lrintf(saturate(rgb.x) * 255.0f) <<  0
            | (int) lrintf(saturate(rgb.y) * 255.0f) <<  8
            | (int) lrintf(saturate(rgb.z) * 255.0f) << 16;
    }

    bool ArgCountError(const char* opt, int expected, int argc)
    {
        if (argc < expected)
        {
            fprintf(stderr, "Not enough arguments for %s: expected %d, have %d\n", opt, expected, argc);
            return true;
        }

        return false;
    }

    inline int HemiInset(float y2, int width)
    {
        float maxX2 = 1.0f - y2;
        float maxX = sqrtf(maxX2);

        return (int) lrintf(ceil((1.0f - maxX) * width / 2.0f));
    }

    Vec3f pow(Vec3f v, float n)
    {
        return Vec3f(powf(v.x, n), powf(v.y, n), powf(v.z, n));
    }

    Vec3f toneMapLinear(Vec3f c, float weight)
    {
        return c * weight;
    }

    Vec3f toneMapExp(Vec3f c, float weight)
    {
        return Vec3f(vl_one) - Vec3f(exp(-weight * c.x), exp(-weight * c.y), exp(-weight * c.z));
    }

    Vec3f toneMapReinhard(Vec3f c, float weight)
    {
        c *= weight;
        return c / (Vec3f(vl_one) + c);
    }

    typedef Vec3f ToneMapFunc(Vec3f c, float weight);

    enum kToneMapType
    {
        kToneMapLinear,
        kToneMapExponential,
        kToneMapReinhard,
        kNumToneMapTypes
    };

    ToneMapFunc* kToneMapFuncs[kNumToneMapTypes + 1] =
    {
        toneMapLinear,
        toneMapExp,
        toneMapReinhard,
        nullptr
    };

    struct MapInfo
    {
        float weight    = 5e-5f;
        float gamma     = 2.2f;
        float hemiSign  = 1.0f;
        bool  fisheye   = false;

        ToneMapFunc* toneMap = toneMapLinear;
    };

    bool PFMWrite(const char* filename, int width, int height, Vec3f* image)
    {
        FILE* f = fopen(filename, "w");

        if (!f)
            return false;

        fprintf(f, "PF\n");
        fprintf(f, "%d %d\n", width, height);
        fprintf(f, "-1.0\n");   // -ve = little endian

        image += width * height;

        for (int i = 0; i < height; i++)
        {
            image -= width;
            fwrite(image, sizeof(float) * 3, width, f);
        }

        fclose(f);

        return true;
    }
}


//------------------------------------------------------------------------------
// Projected (or fisheye) hemisphere in LDR (png) and HDR (pfm)
//------------------------------------------------------------------------------

namespace
{
    /// Fill top-down projection of upper or lower hemisphere
    void SkyToHemisphere(const SunSky& sunSky, int width, int height, uint8_t* data, int stride, const MapInfo& mi)
    {
        float invGamma = 1.0;

        if (mi.gamma > 0.0)
            invGamma = 1.0f / mi.gamma;

        data += (height - 1) * stride;

        for (int i = 0; i < height; i++)
        {
            uint32_t* row = (uint32_t*) data;

            float y = 2.0f * (i + 0.5f) / height - 1.0f;
            float y2 = y * y;

            int sw = HemiInset(y2, width);

            for (int j = sw; j < width - sw; j++)
            {
                float x = 2.0f * (j + 0.5f) / width - 1.0f;
                float x2 = x * x;
                float h2 = x2 + y2;

                Vec3f v;

                if (mi.fisheye)
                {
                    float theta = vl_halfPi - vl_halfPi * sqrtf(h2);
                    float phi = atan2f(y, x);
                    v = Vec3f(cos(phi) * cos(theta), sin(phi) * cos(theta), sin(theta));
                }
                else
                    v = Vec3f(x, y, mi.hemiSign * sqrtf(1.0f - h2));

                Vec3f c = sunSky.SkyRGB(v);

                c = mi.toneMap(c, mi.weight);
                c = pow(c, invGamma);

                row[j] = RGBFToU32(c);
            }

            // fill in surrounds
        #ifdef EDGE_FILL
            for (int j = 0; j < sw; j++)
                row[j] = row[sw];
            for (int j = width - sw; j < width; j++)
                row[j] = row[width - sw - 1];
        #else
            for (int j = 0; j < sw; j++)
                row[j] = 0xFF000000;
            for (int j = width - sw; j < width; j++)
                row[j] = 0xFF000000;
        #endif

            data -= stride;
        }
    }

    struct cStats
    {
        Vec3f avg;
        Vec3f max;
        Vec3f dev;
    };

    void SkyToHemisphere(const SunSky& sunSky, int width, int height, Vec3f* data, const MapInfo& mi, cStats* stats)
    {
        Vec3f maxElts(vl_0);
        Vec3f sumElts(vl_0);
        Vec3f varElts(vl_0);
        int samples = 0;
        int stride = width;

        data += (height - 1) * stride;

        for (int i = 0; i < height; i++)
        {
            Vec3f* row = data;

            float y = 2.0f * (i + 0.5f) / height - 1.0f;
            float y2 = y * y;

            int sw = HemiInset(y2, width);

            for (int j = sw; j < width - sw; j++)
            {
                float x = 2.0f * (j + 0.5f) / width - 1.0f;
                float x2 = x * x;
                float h2 = x2 + y2;

                Vec3f v(x, y, mi.hemiSign * sqrtf(1.0f - h2));

                Vec3f c = sunSky.SkyRGB(v);

                if (stats)
                {
                    maxElts = MaxElts(maxElts, c);
                    sumElts += c;
                    varElts += c * c;
                    samples++;
                }

                c *= mi.weight;

                row[j] = c;
            }

        #ifdef EDGE_FILL
            // fill in surrounds by replicating edge texels
            for (int j = 0; j < sw; j++)
                row[j] = row[sw];
            for (int j = width - sw; j < width; j++)
                row[j] = row[width - sw - 1];
        #else
            // fill in surrounds by replicating edge texels
            for (int j = 0; j < sw; j++)
                row[j] = vl_0;
            for (int j = width - sw; j < width; j++)
                row[j] = vl_0;
        #endif

            data -= stride;
        }

        if (stats)
        {
            stats->avg = sumElts / float(samples);
            stats->max = maxElts;
            varElts = varElts / float(samples) - sqr(stats->avg);
            stats->dev = Vec3f(sqrtf(varElts.x), sqrtf(varElts.y), sqrtf(varElts.z));
        }
    }
}


//------------------------------------------------------------------------------
// Cubemap generation in LDR (png) and HDR (pfm)
//------------------------------------------------------------------------------

namespace
{
    const int kFaceIndices[6][3] =
    {
        { 0, 2, 1 },
        { 2, 0, 1 },
        { 0, 2, 1 },
        { 2, 0, 1 },
        { 0, 1, 2 },
        { 0, 1, 2 },
    };
    const float kFaceSigns[6][3] =
    {
        { +1.0f, +1.0f, +1.0f },
        { +1.0f, -1.0f, +1.0f },
        { -1.0f, -1.0f, +1.0f },
        { -1.0f, +1.0f, +1.0f },
        { +1.0f, -1.0f, +1.0f },
        { +1.0f, +1.0f, -1.0f },
    };

    void SkyToCubeFace(const SunSky& sunSky, int face, int width, int height, uint8_t* data, int stride, const MapInfo& mi)
    {
        float invGamma = 1.0;

        if (mi.gamma > 0.0)
            invGamma = 1.0f / mi.gamma;

        const float* signs   = kFaceSigns  [face];
        const int*   indices = kFaceIndices[face];

        data += (height - 1) * stride;

        for (int i = 0; i < height; i++)
        {
            uint32_t* row = (uint32_t*) data;

            for (int j = 0; j < width; j++)
            {
                Vec3f facePos(2 * (j + 0.5f) / width - 1, 2 * (i + 0.5f) / height - 1, 1.0f);

                Vec3f faceDir
                (
                    signs[0] * facePos[indices[0]],
                    signs[1] * facePos[indices[1]],
                    signs[2] * facePos[indices[2]]
                );

                faceDir = norm(faceDir);
                Vec3f faceColour = sunSky.SkyRGB(faceDir);

                faceColour = mi.toneMap(faceColour, mi.weight);
                faceColour = pow(faceColour, invGamma);

                row[j] = RGBFToU32(faceColour);
            }

            data -= stride;
        }
    }

    void SkyToCubeFace(const SunSky& sunSky, int face, int width, int height, Vec3f* data, const MapInfo& mi)
    {
        const float* signs   = kFaceSigns  [face];
        const int*   indices = kFaceIndices[face];

        int stride = width;
        data += (height - 1) * stride;

        for (int i = 0; i < height; i++)
        {
            Vec3f* row = data;

            for (int j = 0; j < width; j++)
            {
                Vec3f facePos(2 * (j + 0.5f) / width - 1, 2 * (i + 0.5f) / height - 1, 1.0f);

                Vec3f faceDir
                (
                    signs[0] * facePos[indices[0]],
                    signs[1] * facePos[indices[1]],
                    signs[2] * facePos[indices[2]]
                );

                faceDir = norm(faceDir);

                row[j] = sunSky.SkyRGB(faceDir) * mi.weight;
            }

            data -= stride;
        }
    }
}


//------------------------------------------------------------------------------
// Panorama generation in LDR (png) and HDR (pfm)
//------------------------------------------------------------------------------

namespace
{
    void SkyToPanoramic(const SunSky& sunSky, int height, uint8_t* data, int stride, const MapInfo& mi)
    {
        float invGamma = 1.0;

        if (mi.gamma > 0.0)
            invGamma = 1.0f / mi.gamma;

        int width = 2 * height;
        if (stride == 0)
            stride = 4 * width;

        float da = vl_pi / height;
        float phi = vl_pi - 0.5f * da;

        data += (height - 1) * stride;

        for (int i = 0; i < height; i++)
        {
            uint32_t* row = (uint32_t*) data;

            float theta = 0.5f * da;
            float sp = sinf(phi);
            float cp = cosf(phi);

            for (int j = 0; j < width; j++)
            {
                float st = sinf(theta);
                float ct = cosf(theta);

                // middle of image is north, east to right, west to left, edges are south
                Vec3f dir(-st * sp, -ct * sp, cp);

                Vec3f c = sunSky.SkyRGB(dir);

                c = mi.toneMap(c, mi.weight);
                c = pow(c, invGamma);

                row[j] = RGBFToU32(c);
                theta += da;
            }

            data -= stride;
            phi -= da;
        }
    }

    void SkyToPanoramic(const SunSky& sunSky, int height, Vec3f* data, const MapInfo& mi)
    {
        int width = 2 * height;
        int stride = width;

        float da = vl_pi / height;
        float phi = vl_pi - 0.5f * da;

        data += (height - 1) * stride;

        for (int i = 0; i < height; i++)
        {
            Vec3f* row = data;

            float theta = 0.5f * da;
            float sp = sinf(phi);
            float cp = cosf(phi);

            for (int j = 0; j < width; j++)
            {
                float st = sinf(theta);
                float ct = cosf(theta);

                // middle of image is north, east to right, west to left, edges are south
                Vec3f dir(-st * sp, -ct * sp, cp);

                row[j] = sunSky.SkyRGB(dir) * mi.weight;
                theta += da;
            }

            data -= stride;
            phi -= da;
        }
    }

    const float kMinAutoLum    = 2000.0f;
    const float kAutoLumTarget = 0.4f;
}



//------------------------------------------------------------------------------
// Main program
//------------------------------------------------------------------------------

namespace
{
    struct EnumInfo
    {
        const char* mName;
        const char* mShort;
        int         mValue;
    };

    EnumInfo kSkyTypeEnum[] =
    {
        { "Preetham",         "pt",   kPreetham        },
        { "PreethamTable",    "ptt",  kPreethamTable   },
        { "PreethamBRDF",     "ptb",  kPreethamBRDF    },
        { "Hosek",            "hk",   kHosek           },
        { "HosekTable",       "hkt",  kHosekTable      },
        { "HosekBRDF",        "hkb",  kHosekBRDF       },
        { "HosekCubic",       "hc",   kHosekCubic      },
        { "HosekCubicTable",  "hct",  kHosekCubicTable },
        { "HosekCubicBRDF",   "hcb",  kHosekCubicBRDF  },
        { "cieClear",         "cc",   kCIEClear        },
        { "cieOvercast",      "co",   kCIEOvercast     },
        { "ciePartlyCloudy",  "cp",   kCIEPartlyCloudy },
        { nullptr, nullptr, 0 }
    };

    EnumInfo kToneMapTypeEnum[] =
    {
        { "linear",       "l",   kToneMapLinear      },
        { "exponential",  "ex",  kToneMapExponential },
        { "reinhard",     "rh",  kToneMapReinhard    },
        { nullptr, nullptr, 0 }
    };

    int ArgEnum(const EnumInfo info[], const char* name, int defaultValue = -1)
    {
        for ( ; info->mName; info++)
            if (strcasecmp(info->mName, name) == 0 || strcasecmp(info->mShort, name) == 0)
            {
                return info->mValue;
                break;
            }

        return defaultValue;
    }

    int Help(const char* command)
    {
        printf
        (
            "%s <options>\n"
            "\n"
            "Options:\n"
            "  -h : this help\n"
            "  -s <skyType> : use given sky type (default: Preetham.)\n"
            "  -t <time>          : 0 - 24\n"
            "  -d <day of year>   : 0 - 365\n"
            "  -b <turbidity>     : 2 - 12\n"
            "  -x <l>|<r g b>     : 0 - 1, specify ground albedo for Hosek\n"
            "  -r <roughness>     : 0 - 1, specify roughness for BRDF types\n"
            "  -l <latitude> <longitude>\n"
            "  -w <luminance scale>\n"
            "  -g <gamma>\n"
            "  -e <tonemapType> : use given tonemap operator (default: linear)\n"
            "  -a : autoscale intensity\n"
            "  -i : invert hemisphere\n"
            "  -f : fisheye rather than cos projection\n"
            "  -c : output cubemap instead\n"
            "  -p : output panorama instead\n"
            "  -m : output movie, record day as sky.mp4, requires ffmpeg\n"
            "  -v : verbose\n"
            , command
        );

        printf("\n" "skyType:\n");
        for (const EnumInfo* info = kSkyTypeEnum; info->mName; info++)
            printf("  %-16s (%s)\n", info->mName, info->mShort);

        printf("\n" "toneMapType:\n");
        for (const EnumInfo* info = kToneMapTypeEnum; info->mName; info++)
            printf("  %-16s (%s)\n", info->mName, info->mShort);

        return 0;
    }
}

int main(int argc, const char* argv[])
{
    const char* command = argv[0];
    argv++; argc--; // chomp

    if (argc == 0)
        return Help(command);

    // Set up defaults
    time_t unixTime;
    ::time(&unixTime);

    struct tm* theTime = localtime(&unixTime);

    int julianDay = theTime->tm_yday;

    float localTime = theTime->tm_hour + theTime->tm_min / 60.0f + theTime->tm_sec / 3600.0f;
    bool dst = (theTime->tm_isdst != 0);
    Vec2f latLong   = kLondon;
    float turbidity = 2.5;
    Vec3f albedo    = Vec3f(0.3f);
    float overcast  = 0.0f;

    MapInfo mi;
    mi.weight = -1.0f;

    float roughness = -1.0f;
    bool autoscale  = false;
    bool cubeMap    = false;
    bool panoramic  = false;
    bool movie      = false;
    bool verbose    = false;
    tSkyType skyType = kPreetham;

    // Options
    while (argc > 0 && argv[0][0] == '-')
    {
        const char* option = argv[0] + 1;
        argv++; argc--;

        switch (option[0])
        {
        case 'h':
        case '?':
            return Help(command);

        case 't':
            if (ArgCountError(option, 1, argc))
                return -1;
            localTime = (float) atof(argv[0]);
            argv++; argc--;
            break;

        case 'd':
            if (ArgCountError(option, 1, argc))
                return -1;
            julianDay = atoi(argv[0]);
            argv++; argc--;
            break;

        case 'b':
            if (ArgCountError(option, 1, argc))
                return -1;
            turbidity = (float) atof(argv[0]);
            argv++; argc--;
            break;

        case 'o':
            overcast = (float) atof(argv[0]);
            argv++; argc--;
            break;

        case 'x':
            if (ArgCountError(option, 1, argc))
                return -1;
            albedo.x = (float) atof(argv[0]);
            argv++; argc--;

            if (argc >= 1 && argv[0][0] != '-')
            {
                albedo.y = (float) atof(argv[0]);
                argv++; argc--;
            }
            else
                albedo.y = albedo.x;

            if (argc >= 1 && argv[0][0] != '-')
            {
                albedo.z = (float) atof(argv[0]);
                argv++; argc--;
            }
            else
                albedo.z = albedo.y;

            break;

        case 'l':
            if (ArgCountError(option, 2, argc))
                return -1;
            latLong[0] = (float) atof(argv[0]);
            argv++; argc--;
            latLong[1] = (float) atof(argv[0]);
            argv++; argc--;

            dst = false;    // don't take dst from local time info
            break;

        case 'w':
            if (ArgCountError(option, 1, argc))
                return -1;
            mi.weight = (float) atof(argv[0]);
            argv++; argc--;

            break;

        case 'g':
            if (ArgCountError(option, 1, argc))
                return -1;
            mi.gamma = (float) atof(argv[0]);
            argv++; argc--;
            break;

        case 'a':
            autoscale = !autoscale;
            break;
        case 'c':
            cubeMap = !cubeMap;
            break;
        case 'p':
            panoramic = !panoramic;
            break;
        case 'm':
            movie = !movie;
            break;
        case 'i':
            mi.hemiSign = -mi.hemiSign;
            break;
        case 'f':
            mi.fisheye = !mi.fisheye;
            break;
        case 'v':
            verbose = !verbose;
            break;

        case 's':
            {
                if (ArgCountError(option, 1, argc))
                    return -1;

                const char* typeName = argv[0];
                argv++; argc--;

                skyType = (tSkyType) ArgEnum(kSkyTypeEnum, typeName, kNumSkyTypes);

                if (skyType == kNumSkyTypes)
                {
                    fprintf(stderr, "Unknown sky type: %s\n", typeName);
                    return -1;
                }
            }
            break;

        case 'e':
            {
                if (ArgCountError(option, 1, argc))
                    return -1;

                const char* typeName = argv[0];
                argv++; argc--;

                mi.toneMap = kToneMapFuncs[ArgEnum(kToneMapTypeEnum, typeName, kNumToneMapTypes)];

                if (!mi.toneMap)
                {
                    fprintf(stderr, "Unknown tone map type: %s\n", typeName);
                    return -1;
                }
            }
            break;

        case 'r':
            if (ArgCountError(option, 1, argc))
                return -1;
            roughness = saturate((float) atof(argv[0]));
            argv++; argc--;
            break;


        default:
            fprintf(stderr, "Unrecognised option: %s\n", option);
            return -1;
        }
    }

    if (argc > 0)
    {
        fprintf(stderr, "Unrecognised arguments starting with %s\n", argv[0]);
        return -1;
    }

    float timeZone = rintf(latLong[1] / 15.0f);    // estimate for now

    if (dst)
        timeZone += 1.0;

    Vec3f sunDir = SunDirection(localTime, timeZone, julianDay, latLong[0], latLong[1]);

    SunSky sunSky;
    sunSky.SetSkyType(skyType);

    sunSky.SetSunDir(sunDir);
    sunSky.SetTurbidity(turbidity);
    sunSky.SetAlbedo(albedo);
    sunSky.SetOvercast(overcast);

    if (roughness >= 0.0f)
        sunSky.SetRoughness(roughness);

    sunSky.Update();

    if (verbose)
    {
        printf("Time: %g, time zone: %g, day: %d, latitude: %g, longitude: %g, turbidity: %g, albedo: %g\n", localTime, timeZone, julianDay, latLong[0], latLong[1], turbidity, albedo.y);

        float theta = asinf (sunDir.z);
        float phi   = atan2f(sunDir.y, sunDir.x);

        theta *= 180.0f / vl_pi;
        phi   *= 180.0f / vl_pi;

        phi   = 90.0f - phi;    // make relative to North rather than East, and clockwise.

        if (phi < 0.0f)
            phi += 360.0f;

        printf("Sun elevation      : %g\n", theta);
        printf("Sun compass heading: %g\n", phi  );
    }

    if (mi.weight < 0.0f)
    {
        if (kHosek <= skyType && skyType <= kHosekCubicBRDF)
            mi.weight = 8e-5f;
        else
            mi.weight = 5e-5f;
    }

    if (!movie && autoscale)
    {
        float avgLum = sunSky.SkyAverageLuminance();

        if (verbose)
            printf("Average luminance: %g\n", avgLum);

        // Once we get dark enough (sun below horizon), stop auto-scaling, so we don't snap to black
        if (avgLum < kMinAutoLum)
            avgLum = kMinAutoLum;

        float lumScale = kAutoLumTarget / avgLum;

        if (verbose)
            printf("Autoscaling luminance by: %g\n", lumScale);

        mi.weight = lumScale;
    }

    if (verbose)
        printf("Ouput: weight = %g, gamma = %g\n", mi.weight, mi.gamma);

    char fileName[32];

    if (panoramic)
    {
        uint32_t image   [256][512];
        Vec3f    imageHDR[256][512];

        SkyToPanoramic(sunSky, 256, (uint8_t*) image, 0, mi);

        snprintf(fileName, 32, "sky-panoramic.png");

        if (stbi_write_png(fileName, 512, 256, 4, image[0], 0) != 0)
            printf("wrote %s\n", fileName);
        else
            printf("failed to write %s\n", fileName);

        SkyToPanoramic(sunSky, 256, imageHDR[0], mi);

        snprintf(fileName, 32, "sky-panoramic.pfm");

        if (PFMWrite(fileName, 512, 256, imageHDR[0]))
            printf("wrote %s\n", fileName);
        else
            printf("failed to write %s\n", fileName);
    }
    else if (cubeMap)
    {
        uint32_t image   [256][256];
        Vec3f    imageHDR[256][256];

        for (int i = 0; i < 6; i++)
        {
            SkyToCubeFace(sunSky, i, 256, 256, (uint8_t*) image, 4 * 256, mi);

            snprintf(fileName, 32, "sky-cube-%d.png", i);

            if (stbi_write_png(fileName, 256, 256, 4, image[0], 0) != 0)
                printf("wrote %s\n", fileName);
            else
                printf("failed to write %s\n", fileName);

            SkyToCubeFace(sunSky, i, 256, 256, imageHDR[0], mi);

            snprintf(fileName, 32, "sky-cube-%d.pfm", i);

            if (PFMWrite(fileName, 256, 256, imageHDR[0]))
                printf("wrote %s\n", fileName);
            else
                printf("failed to write %s\n", fileName);
        }
    }
#ifndef _MSC_VER
    else if (movie)
    {
        uint32_t image[256][256];

        // crf = constant rate factor, 0 - 51, 0 is lossless, 51 worst
        // -preset = veryfast/faster/fast/medium/slow/slower/veryslow
        const char* cmd = "ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s 256x256 -i - -threads 0 -preset medium -y -pix_fmt yuv420p -crf 10 sky.mp4";

        // open pipe to ffmpeg's stdin in binary write mode
        FILE* ffmpeg = popen(cmd, "w");

        if (!ffmpeg)
        {
            perror("cmd");
            return -1;
        }

        for (float time = 6.0f; time <= 21.0f; time += 0.1f)
        {
            sunSky.SetSunDir(SunDirection(time, timeZone, julianDay, latLong[0], latLong[1]));
            sunSky.Update();

            if (autoscale)
            {
                float avgLum = sunSky.SkyAverageLuminance();

                if (verbose)
                    printf("Average luminance: %g\n", avgLum);

                // Once we get dark enough (sun below horizon), stop auto-scaling, so we don't snap to black
                if (avgLum < kMinAutoLum)
                    avgLum = kMinAutoLum;

                float lumScale = kAutoLumTarget / avgLum;

                if (verbose)
                    printf("Autoscaling luminance by: %g\n", lumScale);

                mi.weight = lumScale;
            }

            SkyToHemisphere(sunSky, 256, 256, (uint8_t*) image, 1024, mi);

            fwrite(image, sizeof(image), 1, ffmpeg);
        }

        if (pclose(ffmpeg) == 0)
            printf("wrote sky.mp4\n");
        else
            printf("failed to write sky.mp4\n");
    }
#endif
    else
    {
        uint32_t image   [256][256];
        Vec3f    imageHDR[256][256];

        SkyToHemisphere(sunSky, 256, 256, (uint8_t*) image, 1024, mi);

        snprintf(fileName, 32, "sky-hemi.png");

        if (stbi_write_png(fileName, 256, 256, 4, image[0], 0) != 0)
            printf("wrote %s\n", fileName);
        else
            printf("failed to write %s\n", fileName);

        cStats stats;
        SkyToHemisphere(sunSky, 256, 256, imageHDR[0], mi, verbose ? &stats : nullptr);

        snprintf(fileName, 32, "sky-hemi.pfm");

        if (PFMWrite(fileName, 256, 256, imageHDR[0]))
            printf("wrote %s\n", fileName);
        else
            printf("failed to write %s\n", fileName);

        if (verbose)
        {
            printf("avg: %8.2f, %8.2f, %8.2f\n", stats.avg.x, stats.avg.y, stats.avg.z);
            printf("max: %8.2f, %8.2f, %8.2f\n", stats.max.x, stats.max.y, stats.max.z);
            printf("dev: %8.2f, %8.2f, %8.2f\n", stats.dev.x, stats.dev.y, stats.dev.z);
        }
    }

    return 0;
}


================================================
FILE: VL234f.hpp
================================================
//
// VL234f.hpp
//
// Andrew Willmott
//

#ifndef VL234f_H
#define VL234f_H

#include <cmath>
// Default definitions

#ifdef VL_NS
    #define VL_NS_BEGIN namespace VL_NS {
    #define VL_NS_END   }
#else
    #define VL_NS_BEGIN
    #define VL_NS_END
#endif

#define VL_REPEAT 0x7FFFFFFF

#define VL_PREFIX_(PREFIX, NAME) PREFIX ## _ ## NAME
#define VL_PREFIX(PREFIX, NAME) VL_PREFIX_(PREFIX, NAME)

// Assertions

#ifndef VL_ASSERT_FULL
    #ifdef VL_DEBUG
        #include <assert.h>
        #define VL_ASSERT_FULL(M_TYPE, M_B, ...) assert(M_B)
    #else
        #define VL_ASSERT_FULL(M_TYPE, M_B, ...)
    #endif
    #define VL_EXPECT_FULL(M_TYPE, M_B, ...) (void)(M_B)
#endif

#define VL_ASSERT_MSG(M_B, ...) VL_ASSERT_FULL("Assert Error", M_B, __VA_ARGS__)
#define VL_EXPECT_MSG(M_B, ...) VL_EXPECT_FULL("Warning", M_B, __VA_ARGS__)
#define VL_INDEX_MSG(M_I, M_N, ...) VL_ASSERT_FULL("Index Error", (unsigned int)(M_I) < (unsigned int)(M_N), __VA_ARGS__)
#define VL_RANGE_MSG(M_I, M_MIN, M_MAX, ...) VL_ASSERT_FULL("Range Error", ((M_MIN) <= (M_I) && (M_I) < (M_MAX)), __VA_ARGS__)
#define VL_ERROR(...) VL_ASSERT_FULL("Error", false, __VA_ARGS__)
#define VL_WARNING(...) VL_EXPECT_FULL("Warning", false, __VA_ARGS__)

#define VL_ASSERT(M_B) VL_ASSERT_MSG(M_B, #M_B)
#define VL_EXPECT(M_B) VL_EXPECT_MSG(M_B, #M_B)
#define VL_INDEX(M_I, M_N) VL_INDEX_MSG(M_I, M_N, "0 <= " #M_I " < " #M_N)
#define VL_RANGE(M_I, M_MIN, M_MAX) VL_RANGE_MSG(M_I, M_MIN, M_MAX, #M_MIN " <= " #M_I " < " #M_MAX)

// Memory

#ifndef VL_NEW
    #define VL_NEW new
    #define VL_DELETE delete
#endif

// Misc

#ifndef VL_CXX_11
    #define VL_CXX_11 __cplusplus >= 201103L
#endif

#ifndef VL_CONSTANTS_H
#define VL_CONSTANTS_H

// --- Mathematical constants -------------------------------------------------

enum    VLDiag       { vl_I = 1, vl_minus_I = -1, vl_nI = -1 };
enum    VLBlock      { vl_zero = 0, vl_one = 1, vl_minus_one = -1, vl_0 = 0, vl_1 = 1, vl_n1 = -1 };
enum    VLAxis       { vl_x, vl_y, vl_z, vl_w };
enum    VLMinusAxis  { vl_minus_x, vl_minus_y, vl_minus_z, vl_minus_w, vl_nx = 0, vl_ny, vl_nz, vl_nw };

typedef VLAxis      vl_axis;        // e.g., Vecf(10, vl_axis(4)), Vec3f(vl_axis(i))
typedef VLMinusAxis vl_minus_axis;  // e.g., Vecf(10, vl_minus_axis(4))

const double vl_pi           = 3.14159265358979323846264338327950288;  // prefer vlf_pi, vld_pi etc.
const double vl_halfPi       = vl_pi / 2.0;
const double vl_quarterPi    = vl_pi / 4.0;
const double vl_twoPi        = vl_pi * 2.0;

#ifdef HUGE_VAL
    const float  vlf_huge = HUGE_VALF;
    const double vld_huge = HUGE_VAL;
#else
    const float  vlf_huge = 1e50f;
    const double vld_huge = 1e500;
#endif

const double vl_huge = vld_huge;

const float  vlf_eps = 1.19209290E-07F;
const double vld_eps = 2.2204460492503131e-016;

struct VLVecType { typedef void IsVec; typedef float Elt; };
struct VLMatType { typedef void IsMat; typedef float Elt; };
struct VLVolType { typedef void IsVol; typedef float Elt; };

#define VL_PREFIX_(PREFIX, NAME) PREFIX ## _ ## NAME
#define VL_PREFIX(PREFIX, NAME) VL_PREFIX_(PREFIX, NAME)
#define VL_CS(NAME) VL_PREFIX(vlf, NAME)

#endif

#ifndef VL_NO_REAL
const float VL_CS(pi)        = float(3.14159265358979323846264338327950288);
const float VL_CS(halfPi)    = float(VL_CS(pi) / 2);
const float VL_CS(quarterPi) = float(VL_CS(pi) / 4);
const float VL_CS(twoPi)     = floa
Download .txt
gitextract_6nm16y2y/

├── .gitignore
├── HosekCubic.h
├── HosekDataXYZ.h
├── LICENSE.md
├── Makefile
├── README.md
├── SunSky.cpp
├── SunSky.hpp
├── SunSkyTool.cpp
├── VL234f.hpp
├── gen-images.sh
├── sky.sh
├── skybox_fs.sc
└── stb_image_mini.h
Download .txt
SYMBOL INDEX (484 symbols across 5 files)

FILE: SunSky.cpp
  function Vec3f (line 51) | inline Vec3f xyYToXYZ(const Vec3f& c)
  function Vec3f (line 56) | inline Vec3f xyYToRGB(const Vec3f& xyY)
  function Vec3f (line 68) | inline Vec3f XYZToRGB(const Vec3f& XYZ)
  function Vec3f (line 78) | inline Vec3f RGBToXYZ(const Vec3f& rgb)
  function RGBToLuminance (line 88) | inline float RGBToLuminance(const Vec3f& rgb)
  function Vec2f (line 93) | inline Vec2f XYZToChroma(Vec3f XYZ)
  function ToU8 (line 101) | inline uint8_t ToU8(float f)
  function DegreesToRadians (line 111) | inline float DegreesToRadians(float d)
  function ClampUnit (line 116) | inline float ClampUnit(float s)
  function ClampPositive (line 125) | inline float ClampPositive(float s)
  function Vec3f (line 131) | inline Vec3f ClampPositive3(Vec3f v)
  function Vec3f (line 137) | inline Vec3f ClampUnit(Vec3f v)
  function FloorToSInt32 (line 146) | inline int32_t FloorToSInt32(float s) // force int 'fast' version of this
  function LerpClamp (line 158) | inline float LerpClamp(float s)
  function T_V (line 165) | T_V LerpSample(float s, int n, const T_V c[])
  function T_V (line 181) | T_V BiLerpSample(float s, float t, int w, int h, const T_V c[])
  function Vec3f (line 210) | Vec3f SSLib::SunDirection(float timeOfDay, float timeZone, int julianDay...
  function Vec2f (line 230) | Vec2f SSLib::SunriseAndSunset(float timeZone, int julianDay, float latit...
  function Vec3f (line 285) | Vec3f SSLib::SunRGB(float cosTheta, float turbidity)
  function Vec2f (line 301) | Vec2f SSLib::SunChroma(float cosTheta, float turbidity)
  function CIELumRatio (line 391) | inline float CIELumRatio(const Vec3f& v, const Vec3f& toSun, float a, fl...
  function PerezUpper (line 450) | inline float PerezUpper(const float* lambdas, float cosTheta, float gamm...
  function PerezLower (line 456) | inline float PerezLower(const float* lambdas, float cosThetaS, float the...
  function Vec3f (line 578) | Vec3f SkyPreetham::SkyRGB(const Vec3f& v) const
  function Vec2f (line 611) | Vec2f SkyPreetham::SkyChroma(const Vec3f& v) const
  function EvalQuintic (line 634) | inline float EvalQuintic(const float w[6], const float data[6])
  function EvalQuintic (line 644) | inline void EvalQuintic(const float w[6], const float data[6][9], float ...
  function FindQuinticWeights (line 657) | inline void FindQuinticWeights(float s, float w[6])
  function FindHosekCoeffs (line 680) | float FindHosekCoeffs
  function Vec4f (line 736) | inline Vec4f CubicWeights(float s)
  function FindHosekCoeffs (line 749) | float FindHosekCoeffs
  function EvalHosekCoeffs (line 808) | float EvalHosekCoeffs(const float coeffs[9], float cosTheta, float gamma...
  function Vec3f (line 965) | Vec3f SkyHosek::SkyXYZ(const Vec3f& v) const
  function Vec3f (line 986) | Vec3f SkyHosek::SkyRGB(const Vec3f& v) const
  function MapTheta (line 1002) | inline float MapTheta(float t)
  function UnmapTheta (line 1006) | inline float UnmapTheta(float t)
  function UnmapThetaWeight (line 1010) | inline float UnmapThetaWeight(float t)
  function MapTheta (line 1015) | inline float MapTheta(float t)
  function UnmapTheta (line 1019) | inline float UnmapTheta(float t)
  function UnmapThetaWeight (line 1023) | inline float UnmapThetaWeight(float t)
  function MapGamma (line 1030) | inline float MapGamma(float g)
  function UnmapGamma (line 1034) | inline float UnmapGamma(float g)
  function UnmapGammaWeight (line 1038) | inline float UnmapGammaWeight(float g)
  function Vec3f (line 1194) | Vec3f SkyTable::SkyRGB(const SkyPreetham& pt, const Vec3f& v) const
  function Vec3f (line 1220) | Vec3f SkyTable::SkyRGB(const SkyHosek& hk, const Vec3f& v) const
  function CalcCosPowerSatZH7 (line 1331) | void CalcCosPowerSatZH7(float n, float zcoeffs[7])
  function CalcZH7Weights (line 1354) | void CalcZH7Weights(float z, float w[7])
  function WindowScale (line 1371) | inline float WindowScale(int n, float gamma)
  function T (line 1378) | T SampleZH7(float z, const T zhCoeffs[7])
  function AddZH7Sample (line 1391) | void AddZH7Sample(float z, T c, T zhCoeffs[7])
  function ApplyZH7Windowing (line 1400) | void ApplyZH7Windowing(float gamma, T coeffs[7])
  function ConvolveZH7WithZH7Norm (line 1407) | void ConvolveZH7WithZH7Norm(const float brdfCoeffs[7], const T zhCoeffsI...
  function FindZH7FromThetaTable (line 1419) | void FindZH7FromThetaTable(int tableSize, const T table[], T zhCoeffs[7])
  function GenerateThetaTableFromZH7 (line 1438) | void GenerateThetaTableFromZH7(const T zhCoeffs[7], int tableSize, T tab...
  function FindZH7FromGammaTable (line 1451) | void FindZH7FromGammaTable(int tableSize, const T table[], T zhCoeffs[7])
  function GenerateGammaTableFromZH7 (line 1471) | void GenerateGammaTableFromZH7(T zhCoeffs[7], int tableSize, T table[])
  function Vec3f (line 1483) | inline Vec3f Bias_xyY(Vec3f c)  // effectively make delta lum proportion...
  function Vec3f (line 1491) | inline Vec3f Unbias_xyY(Vec3f c)  // Return to delta xyY form
  function RowPower (line 1510) | constexpr float RowPower(float i, float n)
  function Vec3f (line 1741) | Vec3f SkyBRDF::ConvolvedSkyRGB(const SkyPreetham& pt, const Vec3f& v, fl...
  function Vec3f (line 1767) | Vec3f SkyBRDF::ConvolvedSkyRGB(const SkyHosek& hk, const Vec3f& v, float...
  function tSkyType (line 1955) | tSkyType SunSky::SkyType() const
  function Vec3f (line 2007) | Vec3f SunSky::SkyRGB(const Vec3f& v) const
  function Vec2f (line 2064) | Vec2f SunSky::SkyChroma(const Vec3f& v) const
  function Vec3f (line 2116) | Vec3f SunSky::SunRGB() const
  function Vec2f (line 2126) | Vec2f SunSky::SunChroma() const
  function Vec3f (line 2131) | Vec3f SunSky::SunSkyRGB(const Vec3f& v, float overcast) const

FILE: SunSky.hpp
  type SSLib (line 17) | namespace SSLib
    class SkyPreetham (line 54) | class SkyPreetham
    class SkyHosek (line 81) | class SkyHosek
    class SkyTable (line 105) | class SkyTable
    class SkyBRDF (line 142) | class SkyBRDF
    type tSkyType (line 186) | enum tSkyType
    class SunSky (line 203) | class SunSky

FILE: SunSkyTool.cpp
  function saturate (line 46) | inline float saturate(float s)
  function Max (line 55) | inline float Max(float a, float b)
  function Vec3f (line 60) | inline Vec3f MaxElts(const Vec3f& a, const Vec3f& b)
  function RGBFToU32 (line 70) | inline uint32_t RGBFToU32(Vec3f rgb)
  function ArgCountError (line 79) | bool ArgCountError(const char* opt, int expected, int argc)
  function HemiInset (line 90) | inline int HemiInset(float y2, int width)
  function Vec3f (line 98) | Vec3f pow(Vec3f v, float n)
  function Vec3f (line 103) | Vec3f toneMapLinear(Vec3f c, float weight)
  function Vec3f (line 108) | Vec3f toneMapExp(Vec3f c, float weight)
  function Vec3f (line 113) | Vec3f toneMapReinhard(Vec3f c, float weight)
  type kToneMapType (line 121) | enum kToneMapType
  type MapInfo (line 137) | struct MapInfo
  function PFMWrite (line 147) | bool PFMWrite(const char* filename, int width, int height, Vec3f* image)
  function SkyToHemisphere (line 180) | void SkyToHemisphere(const SunSky& sunSky, int width, int height, uint8_...
  type cStats (line 240) | struct cStats
  function SkyToHemisphere (line 247) | void SkyToHemisphere(const SunSky& sunSky, int width, int height, Vec3f*...
  function SkyToCubeFace (line 342) | void SkyToCubeFace(const SunSky& sunSky, int face, int width, int height...
  function SkyToCubeFace (line 382) | void SkyToCubeFace(const SunSky& sunSky, int face, int width, int height...
  function SkyToPanoramic (line 422) | void SkyToPanoramic(const SunSky& sunSky, int height, uint8_t* data, int...
  function SkyToPanoramic (line 468) | void SkyToPanoramic(const SunSky& sunSky, int height, Vec3f* data, const...
  type EnumInfo (line 515) | struct EnumInfo
  function ArgEnum (line 547) | int ArgEnum(const EnumInfo info[], const char* name, int defaultValue = -1)
  function Help (line 559) | int Help(const char* command)
  function main (line 599) | int main(int argc, const char* argv[])

FILE: VL234f.hpp
  type VLDiag (line 68) | enum    VLDiag       { vl_I = 1, vl_minus_I = -1, vl_nI = -1 }
  type VLBlock (line 69) | enum    VLBlock      { vl_zero = 0, vl_one = 1, vl_minus_one = -1, vl_0 ...
  type VLAxis (line 70) | enum    VLAxis       { vl_x, vl_y, vl_z, vl_w }
  type VLMinusAxis (line 71) | enum    VLMinusAxis  { vl_minus_x, vl_minus_y, vl_minus_z, vl_minus_w, v...
  type VLVecType (line 94) | struct VLVecType { typedef void IsVec; typedef float Elt; }
  type VLMatType (line 95) | struct VLMatType { typedef void IsMat; typedef float Elt; }
  type VLVolType (line 96) | struct VLVolType { typedef void IsVol; typedef float Elt; }
  function len (line 123) | inline float  len   (float  x) { return abs(x); }
  function len (line 124) | inline double len   (double x) { return abs(x); }
  function len (line 125) | inline int    len   (int    x) { return abs(x); }
  function sqrlen (line 127) | inline float  sqrlen(float  x) { return x * x; }
  function sqrlen (line 128) | inline double sqrlen(double x) { return x * x; }
  function sqrlen (line 129) | inline int    sqrlen(int    x) { return x * x; }
  function T (line 131) | inline T sqr (T x) { return x * x; }
  function T (line 132) | inline T cube(T x) { return x * x * x; }
  function sign (line 134) | inline float  sign(float a ) { return a < 0.0f ? -1.0f : 1.0f; }
  function sign (line 135) | inline double sign(double a) { return a < 0.0  ? -1.0  : 1.0 ; }
  function lerp (line 137) | inline float  lerp(float  a, float  b, float  s) { return (1.0f - s) * a...
  function lerp (line 138) | inline double lerp(double a, double b, double s) { return (1.0  - s) * a...
  function T (line 160) | inline T lerp(T x, T y, S s)
  function T (line 165) | inline T vl_min(T a, T b)
  function T (line 170) | inline T vl_max(T a, T b)
  function T (line 175) | inline T vl_clamp(T x, T min, T max)
  function T (line 180) | inline T vl_clamp_lower(T x, T min)
  function T (line 185) | inline T vl_clamp_upper(T x, T max)
  function vl_sincos (line 191) | inline void vl_sincos(double phi, double* sinv, double* cosv) { VL_SINCO...
  function vl_sincos (line 192) | inline void vl_sincos(float  phi, float*  sinv, float*  cosv) { VL_SINCO...
  function vl_sincos (line 196) | inline void vl_sincos(double phi, double* sinv, double* cosv)
  function vl_sincos (line 201) | inline void vl_sincos(float phi, float* sinv, float* cosv)
  class Vec2f (line 219) | class Vec2f : public VLVecType
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 364) | inline Vec2f& Vec2f::operator = (const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 372) | inline Vec2f& Vec2f::operator = (const T& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 382) | inline Vec2f& Vec2f::operator += (const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 390) | inline Vec2f& Vec2f::operator -= (const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 398) | inline Vec2f& Vec2f::operator *= (const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 406) | inline Vec2f& Vec2f::operator *= (float s)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 414) | inline Vec2f& Vec2f::operator /= (const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 422) | inline Vec2f& Vec2f::operator /= (float s)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 430) | inline Vec2f Vec2f::operator + (const Vec2f& a) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 440) | inline Vec2f Vec2f::operator - (const Vec2f& a) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 450) | inline const Vec2f& Vec2f::operator + () const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 455) | inline Vec2f Vec2f::operator - () const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 465) | inline Vec2f Vec2f::operator * (const Vec2f& a) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 475) | inline Vec2f Vec2f::operator * (float s) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 485) | inline Vec2f operator * (float s, const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 490) | inline Vec2f Vec2f::operator / (const Vec2f& a) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 500) | inline Vec2f Vec2f::operator / (float s) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function dot (line 510) | inline float dot(const Vec2f& a, const Vec2f& b)
  function Vec2f (line 515) | inline Vec2f cross(const Vec2f& a)
    method Elts (line 237) | int          Elts() const { return 2; }
  function sqrlen (line 525) | inline float sqrlen(const Vec2f& v)
  function len (line 531) | inline float len(const Vec2f& v)
  function Vec2f (line 536) | inline Vec2f norm(const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 542) | inline Vec2f norm_safe(const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 547) | inline Vec2f inv(const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 553) | inline Vec2f abs(const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 558) | inline Vec2f& Vec2f::MakeUnit(int i, float k)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 570) | inline Vec2f& Vec2f::MakeZero()
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 576) | inline Vec2f& Vec2f::MakeBlock(float k)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 599) | inline Vec2f& Vec2f::operator = (VLBlock k)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 605) | inline Vec2f& Vec2f::operator = (VLAxis k)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 611) | inline Vec2f& Vec2f::operator = (VLMinusAxis k)
    method Elts (line 237) | int          Elts() const { return 2; }
  class Vec2f (line 656) | class Vec2f
    method Elts (line 237) | int          Elts() const { return 2; }
  class Vec3f (line 658) | class Vec3f : public VLVecType
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 819) | inline Vec3f& Vec3f::operator = (const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 828) | inline Vec3f& Vec3f::operator = (const T& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 838) | inline Vec3f& Vec3f::operator += (const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 847) | inline Vec3f& Vec3f::operator -= (const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 856) | inline Vec3f& Vec3f::operator *= (const Vec3f& a)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 865) | inline Vec3f& Vec3f::operator *= (float s)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 874) | inline Vec3f& Vec3f::operator /= (const Vec3f& a)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 883) | inline Vec3f& Vec3f::operator /= (float s)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 892) | inline Vec3f Vec3f::operator + (const Vec3f& a) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 903) | inline Vec3f Vec3f::operator - (const Vec3f& a) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 914) | inline const Vec3f& Vec3f::operator + () const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 919) | inline Vec3f Vec3f::operator - () const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 930) | inline Vec3f Vec3f::operator * (const Vec3f& a) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 941) | inline Vec3f Vec3f::operator * (float s) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 952) | inline Vec3f Vec3f::operator / (const Vec3f& a) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 963) | inline Vec3f Vec3f::operator / (float s) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 974) | inline Vec3f operator * (float s, const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 979) | inline Vec3f& Vec3f::MakeUnit(int n, float k)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 996) | inline Vec3f& Vec3f::MakeZero()
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1002) | inline Vec3f& Vec3f::MakeBlock(float k)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1026) | inline Vec3f& Vec3f::operator = (VLBlock k)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1032) | inline Vec3f& Vec3f::operator = (VLAxis k)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1038) | inline Vec3f& Vec3f::operator = (VLMinusAxis k)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec2f (line 1064) | inline Vec2f& Vec3f::AsVec2()
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 1069) | inline const Vec2f& Vec3f::AsVec2() const
    method Elts (line 237) | int          Elts() const { return 2; }
  function dot (line 1075) | inline float dot(const Vec3f& a, const Vec3f& b)
  function Vec3f (line 1080) | inline Vec3f cross(const Vec3f& a, const Vec3f& b)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1091) | inline Vec3f cross_x(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1094) | inline Vec3f cross_y(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1097) | inline Vec3f cross_z(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function sqrlen (line 1100) | inline float sqrlen(const Vec3f& v)
  function len (line 1106) | inline float len(const Vec3f& v)
  function Vec3f (line 1111) | inline Vec3f norm(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1117) | inline Vec3f norm_safe(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1122) | inline Vec3f inv(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec2f (line 1127) | inline Vec2f proj(const Vec3f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec3f (line 1140) | inline Vec3f abs(const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  class Vec2f (line 1154) | class Vec2f
    method Elts (line 237) | int          Elts() const { return 2; }
  class Vec3f (line 1155) | class Vec3f
    method Elts (line 677) | int          Elts() const { return 3; }
  class Vec4f (line 1157) | class Vec4f : public VLVecType
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1325) | inline Vec4f& Vec4f::operator = (const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1335) | inline Vec4f& Vec4f::operator = (const T& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1345) | inline Vec4f& Vec4f::operator += (const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1355) | inline Vec4f& Vec4f::operator -= (const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1365) | inline Vec4f& Vec4f::operator *= (const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1375) | inline Vec4f& Vec4f::operator *= (float s)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1385) | inline Vec4f& Vec4f::operator /= (const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1395) | inline Vec4f& Vec4f::operator /= (float s)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1406) | inline Vec4f Vec4f::operator + (const Vec4f& a) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1418) | inline Vec4f Vec4f::operator - (const Vec4f& a) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1430) | inline const Vec4f& Vec4f::operator + () const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1435) | inline Vec4f Vec4f::operator - () const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1447) | inline Vec4f Vec4f::operator * (const Vec4f& a) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1459) | inline Vec4f Vec4f::operator * (float s) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1471) | inline Vec4f Vec4f::operator / (const Vec4f& a) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1483) | inline Vec4f Vec4f::operator / (float s) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1495) | inline Vec4f operator * (float s, const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1500) | inline Vec4f& Vec4f::MakeZero()
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1506) | inline Vec4f& Vec4f::MakeBlock(float k)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1528) | inline Vec4f& Vec4f::operator = (VLBlock k)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1534) | inline Vec4f& Vec4f::operator = (VLAxis k)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1540) | inline Vec4f& Vec4f::operator = (VLMinusAxis k)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec2f (line 1546) | inline Vec2f& Vec4f::AsVec2()
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 1551) | inline const Vec2f& Vec4f::AsVec2() const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec3f (line 1556) | inline Vec3f& Vec4f::AsVec3()
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 1561) | inline const Vec3f& Vec4f::AsVec3() const
    method Elts (line 677) | int          Elts() const { return 3; }
  function dot (line 1568) | inline float dot(const Vec4f& a, const Vec4f& b)
  function sqrlen (line 1573) | inline float sqrlen(const Vec4f& v)
  function len (line 1579) | inline float len(const Vec4f& v)
  function Vec4f (line 1584) | inline Vec4f norm(const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1590) | inline Vec4f norm_safe(const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1595) | inline Vec4f inv(const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 1601) | inline Vec4f abs(const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  class Mat2f (line 1615) | class Mat2f : public VLMatType
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Vec2f (line 1768) | inline Vec2f& Mat2f::operator [] (int i)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 1774) | inline const Vec2f& Mat2f::operator [] (int i) const
    method Elts (line 237) | int          Elts() const { return 2; }
  function Mat2f (line 1836) | inline Mat2f& Mat2f::operator = (VLDiag k)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1842) | inline Mat2f& Mat2f::operator = (VLBlock k)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1848) | inline Mat2f& Mat2f::operator = (const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1856) | inline Mat2f& Mat2f::operator = (const T& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1866) | inline Mat2f& Mat2f::operator += (const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1874) | inline Mat2f& Mat2f::operator -= (const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1882) | inline Mat2f& Mat2f::operator *= (const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1893) | inline Mat2f& Mat2f::operator *= (float s)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1901) | inline Mat2f& Mat2f::operator /= (float s)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1910) | inline Mat2f Mat2f::operator + (const Mat2f& m) const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1920) | inline Mat2f Mat2f::operator - (const Mat2f& m) const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1930) | inline const Mat2f& Mat2f::operator + () const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1935) | inline Mat2f Mat2f::operator - () const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1945) | inline Mat2f Mat2f::operator * (const Mat2f& m) const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1957) | inline Mat2f Mat2f::operator * (float s) const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1967) | inline Mat2f Mat2f::operator / (float s) const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat2f (line 1977) | inline Mat2f  operator *  (float s, const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Vec2f (line 1982) | inline Vec2f operator * (const Mat2f& m, const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 1992) | inline Vec2f operator * (const Vec2f& v, const Mat2f& m)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 2002) | inline Vec2f& operator *= (Vec2f& v, const Mat2f& m)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 2014) | inline Vec2f row(const Mat2f& m, int i)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec2f (line 2020) | inline Vec2f col(const Mat2f& m, int j)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Mat2f (line 2026) | inline Mat2f trans(const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function trace (line 2036) | inline float trace(const Mat2f& m)
  function Mat2f (line 2041) | inline Mat2f adj(const Mat2f& m)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  class Vec4f (line 2060) | class Vec4f
    method Elts (line 1176) | int          Elts() const { return 4; }
  class Mat3f (line 2062) | class Mat3f : public VLMatType
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Vec3f (line 2230) | inline Vec3f& Mat3f::operator [] (int i)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 2236) | inline const Vec3f& Mat3f::operator [] (int i) const
    method Elts (line 677) | int          Elts() const { return 3; }
  function Mat2f (line 2303) | inline Mat2f Mat3f::AsMat2() const
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat3f (line 2308) | inline Mat3f& Mat3f::operator = (VLDiag k)
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2314) | inline Mat3f& Mat3f::operator = (VLBlock k)
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2320) | inline Mat3f& Mat3f::operator = (const T& m)
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2330) | inline const Mat3f& Mat3f::operator + () const
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2335) | inline Mat3f operator *  (const float s, const Mat3f& m)
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Vec3f (line 2340) | inline Vec3f operator * (const Mat3f& m, const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 2351) | inline Vec3f operator * (const Vec3f& v, const Mat3f& m)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 2362) | inline Vec3f& operator *= (Vec3f& v, const Mat3f& m)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 2375) | inline Vec3f row(const Mat3f& m, int i)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec3f (line 2381) | inline Vec3f col(const Mat3f& m, int j)
    method Elts (line 677) | int          Elts() const { return 3; }
  class Vec3f (line 2397) | class Vec3f
    method Elts (line 677) | int          Elts() const { return 3; }
  class Mat4f (line 2399) | class Mat4f : public VLMatType
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Vec4f (line 2559) | inline Vec4f& Mat4f::operator [] (int i)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 2565) | inline const Vec4f& Mat4f::operator [] (int i) const
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Mat4f (line 2597) | inline Mat4f& Mat4f::operator = (VLDiag k)
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2603) | inline Mat4f& Mat4f::operator = (VLBlock k)
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2609) | inline Mat4f& Mat4f::operator = (const T& m)
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2619) | inline const Mat4f& Mat4f::operator + () const
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2624) | inline Mat4f operator * (float s, const Mat4f& m)
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Vec4f (line 2629) | inline Vec4f row(const Mat4f& m, int i)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Vec4f (line 2635) | inline Vec4f col(const Mat4f& m, int j)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Mat2f (line 2682) | inline Mat2f Rot2f(float theta)                            { return RRot...
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat3f (line 2684) | inline Mat3f Rot3f(const Vec3f& axis, float theta)         { return RRot...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2685) | inline Mat3f Rot3f(const Vec4f& quaternion)               { return RRot3...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2686) | inline Mat3f Rot3f(const Vec3f& from, const Vec3f& to)    { return RRot3...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2688) | inline Mat3f HRot3f  (float theta)                         { return HRRo...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2689) | inline Mat3f HTrans3f(const Vec2f& t)                     { return HRTra...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat4f (line 2691) | inline Mat4f HRot4f  (const Vec3f& axis, float theta)      { return HRRo...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2692) | inline Mat4f HRot4f  (const Vec4f& q)                     { return HRRot...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2693) | inline Mat4f HRot4f  (const Vec3f& from, const Vec3f& to) { return HRRot...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2694) | inline Mat4f HTrans4f(const Vec3f& t)                     { return HRTra...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Vec2f (line 2696) | inline Vec2f xform(const Mat2f& m, const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Mat2f (line 2698) | inline Mat2f xform(const Mat2f& m, const Mat2f& n)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Vec2f (line 2701) | inline Vec2f xform(const Mat3f& m, const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec3f (line 2703) | inline Vec3f xform(const Mat3f& m, const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Mat3f (line 2705) | inline Mat3f xform(const Mat3f& m, const Mat3f& n)
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Vec3f (line 2708) | inline Vec3f xform(const Mat4f& m, const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec4f (line 2710) | inline Vec4f xform(const Mat4f& m, const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Mat4f (line 2712) | inline Mat4f xform(const Mat4f& m, const Mat4f& n)
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat2f (line 2716) | inline Mat2f Rot2f(float theta)                            { return CRot...
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Mat3f (line 2718) | inline Mat3f Rot3f(const Vec3f& axis, float theta)         { return CRot...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2719) | inline Mat3f Rot3f(const Vec4f& quaternion)               { return CRot3...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2720) | inline Mat3f Rot3f(const Vec3f& from, const Vec3f& to)    { return CRot3...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2722) | inline Mat3f HRot3f  (float theta)                         { return HCRo...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat3f (line 2723) | inline Mat3f HTrans3f(const Vec2f& t)                     { return HCTra...
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Mat4f (line 2725) | inline Mat4f HRot4f  (const Vec3f& axis, float theta)      { return HCRo...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2726) | inline Mat4f HRot4f  (const Vec4f& q)                     { return HCRot...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2727) | inline Mat4f HRot4f  (const Vec3f& from, const Vec3f& to) { return HCRot...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Mat4f (line 2728) | inline Mat4f HTrans4f(const Vec3f& t)                     { return HCTra...
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  function Vec2f (line 2730) | inline Vec2f xform(const Mat2f& m, const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Mat2f (line 2732) | inline Mat2f xform(const Mat2f& m, const Mat2f& n)
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  function Vec2f (line 2735) | inline Vec2f xform(const Mat3f& m, const Vec2f& v)
    method Elts (line 237) | int          Elts() const { return 2; }
  function Vec3f (line 2737) | inline Vec3f xform(const Mat3f& m, const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Mat3f (line 2739) | inline Mat3f xform(const Mat3f& m, const Mat3f& n)
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  function Vec3f (line 2742) | inline Vec3f xform(const Mat4f& m, const Vec3f& v)
    method Elts (line 677) | int          Elts() const { return 3; }
  function Vec4f (line 2744) | inline Vec4f xform(const Mat4f& m, const Vec4f& v)
    method Elts (line 1176) | int          Elts() const { return 4; }
  function Mat4f (line 2746) | inline Mat4f xform(const Mat4f& m, const Mat4f& n)
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }
  class Vec2f (line 2765) | class Vec2f
    method Elts (line 237) | int          Elts() const { return 2; }
  class Vec3f (line 2766) | class Vec3f
    method Elts (line 677) | int          Elts() const { return 3; }
  class Vec4f (line 2767) | class Vec4f
    method Elts (line 1176) | int          Elts() const { return 4; }
  class Mat2f (line 2768) | class Mat2f
    method Elts (line 1635) | int          Elts() const { return 4; }
    method Rows (line 1636) | int          Rows() const { return 2; }
    method Cols (line 1637) | int          Cols() const { return 2; }
  class Mat3f (line 2769) | class Mat3f
    method Elts (line 2085) | int          Elts() const { return 9; }
    method Rows (line 2086) | int          Rows() const { return 3; }
    method Cols (line 2087) | int          Cols() const { return 3; }
  class Mat4f (line 2770) | class Mat4f
    method Elts (line 2423) | int          Elts() const { return 16; }
    method Rows (line 2424) | int          Rows() const { return  4; }
    method Cols (line 2425) | int          Cols() const { return  4; }

FILE: stb_image_mini.h
  type stbi_uc (line 37) | typedef unsigned char stbi_uc;
  type stbi_us (line 38) | typedef unsigned short stbi_us;
  type stbi_io_callbacks (line 48) | typedef struct
  type stbi__uint16 (line 147) | typedef unsigned short stbi__uint16;
  type stbi__int16 (line 148) | typedef   signed short stbi__int16;
  type stbi__uint32 (line 149) | typedef unsigned int   stbi__uint32;
  type stbi__int32 (line 150) | typedef   signed int   stbi__int32;
  type stbi__uint16 (line 153) | typedef uint16_t stbi__uint16;
  type stbi__int16 (line 154) | typedef int16_t  stbi__int16;
  type stbi__uint32 (line 155) | typedef uint32_t stbi__uint32;
  type stbi__int32 (line 156) | typedef int32_t  stbi__int32;
  type stbi__context (line 219) | typedef struct
  function stbi__start_mem (line 239) | static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int...
  function stbi__start_callbacks (line 248) | static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c...
  function stbi__stdio_read (line 260) | static int stbi__stdio_read(void *user, char *data, int size)
  function stbi__stdio_skip (line 265) | static void stbi__stdio_skip(void *user, int n)
  function stbi__stdio_eof (line 270) | static int stbi__stdio_eof(void *user)
  function stbi__start_file (line 282) | static void stbi__start_file(stbi__context *s, FILE *f)
  function stbi__rewind (line 290) | static void stbi__rewind(stbi__context *s)
  type stbi__result_info (line 305) | typedef struct
  function STBIDEF (line 330) | STBIDEF const char *stbi_failure_reason(void)
  function stbi__err (line 335) | static int stbi__err(const char *str)
  function stbi__addsizes_valid (line 358) | static int stbi__addsizes_valid(int a, int b)
  function stbi__mul2sizes_valid (line 370) | static int stbi__mul2sizes_valid(int a, int b)
  function stbi__mad2sizes_valid (line 379) | static int stbi__mad2sizes_valid(int a, int b, int add)
  function stbi__mad3sizes_valid (line 385) | static int stbi__mad3sizes_valid(int a, int b, int c, int add)
  function STBIDEF (line 422) | STBIDEF void stbi_image_free(void *retval_from_stbi_load)
  function STBIDEF (line 431) | STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
  function stbi__vertical_flip (line 451) | static void stbi__vertical_flip(void *image, int w, int h, int bytes_per...
  function FILE (line 493) | static FILE *stbi__fopen(char const *filename, char const *mode)
  function STBIDEF (line 506) | STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *co...
  function STBIDEF (line 516) | STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp,...
  function STBIDEF (line 529) | STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, i...
  function STBIDEF (line 536) | STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk,...
  function stbi__refill_buffer (line 556) | static void stbi__refill_buffer(stbi__context *s)
  function stbi_inline (line 572) | stbi_inline static stbi_uc stbi__get8(stbi__context *s)
  function stbi_inline (line 583) | stbi_inline static int stbi__at_eof(stbi__context *s)
  function stbi__skip (line 595) | static void stbi__skip(stbi__context *s, int n)
  function stbi__getn (line 612) | static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
  function stbi__get16be (line 636) | static int stbi__get16be(stbi__context *s)
  function stbi__uint32 (line 642) | static stbi__uint32 stbi__get32be(stbi__context *s)
  function stbi_uc (line 665) | static stbi_uc stbi__compute_y(int r, int g, int b)
  type stbi__huffman (line 739) | typedef struct
  type stbi__jpeg (line 750) | typedef struct
  function stbi__build_huffman (line 804) | static int stbi__build_huffman(stbi__huffman *h, int *count)
  function stbi__build_fast_ac (line 848) | static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)
  function stbi__grow_buffer_unsafe (line 873) | static void stbi__grow_buffer_unsafe(stbi__jpeg *j)
  function stbi_inline (line 895) | stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffm...
  function stbi_inline (line 949) | stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
  function stbi_inline (line 965) | stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
  function stbi_inline (line 976) | stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
  function stbi__jpeg_decode_block (line 1004) | static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__...
  function stbi__jpeg_decode_block_prog_dc (line 1056) | static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64]...
  function stbi__jpeg_decode_block_prog_ac (line 1083) | static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64]...
  function stbi_inline (line 1203) | stbi_inline static stbi_uc stbi__clamp(int x)
  function stbi__idct_block (line 1254) | static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])
  function stbi_uc (line 1319) | static stbi_uc stbi__get_marker(stbi__jpeg *j)
  function stbi__jpeg_reset (line 1336) | static void stbi__jpeg_reset(stbi__jpeg *j)
  function stbi__parse_entropy_coded_data (line 1349) | static int stbi__parse_entropy_coded_data(stbi__jpeg *z)
  function stbi__jpeg_dequantize (line 1473) | static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant)
  function stbi__jpeg_finish (line 1480) | static void stbi__jpeg_finish(stbi__jpeg *z)
  function stbi__process_marker (line 1499) | static int stbi__process_marker(stbi__jpeg *z, int m)
  function stbi__process_scan_header (line 1602) | static int stbi__process_scan_header(stbi__jpeg *z)
  function stbi__free_jpeg_components (line 1641) | static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why)
  function stbi__process_frame_header (line 1663) | static int stbi__process_frame_header(stbi__jpeg *z, int scan)
  function stbi__decode_jpeg_header (line 1755) | static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
  function stbi__decode_jpeg_image (line 1780) | static int stbi__decode_jpeg_image(stbi__jpeg *j)
  type stbi_uc (line 1822) | typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_u...
  function stbi_uc (line 1827) | static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *...
  function stbi_uc (line 1836) | static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, s...
  function stbi_uc (line 1846) | static stbi_uc*  stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, ...
  function stbi_uc (line 1876) | static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, ...
  function stbi_uc (line 1901) | static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_nea...
  function stbi__YCbCr_to_RGB_row (line 1915) | static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const...
  function stbi__setup_jpeg (line 1942) | static void stbi__setup_jpeg(stbi__jpeg *j)
  function stbi__cleanup_jpeg (line 1952) | static void stbi__cleanup_jpeg(stbi__jpeg *j)
  type stbi__resample (line 1957) | typedef struct
  function stbi_uc (line 1968) | static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)
  function stbi_uc (line 1974) | static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, i...
  function stbi__jpeg_test (line 2145) | static int stbi__jpeg_test(stbi__context *s)
  function stbi__jpeg_info_raw (line 2157) | static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)
  function stbi__jpeg_info (line 2169) | static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
  type stbi__zhuffman (line 2194) | typedef struct
  function stbi_inline (line 2204) | stbi_inline static int stbi__bitreverse16(int n)
  function stbi_inline (line 2213) | stbi_inline static int stbi__bit_reverse(int v, int bits)
  function stbi__zbuild_huffman (line 2221) | static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizeli...
  type stbi__zbuf (line 2274) | typedef struct
  function stbi_inline (line 2288) | stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
  function stbi__fill_bits (line 2294) | static void stbi__fill_bits(stbi__zbuf *z)
  function stbi__zreceive (line 2303) | int stbi__zreceive(stbi__zbuf *z, int n)
  function stbi__zhuffman_decode_slowpath (line 2313) | static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
  function stbi_inline (line 2331) | stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffm...
  function stbi__zexpand (line 2345) | static int stbi__zexpand(stbi__zbuf *z, char *zout, int n)  // need to m...
  function stbi__parse_huffman_block (line 2378) | static int stbi__parse_huffman_block(stbi__zbuf *a)
  function stbi__compute_huffman_codes (line 2420) | static int stbi__compute_huffman_codes(stbi__zbuf *a)
  function stbi__parse_uncompressed_block (line 2469) | static int stbi__parse_uncompressed_block(stbi__zbuf *a)
  function stbi__parse_zlib_header (line 2498) | static int stbi__parse_zlib_header(stbi__zbuf *a)
  function stbi__parse_zlib (line 2540) | static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
  function stbi__do_zlib (line 2568) | static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, i...
  function STBIDEF (line 2578) | STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int ...
  function STBIDEF (line 2594) | STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *...
  function STBIDEF (line 2599) | STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *b...
  function STBIDEF (line 2615) | STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const ...
  function STBIDEF (line 2626) | STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int l...
  function STBIDEF (line 2642) | STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, co...
  type stbi__pngchunk (line 2664) | typedef struct
  function stbi__pngchunk (line 2670) | static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
  function stbi__check_png_header (line 2678) | static int stbi__check_png_header(stbi__context *s)
  type stbi__png (line 2687) | typedef struct
  function stbi__paeth (line 2715) | static int stbi__paeth(int a, int b, int c)
  function stbi__create_png_image_raw (line 2729) | static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__...
  function stbi__create_png_image (line 2941) | static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stb...
  function stbi__compute_transparency (line 2985) | static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int o...
  function stbi__compute_transparency16 (line 3010) | static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3]...
  function stbi__expand_png_palette (line 3035) | static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int ...
  function STBIDEF (line 3075) | STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpr...
  function STBIDEF (line 3080) | STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_conv...
  function stbi__de_iphone (line 3085) | static void stbi__de_iphone(stbi__png *z)
  function stbi__parse_png_file (line 3130) | static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
  function stbi__png_test (line 3336) | static int stbi__png_test(stbi__context *s)
  function stbi__png_info_raw (line 3344) | static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
  function stbi__png_info (line 3356) | static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
  function stbi__info_main (line 3363) | static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
  function STBIDEF (line 3372) | STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)
  function STBIDEF (line 3382) | STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
  function STBIDEF (line 3393) | STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x...
  function STBIDEF (line 3400) | STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *u...
  type stbiw_uint32 (line 3444) | typedef unsigned int stbiw_uint32;
  function writefv (line 3447) | static void writefv(FILE *f, const char *fmt, va_list v)
  function write3 (line 3467) | static void write3(FILE *f, unsigned char a, unsigned char b, unsigned c...
  function write_pixels (line 3474) | static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, i...
  function outfile (line 3521) | static int outfile(char const *filename, int rgb_dir, int vdir, int x, i...
  function stbi_write_bmp (line 3537) | int stbi_write_bmp(char const *filename, int x, int y, int comp, const v...
  function stbi_write_tga (line 3546) | int stbi_write_tga(char const *filename, int x, int y, int comp, const v...
  function stbiw__linear_to_rgbe (line 3560) | void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
  function stbiw__zlib_bitrev (line 3617) | static int stbiw__zlib_bitrev(int code, int codebits)
  function stbiw__zlib_countm (line 3627) | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *...
  function stbiw__zhash (line 3635) | static unsigned int stbiw__zhash(unsigned char *data)
  function stbiw__crc32 (line 3764) | unsigned int stbiw__crc32(unsigned char *buffer, int len)
  function stbiw__wpcrc (line 3782) | static void stbiw__wpcrc(unsigned char **data, int len)
  function stbiw__paeth (line 3788) | static unsigned char stbiw__paeth(int a, int b, int c)
  function stbi_write_png (line 3888) | int stbi_write_png(char const *filename, int x, int y, int comp, const v...
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (421K chars).
[
  {
    "path": ".gitignore",
    "chars": 7,
    "preview": "sunsky\n"
  },
  {
    "path": "HosekCubic.h",
    "chars": 11884,
    "preview": "#if defined(__clang__)\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmissing-braces\"\n#endif\n"
  },
  {
    "path": "HosekDataXYZ.h",
    "chars": 67043,
    "preview": "/*\nThis source is published under the following 3-clause BSD license.\n\nCopyright (c) 2012 - 2013, Lukas Hosek and Alexan"
  },
  {
    "path": "LICENSE.md",
    "chars": 1210,
    "preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
  },
  {
    "path": "Makefile",
    "chars": 146,
    "preview": "CXXFLAGS = -std=c++11 -O3\n\nsunsky: SunSky.cpp SunSky.hpp SunSkyTool.cpp\n\t$(CXX) $(CXXFLAGS) -o $@ SunSky.cpp SunSkyTool."
  },
  {
    "path": "README.md",
    "chars": 6339,
    "preview": "Overview\n========\n\nThe code in SunSky.* is an implementation of several sky models:\n\n* The Preetham Clear Sky model. (Fr"
  },
  {
    "path": "SunSky.cpp",
    "chars": 70132,
    "preview": "//\n// SunSky.cpp\n//\n// Implements SunSky.hpp\n//\n// Andrew Willmott, Preetham sun/sky based on code by Brian Smits,\n// Ho"
  },
  {
    "path": "SunSky.hpp",
    "chars": 11528,
    "preview": "//\n// SunSky.hpp\n//\n// Implements various sky models -- Preetham, Hosek, CIE* -- as\n// well as a separable table-based m"
  },
  {
    "path": "SunSkyTool.cpp",
    "chars": 27150,
    "preview": "//\n//  SunSkyTool.cpp\n//\n//  Test tool for SunSky.*\n//\n//  Andrew Willmott\n//\n\n#define _CRT_SECURE_NO_WARNINGS\n#define _"
  },
  {
    "path": "VL234f.hpp",
    "chars": 64527,
    "preview": "//\n// VL234f.hpp\n//\n// Andrew Willmott\n//\n\n#ifndef VL234f_H\n#define VL234f_H\n\n#include <cmath>\n// Default definitions\n\n#"
  },
  {
    "path": "gen-images.sh",
    "chars": 1258,
    "preview": "\n\nmkdir -p images\n\nSUNSKY=\"./sunsky -f -v -l 30 0 -d 58 -b 3\"\nOPTS=\n\nTIMES=\"12.5 13.5 14.5 15.5 16.5 17.5 18.5\"\nROUGHNES"
  },
  {
    "path": "sky.sh",
    "chars": 3111,
    "preview": "//\n// Functions for evaluating sky model for sky box quad and lighting\n//\n// See https://github.com/andrewwillmott/sun-s"
  },
  {
    "path": "skybox_fs.sc",
    "chars": 1011,
    "preview": "//\n// Sky quad shader\n//\n\n$input v_param0\n\n#include \"lib/sky.sh\"\n\nuniform vec4 u_sunColour;\nuniform vec4 u_sunDirection;"
  },
  {
    "path": "stb_image_mini.h",
    "chars": 135928,
    "preview": "// This is a cutdown version of stb_image[_write].h supporting only png and jpeg.\n// See the original for documentation "
  }
]

About this extraction

This page contains the full source code of the andrewwillmott/sun-sky GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (391.9 KB), approximately 150.4k tokens, and a symbol index with 484 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!