diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b94016..7faddc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.7) + option(VERSION_STRING "Project version string" "0.0.0") project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) diff --git a/src/color/aces.hpp b/src/color/aces.hpp new file mode 100644 index 0000000..a127ca1 --- /dev/null +++ b/src/color/aces.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_ACES_HPP +#define ANTKEEPER_COLOR_ACES_HPP + +#include "color/rgb.hpp" +#include "math/math.hpp" + +namespace color { + +/// ACES color spaces. +namespace aces { + +/// CIE xy chromaticity coordinates of the ACES white point (~D60). +template +constexpr math::vector2 white_point = {T{0.32168}, {0.33767}}; + +/// ACES AP0 color space. +template +constexpr rgb::color_space ap0 +( + {T{0.7347}, T{ 0.2653}}, + {T{0.0000}, T{ 1.0000}}, + {T{0.0001}, T{-0.0770}}, + aces::white_point, + nullptr, + nullptr +); + +/// ACES AP1 color space. +template +constexpr rgb::color_space ap1 +( + {T{0.713}, T{0.293}}, + {T{0.165}, T{0.830}}, + {T{0.128}, T{0.044}}, + aces::white_point, + nullptr, + nullptr +); + +/** + * Constructs a saturation adjustment matrix. + * + * @param s Saturation adjustment factor. + * @param to_y Color space to CIE XYZ luminance vector. + * + * @return Saturation adjustment matrix. + */ +template +constexpr math::matrix adjust_saturation(T s, const math::vector3& to_y) +{ + const math::vector3 v = to_y * (T{1} - s); + return math::matrix + { + v[0] + s, v[0], v[0], + v[1], v[1] + s, v[1], + v[2], v[2], v[2] + s + }; +} + +/// ACES AP1 RRT saturation adjustment matrix. +template +constexpr math::matrix ap1_rrt_sat = aces::adjust_saturation(T{0.96}, ap1.to_y); + +/// ACES AP1 ODT saturation adjustment matrix. +template +constexpr math::matrix ap1_odt_sat = aces::adjust_saturation(T{0.93}, ap1.to_y); + +} // namespace aces + +} // namespace color + +#endif // ANTKEEPER_COLOR_ACES_HPP diff --git a/src/color/acescg.hpp b/src/color/acescg.hpp deleted file mode 100644 index 1b50f34..0000000 --- a/src/color/acescg.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2021 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_ACESCG_HPP -#define ANTKEEPER_COLOR_ACESCG_HPP - -#include "math/math.hpp" - -namespace color { - -/// Functions which operate in the ACEScg colorspace. -namespace acescg { - -/// CIE chromaticity coordinates of the ACEScg white point (~D60). -template -constexpr math::vector2 whitepoint = {0.32168, 0.33767}; - -/** - * Calculates the luminance of an ACEScg color. - * - * @param x ACEScg color. - * @return return Luminance of @p x. - */ -template -T luminance(const math::vector3& x); - -/** - * Transforms an ACEScg color into the linear sRGB colorspace using the Bradford chromatic adaption transform. - * - * @param x ACEScg color. - * @return return Linear sRGB color. - * - * @see https://www.colour-science.org/apps/ - */ -template -math::vector3 to_srgb(const math::vector3& x); - -/** - * Transforms an ACEScg color into the CIE XYZ colorspace. - * - * @param x ACEScg color. - * @return return CIE XYZ color. - */ -template -math::vector3 to_xyz(const math::vector3& x); - -template -T luminance(const math::vector3& x) -{ - static const math::vector3 luma = {0.272228716780914, 0.674081765811148, 0.053689517407937}; - return math::dot(x, luma); -} - -template -math::vector3 to_srgb(const math::vector3& x) -{ - static const math::matrix3 acescg_to_srgb - {{ - { 1.704858676289160, -0.130076824208823, -0.023964072927574}, - {-0.621716021885330, 1.140735774822504, -0.128975508299318}, - {-0.083299371729058, -0.010559801677511, 1.153014018916862} - }}; - - return acescg_to_srgb * x; -} - -template -math::vector3 to_xyz(const math::vector3& x) -{ - static const math::matrix3 acescg_to_xyz - {{ - {0.662454181108505, 0.272228716780914, -0.005574649490394}, - {0.134004206456433, 0.674081765811148, 0.004060733528983}, - {0.156187687004908, 0.053689517407937, 1.010339100312997} - }}; - - return acescg_to_xyz * x; -} - -} // namespace acescg -} // namespace color - -#endif // ANTKEEPER_COLOR_ACESCG_HPP diff --git a/src/color/cat.hpp b/src/color/cat.hpp new file mode 100644 index 0000000..3b56d03 --- /dev/null +++ b/src/color/cat.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_CAT_HPP +#define ANTKEEPER_COLOR_CAT_HPP + +#include "math/math.hpp" + +namespace color { + +/// Chromatic adaption transforms (CAT). +namespace cat { + +/** + * Bradford cone response matrix. + * + * @see Specification ICC.1:2010 (Profile version 4.3.0.0). Image technology colour management — Architecture, profile format, and data structure, Annex E.3, pp. 102. + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix bradford = +{ + 0.8951, -0.7502, 0.0389, + 0.2664, 1.7135, -0.0685, + -0.1614, 0.0367, 1.0296 +}; + +/** + * von Kries cone response matrix. + * + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix von_kries = +{ + 0.40024, -0.22630, 0.00000 + 0.70760, 1.16532, 0.00000 + -0.08081, 0.04570, 0.91822 +}; + +/** + * XYZ scaling cone response matrix. + * + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix xyz_scaling = +{ + T{1}, T{0}. T{0}, + T{0}, T{1}. T{0}, + T{0}, T{0}. T{1} +}; + +/** + * Constructs a chromatic adaptation transform (CAT) matrix. + * + * @param w0 CIE xy chromaticity coordinates of the source illuminant. + * @param w1 CIE xy chromaticity coordinates of the destination illuminant. + * @param cone_response Cone response matrix. + * + * @return CAT matrix. + * + * @see Specification ICC.1:2010 (Profile version 4.3.0.0). Image technology colour management — Architecture, profile format, and data structure, Annex E.3, pp. 102. + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix matrix(const math::vector2& w0, const math::vector2& w1, const math::matrix& cone_response = bradford) +{ + // Convert CIE xy chromaticity coordinates to CIE XYZ colors + const math::vector3 w0_xyz = {w0[0] / w0[1], T{1}, (T{1} - w0[0] - w0[1]) / w0[1]}; + const math::vector3 w1_xyz = {w1[0] / w1[1], T{1}, (T{1} - w1[0] - w1[1]) / w1[1]}; + + // Calculate cone response of CIE XYZ colors + const math::vector3 w0_cone_response = cone_response * w0_xyz; + const math::vector3 w1_cone_response = cone_response * w1_xyz; + + const math::matrix scale = + { + w1_cone_response[0] / w0_cone_response[0], T{0}, T{0}, + T{0}, w1_cone_response[1] / w0_cone_response[1], T{0}, + T{0}, T{0}, w1_cone_response[2] / w0_cone_response[2], + }; + + return math::inverse(cone_response) * scale * cone_response; +} + +} // namespace cat +} // namespace color + +#endif // ANTKEEPER_COLOR_CAT_HPP diff --git a/src/color/cct.hpp b/src/color/cct.hpp index c9d676c..e9f3eb5 100644 --- a/src/color/cct.hpp +++ b/src/color/cct.hpp @@ -38,7 +38,15 @@ namespace cct { * @see Krystek, M. (1985), An algorithm to calculate correlated colour temperature. Color Res. Appl., 10: 38-40. */ template -math::vector2 to_ucs(T t); +math::vector2 to_ucs(T t) +{ + const T tt = t * t; + return math::vector2 + { + (T{0.860117757} + T{1.54118254e-4} * t + T{1.28641212e-7} * tt) / (T{1} + T{8.42420235e-4} * t + T{7.08145163e-7} * tt), + (T{0.317398726} + T{4.22806245e-5} * t + T{4.20481691e-8} * tt) / (T{1} - T{2.89741816e-5} * t + T{1.61456053e-7} * tt) + }; +} /** * Calculates CIE xyY colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. @@ -47,7 +55,10 @@ math::vector2 to_ucs(T t); * @return CIE xyY color with `Y = 1`. */ template -math::vector3 to_xyy(T t); +math::vector3 to_xyy(T t) +{ + return ucs::to_xyy(to_ucs(t), T{1}); +} /** * Calculates CIE XYZ colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. @@ -55,27 +66,6 @@ math::vector3 to_xyy(T t); * @param t Correlated color temperature, in Kelvin. * @return CIE XYZ color with `Y = 1`. */ -template -math::vector3 to_xyz(T t); - -template -math::vector2 to_ucs(T t) -{ - // Approximate the Planckian locus in CIE 1960 UCS colorspace (Krystek's algorithm) - const T tt = t * t; - return math::vector2 - { - (T(0.860117757) + T(1.54118254e-4) * t + T(1.28641212e-7) * tt) / (T(1.0) + T(8.42420235e-4) * t + T(7.08145163e-7) * tt), - (T(0.317398726) + T(4.22806245e-5) * t + T(4.20481691e-8) * tt) / (T(1.0) - T(2.89741816e-5) * t + T(1.61456053e-7) * tt) - }; -} - -template -math::vector3 to_xyy(T t) -{ - return ucs::to_xyy(to_ucs(t), T(1.0)); -} - template math::vector3 to_xyz(T t) { diff --git a/src/color/color.hpp b/src/color/color.hpp index b5bcf10..8be5a20 100644 --- a/src/color/color.hpp +++ b/src/color/color.hpp @@ -23,12 +23,15 @@ /// Color manipulation functions. namespace color {} -#include "acescg.hpp" +#include "aces.hpp" #include "cct.hpp" #include "index.hpp" +#include "rgb.hpp" #include "srgb.hpp" #include "ucs.hpp" #include "xyy.hpp" #include "xyz.hpp" +#include "cat.hpp" +#include "illuminant.hpp" #endif // ANTKEEPER_COLOR_HPP diff --git a/src/color/illuminant.hpp b/src/color/illuminant.hpp new file mode 100644 index 0000000..0b134ec --- /dev/null +++ b/src/color/illuminant.hpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_ILLUMINANT_HPP +#define ANTKEEPER_COLOR_ILLUMINANT_HPP + +#include "math/math.hpp" + +namespace color { + +/** + * CIE standard illuminants. + * + * @see https://en.wikipedia.org/wiki/Standard_illuminant + */ +namespace illuminant { + +/// CIE 1931 2 Degree Standard Observer illuminants. +namespace deg2 { + + template + constexpr math::vector2 a = {T{0.44757}, T{0.40745}}; + template + constexpr math::vector2 b = {T{0.34842}, T{0.35161}}; + template + constexpr math::vector2 c = {T{0.31006}, T{0.31616}}; + template + constexpr math::vector2 d50 = {T{0.34567}, T{0.35850}}; + template + constexpr math::vector2 d55 = {T{0.33242}, T{0.34743}}; + template + constexpr math::vector2 d65 = {T{0.31271}, T{0.32902}}; + template + constexpr math::vector2 d75 = {T{0.29902}, T{0.31485}}; + template + constexpr math::vector2 d93 = {T{0.28315}, T{0.29711}}; + template + constexpr math::vector2 e = {T{0.33333}, T{0.33333}}; + template + constexpr math::vector2 f1 = {T{0.31310}, T{0.33727}}; + template + constexpr math::vector2 f2 = {T{0.37208}, T{0.37529}}; + template + constexpr math::vector2 f3 = {T{0.40910}, T{0.39430}}; + template + constexpr math::vector2 f4 = {T{0.44018}, T{0.40329}}; + template + constexpr math::vector2 f5 = {T{0.31379}, T{0.34531}}; + template + constexpr math::vector2 f6 = {T{0.37790}, T{0.38835}}; + template + constexpr math::vector2 f7 = {T{0.31292}, T{0.32933}}; + template + constexpr math::vector2 f8 = {T{0.34588}, T{0.35875}}; + template + constexpr math::vector2 f9 = {T{0.37417}, T{0.37281}}; + template + constexpr math::vector2 f10 = {T{0.34609}, T{0.35986}}; + template + constexpr math::vector2 f11 = {T{0.38052}, T{0.37713}}; + template + constexpr math::vector2 f12 = {T{0.43695}, T{0.40441}}; + template + constexpr math::vector2 led_b1 = {T{0.4560}, T{0.4078}}; + template + constexpr math::vector2 led_b2 = {T{0.4357}, T{0.4012}}; + template + constexpr math::vector2 led_b3 = {T{0.3756}, T{0.3723}}; + template + constexpr math::vector2 led_b4 = {T{0.3422}, T{0.3502}}; + template + constexpr math::vector2 led_b5 = {T{0.3118}, T{0.3236}}; + template + constexpr math::vector2 led_bh1 = {T{0.4474}, T{0.4066}}; + template + constexpr math::vector2 led_rgb1 = {T{0.4557}, T{0.4211}}; + template + constexpr math::vector2 led_v1 = {T{0.4560}, T{0.4548}}; + template + constexpr math::vector2 led_v2 = {T{0.3781}, T{0.3775}}; + +} // deg2 + +/// CIE 1964 10 Degree Standard Observer illuminants. +namespace deg10 { + + template + constexpr math::vector2 a = {T{0.45117}, T{0.40594}}; + template + constexpr math::vector2 b = {T{0.34980}, T{0.35270}}; + template + constexpr math::vector2 c = {T{0.31039}, T{0.31905}}; + template + constexpr math::vector2 d50 = {T{0.34773}, T{0.35952}}; + template + constexpr math::vector2 d55 = {T{0.33411}, T{0.34877}}; + template + constexpr math::vector2 d65 = {T{0.31382}, T{0.33100}}; + template + constexpr math::vector2 d75 = {T{0.29968}, T{0.31740}}; + template + constexpr math::vector2 d93 = {T{0.28327}, T{0.30043}}; + template + constexpr math::vector2 e = {T{0.33333}, T{0.33333}}; + template + constexpr math::vector2 f1 = {T{0.31811}, T{0.33559}}; + template + constexpr math::vector2 f2 = {T{0.37925}, T{0.36733}}; + template + constexpr math::vector2 f3 = {T{0.41761}, T{0.38324}}; + template + constexpr math::vector2 f4 = {T{0.44920}, T{0.39074}}; + template + constexpr math::vector2 f5 = {T{0.31975}, T{0.34246}}; + template + constexpr math::vector2 f6 = {T{0.38660}, T{0.37847}}; + template + constexpr math::vector2 f7 = {T{0.31569}, T{0.32960}}; + template + constexpr math::vector2 f8 = {T{0.34902}, T{0.35939}}; + template + constexpr math::vector2 f9 = {T{0.37829}, T{0.37045}}; + template + constexpr math::vector2 f10 = {T{0.35090}, T{0.35444}}; + template + constexpr math::vector2 f11 = {T{0.38541}, T{0.37123}}; + template + constexpr math::vector2 f12 = {T{0.44256}, T{0.39717}}; + +} // namespace deg10 + +} // namespace illuminant + +} // namespace color + +#endif // ANTKEEPER_COLOR_ILLUMINANT_HPP diff --git a/src/color/index.hpp b/src/color/index.hpp index 6033035..ff7b552 100644 --- a/src/color/index.hpp +++ b/src/color/index.hpp @@ -33,13 +33,10 @@ namespace index { * * @see Ballesteros, F. J. (2012). "New insights into black bodies". EPL 97 (2012) 34008. */ -template -T bv_to_cct(T bv); - template T bv_to_cct(T bv) { - return T(4600.0) * (T(1.0) / (T(0.92) * bv + T(1.7)) + T(1.0) / (T(0.92) * bv + T(0.62))); + return T{4600} * (T{1} / (T{0.92} * bv + T{1.7}) + T{1} / (T{0.92} * bv + T{0.62})); } } // namespace index diff --git a/src/color/rgb.hpp b/src/color/rgb.hpp new file mode 100644 index 0000000..4e83a7f --- /dev/null +++ b/src/color/rgb.hpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_RGB_HPP +#define ANTKEEPER_COLOR_RGB_HPP + +#include "color/cat.hpp" +#include "math/math.hpp" + +namespace color { + +/// Functions which operate on various RGB colorspaces. +namespace rgb { + +/** + * Constructs a matrix which transforms an RGB color into a CIE XYZ color. + * + * @param r CIE xy chromaticity coordinates of the red primary. + * @param g CIE xy chromaticity coordinates of the green primary. + * @param b CIE xy chromaticity coordinates of the blue primary. + * @param w CIE xy chromaticity coordinates of the white point. + * + * @return Matrix which transforms an RGB color into a CIE XYZ color. + * + * @see https://www.ryanjuckett.com/rgb-color-space-conversion/ + * @see https://mina86.com/2019/srgb-xyz-matrix/ + */ +template +constexpr math::matrix to_xyz(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w) +{ + const math::matrix m = + { + r[0], r[1], T{1} - (r[0] + r[1]), + g[0], g[1], T{1} - (g[0] + g[1]), + b[0], b[1], T{1} - (b[0] + b[1]) + }; + + const math::vector3 scale = math::inverse(m) * math::vector3{w[0] / w[1], T{1}, (T{1} - (w[0] + w[1])) / w[1]}; + + return math::matrix + { + m[0][0] * scale[0], m[0][1] * scale[0], m[0][2] * scale[0], + m[1][0] * scale[1], m[1][1] * scale[1], m[1][2] * scale[1], + m[2][0] * scale[2], m[2][1] * scale[2], m[2][2] * scale[2], + }; +} + +/** + * RGB color space. + */ +template +struct color_space +{ + /// Transfer function function pointer type. + typedef math::vector3 (*transfer_function_type)(const math::vector3&); + + /// CIE xy chromaticity coordinates of the red primary. + const math::vector2 r; + + /// CIE xy chromaticity coordinates of the green primary. + const math::vector2 g; + + /// CIE xy chromaticity coordinates of the blue primary. + const math::vector2 b; + + /// CIE xy chromaticity coordinates of the white point. + const math::vector2 w; + + /// Function pointer to the electro-optical transfer function. + const transfer_function_type eotf; + + /// Function pointer to the inverse electro-optical transfer function. + const transfer_function_type inverse_eotf; + + /// Matrix which transforms an RGB color to a CIE XYZ color. + const math::matrix3x3 to_xyz; + + /// Matrix which transforms a CIE XYZ color to an RGB color. + const math::matrix3x3 from_xyz; + + /// Vector which gives the luminance of an RGB color via dot product. + const math::vector3 to_y; + + /** + * Constructs an RGB color space. + * + * @param r CIE xy chromaticity coordinates of the red primary. + * @param g CIE xy chromaticity coordinates of the green primary. + * @param b CIE xy chromaticity coordinates of the blue primary. + * @param w CIE xy chromaticity coordinates of the white point. + */ + constexpr color_space(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w, transfer_function_type eotf, transfer_function_type inverse_eotf); + + /** + * Measures the luminance of a linear RGB color. + * + * @param x Linear RGB color. + * @return return Luminance of @p x. + */ + constexpr T luminance(const math::vector3& x) const; +}; + +template +constexpr color_space::color_space(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w, transfer_function_type eotf, transfer_function_type inverse_eotf): + r(r), + g(g), + b(b), + w(w), + eotf(eotf), + inverse_eotf(inverse_eotf), + to_xyz(color::rgb::to_xyz(r, g, b, w)), + from_xyz(math::inverse(to_xyz)), + to_y{to_xyz[0][1], to_xyz[1][1], to_xyz[2][1]} +{} + +template +constexpr T color_space::luminance(const math::vector3& x) const +{ + return math::dot(x, to_y); +} + +/** + * Constructs a matrix which transforms a color from one RGB color space to another RGB color space. + * + * @param s0 Source color space. + * @param s1 Destination color space. + * @param cone_response Chromatic adaptation transform cone response matrix. + * + * @return Color space transformation matrix. + */ +template +constexpr math::matrix3x3 to_rgb(const color_space& s0, const color_space& s1, const math::matrix3x3& cone_response = color::cat::bradford) +{ + return s1.from_xyz * color::cat::matrix(s0.w, s1.w, cone_response) * s0.to_xyz; +} + +} // namespace rgb +} // namespace color + +#endif // ANTKEEPER_COLOR_RGB_HPP diff --git a/src/color/srgb.hpp b/src/color/srgb.hpp index 03561be..a43e429 100644 --- a/src/color/srgb.hpp +++ b/src/color/srgb.hpp @@ -20,149 +20,71 @@ #ifndef ANTKEEPER_COLOR_SRGB_HPP #define ANTKEEPER_COLOR_SRGB_HPP +#include "color/rgb.hpp" +#include "color/illuminant.hpp" #include "math/math.hpp" #include namespace color { -/// Functions which operate in the sRGB colorspace. -namespace srgb { - -/// CIE chromaticity coordinates of the sRGB white point (D65) -template -constexpr math::vector2 whitepoint = {0.31271, 0.32902}; - /** - * Performs the sRGB Electro-Optical Transfer Function (EOTF), also known as the sRGB decoding function. + * sRGB electro-optical transfer function (EOTF), also known as the sRGB decoding function. * * @param v sRGB electrical signal (gamma-encoded sRGB). + * * @return Corresponding luminance of the signal (linear sRGB). */ -/// @{ -float eotf(float v); -double eotf(double v); template -math::vector3 eotf(const math::vector3& v); -/// @} +math::vector3 srgb_eotf(const math::vector3& v) +{ + auto f = [](T x) -> T + { + return x < T{0.04045} ? x / T{12.92} : std::pow((x + T{0.055}) / T{1.055}, T{2.4}); + }; + + return math::vector3 + { + f(v[0]), + f(v[1]), + f(v[2]) + }; +} /** - * Performs the sRGB inverse Electro-Optical Transfer Function (EOTF), also known as the sRGB encoding function. + * sRGB inverse electro-optical transfer function (EOTF), also known as the sRGB encoding function. * * @param l sRGB luminance (linear sRGB). - * @return Corresponding electrical signal (gamma-encoded sRGB). - */ -/// @{ -float eotf_inverse(float v); -double eotf_inverse(double v); -template -math::vector3 eotf_inverse(const math::vector3& l); -/// @} - -/** - * Calculates the luminance of a linear sRGB color. * - * @param x Linear sRGB color. - * @return return Luminance of @p x. - */ -template -T luminance(const math::vector3& x); - -/** - * Transforms a linear sRGB color into the ACEScg colorspace using the Bradford chromatic adaption transform. - * - * @param x Linear sRGB color. - * @return ACEScg color. - * - * @see https://www.colour-science.org/apps/ - */ -template -math::vector3 to_acescg(const math::vector3& x); - -/** - * Transforms a linear sRGB color into the CIE XYZ colorspace. - * - * @param x Linear sRGB color. - * @return CIE XYZ color. + * @return Corresponding electrical signal (gamma-encoded sRGB). */ template -math::vector3 to_xyz(const math::vector3& x); - -inline float eotf(float v) -{ - return (v < 0.04045f) ? (v / 12.92f) : std::pow((v + 0.055f) / 1.055f, 2.4f); -} - -inline double eotf(double v) +math::vector3 srgb_inverse_eotf(const math::vector3& l) { - return (v < 0.04045) ? (v / 12.92) : std::pow((v + 0.055) / 1.055, 2.4); -} - -template -math::vector3 eotf(const math::vector3& v) -{ - return math::vector3 - { - eotf(v[0]), - eotf(v[1]), - eotf(v[2]) - }; -} - -inline float eotf_inverse(float l) -{ - return (l <= 0.0031308f) ? (l * 12.92f) : (std::pow(l, 1.0f / 2.4f) * 1.055f - 0.055f); -} - -inline double eotf_inverse(double l) -{ - return (l <= 0.0031308) ? (l * 12.92) : (std::pow(l, 1.0 / 2.4) * 1.055 - 0.055); -} - -template -math::vector3 eotf_inverse(const math::vector3& l) -{ - return math::vector3 - { - eotf_inverse(l[0]), - eotf_inverse(l[1]), - eotf_inverse(l[2]) - }; -} - -template -T luminance(const math::vector3& x) -{ - static const math::vector3 luma = {0.212639005871510, 0.715168678767756, 0.072192315360734}; - return math::dot(x, luma); -} - -template -math::vector3 to_acescg(const math::vector3& x) -{ - static const math::matrix3 srgb_to_acescg - {{ - {0.613132422390542, 0.070124380833917, 0.020587657528185}, - {0.339538015799666, 0.916394011313573, 0.109574571610682}, - {0.047416696048269, 0.013451523958235, 0.869785404035327} - }}; + auto f = [](T x) -> T + { + return x <= T{0.0031308} ? x * T{12.92} : std::pow(x, T{1} / T{2.4}) * T{1.055} - T{0.055}; + }; - return srgb_to_acescg * x; + return math::vector3 + { + f(l[0]), + f(l[1]), + f(l[2]) + }; } +/// sRGB color space. template -math::vector3 to_xyz(const math::vector3& x) -{ - static const math::matrix3 srgb_to_xyz - {{ - {0.412390799265959, 0.212639005871510, 0.019330818715592}, - {0.357584339383878, 0.715168678767756, 0.119194779794626}, - {0.180480788401834, 0.072192315360734, 0.950532152249661} - }}; - - return srgb_to_xyz * x; -} +constexpr rgb::color_space srgb +( + {T{0.64}, T{0.33}}, + {T{0.30}, T{0.60}}, + {T{0.15}, T{0.06}}, + color::illuminant::deg2::d65, + &srgb_eotf, + &srgb_inverse_eotf +); -} // namespace srgb } // namespace color #endif // ANTKEEPER_COLOR_SRGB_HPP diff --git a/src/color/ucs.hpp b/src/color/ucs.hpp index a19819f..7b6048b 100644 --- a/src/color/ucs.hpp +++ b/src/color/ucs.hpp @@ -31,23 +31,14 @@ namespace ucs { * Transforms CIE 1960 UCS chromaticity coordinates into the CIE xyY colorspace. * * @param uv CIE 1960 UCS chromaticity coordinates. - * @param luminance Luminance or `Y` value of the resulting xyY color. + * @param y Luminance or `Y` value of the resulting xyY color. * @return CIE xyY color. */ template -math::vector3 to_xyy(const math::vector2& uv, T luminance = T(1.0)); - -template -math::vector3 to_xyy(const math::vector2& uv, T luminance) +constexpr math::vector3 to_xyy(const math::vector2& uv, T y = T{1}) { - const T inverse_denom = T(1.0) / (T(2.0) * uv[0] - T(8.0) * uv[1] + T(4.0)); - - return math::vector3 - { - (T(3.0) * uv[0]) * inverse_denom, - (T(2.0) * uv[1]) * inverse_denom, - luminance - }; + const T d = T{1} / (T{2} * uv[0] - T{8} * uv[1] + T{4}); + return math::vector3{(T{3} * uv[0]) * d, (T{2} * uv[1]) * d, y}; } } // namespace ucs diff --git a/src/color/xyy.hpp b/src/color/xyy.hpp index f3e9d4d..975ffb2 100644 --- a/src/color/xyy.hpp +++ b/src/color/xyy.hpp @@ -34,7 +34,10 @@ namespace xyy { * @return return Luminance of @p x. */ template -T luminance(const math::vector3& x); +constexpr inline T luminance(const math::vector3& x) +{ + return x[2]; +} /** * Transforms a CIE xyY color into the CIE 1960 UCS colorspace. @@ -43,7 +46,11 @@ T luminance(const math::vector3& x); * @return CIE 1960 UCS color. */ template -math::vector2 to_ucs(const math::vector3& x); +constexpr math::vector2 to_ucs(const math::vector3& x) +{ + const T d = T({1} / (T{-2} * x[0] + T{12} * x[1] + T{3})); + return math::vector2{(T{4} * x[0]) * d, (T{6} * x[1]) * d}; +} /** * Transforms a CIE xyY color into the CIE XYZ colorspace. @@ -52,35 +59,9 @@ math::vector2 to_ucs(const math::vector3& x); * @return CIE XYZ color. */ template -math::vector3 to_xyz(const math::vector3& x); - -template -inline T luminance(const math::vector3& x) -{ - return x[2]; -} - -template -math::vector2 to_ucs(const math::vector3& x) -{ - const T inverse_denom = T(1.0) / (T(-2.0) * x[0] + T(12.0) * x[1] + T(3.0)); - - return math::vector2 - { - (T(4.0) * x[0]) * inverse_denom, - (T(6.0) * x[1]) * inverse_denom - }; -} - -template -math::vector3 to_xyz(const math::vector3& x) +constexpr math::vector3 to_xyz(const math::vector3& x) { - return math::vector3 - { - (x[0] * x[2]) / x[1], - x[2], - ((T(1.0) - x[0] - x[1]) * x[2]) / x[1] - }; + return math::vector3{(x[0] * x[2]) / x[1], x[2], ((T{1} - x[0] - x[1]) * x[2]) / x[1]}; } } // namespace xyy diff --git a/src/color/xyz.hpp b/src/color/xyz.hpp index 47a8108..279f55d 100644 --- a/src/color/xyz.hpp +++ b/src/color/xyz.hpp @@ -25,7 +25,7 @@ namespace color { /** - * Functions which operate in the CIE XYZ colorspace. + * Functions which operate in the CIE XYZ color space. * * @see https://en.wikipedia.org/wiki/CIE_1931_color_space */ @@ -38,22 +38,23 @@ namespace xyz { * @return return Luminance of @p x. */ template -T luminance(const math::vector3& x); +constexpr inline T luminance(const math::vector3& x) +{ + return x[1]; +} /** - * Fitted piecewise gaussian approximation to the CIE 1931 standard observer color matching function. + * Transforms a CIE XYZ color into the CIE xyY color space. * - * @param lambda Wavelength of light, in nanometers. - * @return Matching CIE XYZ color. - * - * @see match_x(T) - * @see match_y(T) - * @see match_z(T) - * - * @see Wyman, C., Sloan, P.J., & Shirley, P. (2013). Simple Analytic Approximations to the CIE XYZ Color Matching Functions. + * @param x CIE XYZ color. + * @return CIE xyY color. */ template -math::vector3 match(T lambda); +constexpr math::vector3 to_xyy(const math::vector3& x) +{ + const T sum = x[0] + x[1] + x[2]; + return math::vector3{x[0] / sum, x[1] / sum, x[1]} +} /** * CIE 1931 standard observer color matching function for the X tristimulus value. @@ -63,98 +64,6 @@ math::vector3 match(T lambda); * * @see match(T) */ -template -T match_x(T lambda); - -/** - * CIE 1931 standard observer color matching function for the Y tristimulus value. - * - * @param lambda Wavelength of light, in nanometers. - * @return Matching Y tristimulus value. - * - * @see match(T) - */ -template -T match_y(T lambda); - -/** - * CIE 1931 standard observer color matching function for the Z tristimulus value. - * - * @param lambda Wavelength of light, in nanometers. - * @return Matching Z tristimulus value. - * - * @see match(T) - */ -template -T match_z(T lambda); - -/** - * Transforms a CIE XYZ color into the ACEScg colorspace. - * - * @param x CIE XYZ color. - * @return ACEScg color. - */ -template -math::vector3 to_acescg(const math::vector3& x); - -/** - * Transforms a CIE XYZ color into the linear sRGB colorspace. - * - * @param x CIE XYZ color. - * @return Linear sRGB color. - */ -template -math::vector3 to_srgb(const math::vector3& x); - -/** - * Transforms a CIE XYZ color into the CIE xyY colorspace. - * - * @param x CIE XYZ color. - * @return CIE xyY color. - */ -template -math::vector3 to_xyy(const math::vector3& x); - -/// Chromatic Adaptation Transforms (CATs). -namespace cat { - - /** - * Transforms a CIE XYZ color with an ACES (~D60) illuminant to a CIE XYZ color with a D65 illuminant using the Bradford chromatic adaption transform. - * - * @param x CIE XYZ color with an ACES (~D60) illuminant. - * @return CIE XYZ color with a D65 illuminant. - */ - template - math::vector3 aces_to_d65(const math::vector3& x); - - /** - * Transforms a CIE XYZ color with a D65 illuminant to a CIE XYZ color with an ACES (~D60) illuminant using the Bradford chromatic adaption transform. - * - * @param x CIE XYZ color with a D65 illuminant. - * @return CIE XYZ color with an ACES (~D60) illuminant. - */ - template - math::vector3 d65_to_aces(const math::vector3& x); - -} // namespace cat - -template -inline T luminance(const math::vector3& x) -{ - return x[1]; -} - -template -math::vector3 match(T lambda) -{ - return math::vector3 - { - match_x(lambda), - match_y(lambda), - match_z(lambda) - }; -} - template T match_x(T lambda) { @@ -169,6 +78,14 @@ T match_x(T lambda) return x0 + x1 + x2; } +/** + * CIE 1931 standard observer color matching function for the Y tristimulus value. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching Y tristimulus value. + * + * @see match(T) + */ template T match_y(T lambda) { @@ -181,6 +98,14 @@ T match_y(T lambda) return y0 + y1; } +/** + * CIE 1931 standard observer color matching function for the Z tristimulus value. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching Z tristimulus value. + * + * @see match(T) + */ template T match_z(T lambda) { @@ -193,74 +118,28 @@ T match_z(T lambda) return z0 + z1; } +/** + * Fitted piecewise gaussian approximation to the CIE 1931 standard observer color matching function. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching CIE XYZ color. + * + * @see match_x(T) + * @see match_y(T) + * @see match_z(T) + * + * @see Wyman, C., Sloan, P.J., & Shirley, P. (2013). Simple Analytic Approximations to the CIE XYZ Color Matching Functions. + */ template -math::vector3 to_acescg(const math::vector3& x) -{ - static const math::matrix3 xyz_to_acescg - {{ - { 1.641023379694326, -0.663662858722983, 0.011721894328375}, - {-0.324803294184790, 1.615331591657338, -0.008284441996237}, - {-0.236424695237612, 0.016756347685530, 0.988394858539022} - }}; - - return xyz_to_acescg * x; -} - -template -math::vector3 to_srgb(const math::vector3& x) -{ - static const math::matrix3 xyz_to_srgb - {{ - { 3.240969941904523, -0.969243636280880, 0.055630079696994}, - {-1.537383177570094, 1.875967501507721, -0.203976958888977}, - {-0.498610760293003, 0.041555057407176, 1.056971514242879} - }}; - - return xyz_to_srgb * x; -} - -template -math::vector3 to_xyy(const math::vector3& x) +math::vector3 match(T lambda) { - const T sum = x[0] + x[1] + x[2]; - return math::vector3 - { - x[0] / sum - x[1] / sum, - x[1] - }; -} - -namespace cat { - - template - math::vector3 aces_to_d65(const math::vector3& x) { - static const math::matrix3 cat_aces_to_d65 - {{ - { 0.987225397551079, -0.007603864790602, 0.003066041131217}, - {-0.006114800968213, 1.001874800208719, -0.005084242870792}, - { 0.015926393295811, 0.005322023893623, 1.081519155692042} - }}; - - return cat_aces_to_d65 * x; - } - - template - math::vector3 d65_to_aces(const math::vector3& x) - { - static const math::matrix3 cat_d65_to_aces - {{ - { 1.013033366661459, 0.007703617525351, -0.002835673220262}, - { 0.006107049021859, 0.998150224406968, 0.004675010768250}, - {-0.014947927269674, -0.005025218608126, 0.924644077051100} - }}; - - return cat_d65_to_aces * x; - } - -} // namespace cat + match_x(lambda), + match_y(lambda), + match_z(lambda) + }; +} } // namespace xyz } // namespace color diff --git a/src/config.hpp.in b/src/config.hpp.in index f97f258..a02bd2a 100644 --- a/src/config.hpp.in +++ b/src/config.hpp.in @@ -44,7 +44,7 @@ constexpr float menu_fade_out_duration = 0.125f; constexpr float menu_mouseover_padding = 0.1f; /// Opacity of the menu background. -constexpr float menu_bg_opacity = 2.0f / 3.0f; +constexpr float menu_bg_opacity = 2.0f / 4.0f; /// RGBA color of active menu items. constexpr float4 menu_active_color{1.0f, 1.0f, 1.0f, 1.0f}; @@ -66,7 +66,7 @@ constexpr float nuptial_flight_fade_in_duration = 5.0f; #define MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT 1 #define MATERIAL_PASS_MAX_POINT_LIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 2 +#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 3 #define MATERIAL_PASS_MAX_SPOTLIGHT_COUNT 1 #define MATERIAL_PASS_MAX_BONE_COUNT 64 #define TERRAIN_PATCH_SIZE 200.0f diff --git a/src/entity/components/atmosphere.hpp b/src/entity/components/atmosphere.hpp index 50e28ca..340b21f 100644 --- a/src/entity/components/atmosphere.hpp +++ b/src/entity/components/atmosphere.hpp @@ -34,32 +34,35 @@ struct atmosphere /// Index of refraction of air at sea level. double index_of_refraction; - /// Molar concentration of air at sea level, in mol/m-3. - double air_concentration; + /// Molar concentration of Rayleigh particles at sea level, in mol/m-3. + double rayleigh_concentration; /// Scale height of the exponential distribution of Rayleigh particles, in meters. double rayleigh_scale_height; - /// Molecular density of Rayleigh particles at sea level. - double rayleigh_density; - /// (Dependent) Rayleigh scattering coefficients. double3 rayleigh_scattering; + /// Molar concentration of Mie particles at sea level, in mol/m-3. + double mie_concentration; + /// Scale height of the exponential distribution of Mie particles, in meters. double mie_scale_height; - /// Molecular density of Mie particles at sea level. - double mie_density; + /// Mie phase function anisotropy factor. + double mie_anisotropy; + + /// Mie single-scattering albedo. + double mie_albedo; /// (Dependent) Mie scattering coefficient. double mie_scattering; - /// (Dependent) Mie absorption coefficient. - double mie_absorption; + /// (Dependent) Mie extinction coefficient. + double mie_extinction; - /// Mie phase function anisotropy factor. - double mie_anisotropy; + /// Concentration of ozone in the atmosphere, unitless. + double ozone_concentration; /// Elevation of the lower limit of the triangular distribution of ozone particles, in meters. double ozone_lower_limit; @@ -70,14 +73,11 @@ struct atmosphere /// Elevation of the mode of the triangular distribution of ozone particles, in meters. double ozone_mode; - /// Concentration of ozone in the atmosphere, unitless. - double ozone_concentration; - /// (Dependent) Ozone absorption coefficients. double3 ozone_absorption; - /// Airglow, in lux. - double airglow; + /// Airglow illuminance, in lux. + double3 airglow_illuminance; }; } // namespace component diff --git a/src/entity/components/observer.hpp b/src/entity/components/observer.hpp index 8b8bff3..aadb678 100644 --- a/src/entity/components/observer.hpp +++ b/src/entity/components/observer.hpp @@ -21,20 +21,26 @@ #define ANTKEEPER_ENTITY_COMPONENT_OBSERVER_HPP #include "entity/id.hpp" -#include "utility/fundamental-types.hpp" -#include "scene/camera.hpp" namespace entity { namespace component { -/// Observer +/** + * + */ struct observer { + /// Entity ID of a celestial body to which the observer position is relative. entity::id reference_body_eid; + + /// Elevation of the observer, in radians. double elevation; + + /// Latitude of the observer, in radians. double latitude; + + /// Longitude of the observer, in radians. double longitude; - scene::camera* camera; }; } // namespace component diff --git a/src/entity/systems/astronomy.cpp b/src/entity/systems/astronomy.cpp index 1f92388..9085e2e 100644 --- a/src/entity/systems/astronomy.cpp +++ b/src/entity/systems/astronomy.cpp @@ -27,6 +27,7 @@ #include "color/color.hpp" #include "physics/orbit/orbit.hpp" #include "physics/time/ut1.hpp" +#include "physics/time/jd.hpp" #include "physics/light/photometry.hpp" #include "physics/light/luminosity.hpp" #include "physics/light/refraction.hpp" @@ -40,107 +41,107 @@ namespace entity { namespace system { -template -math::vector3 transmittance(T depth_r, T depth_m, T depth_o, const math::vector3& extinction_r, T extinction_m, const math::vector3& extinction_o) -{ - math::vector3 transmittance_r = extinction_r * depth_r; - math::vector3 transmittance_m = math::vector3{extinction_m, extinction_m, extinction_m} * depth_m; - math::vector3 transmittance_o = extinction_o * depth_o; - - math::vector3 t = transmittance_r + transmittance_m + transmittance_o; - t.x = std::exp(-t.x); - t.y = std::exp(-t.y); - t.z = std::exp(-t.z); - - return t; -} - astronomy::astronomy(entity::registry& registry): updatable(registry), - time(0.0), + time_days(0.0), + time_centuries(0.0), time_scale(1.0), - reference_entity(entt::null), - observer_location{0, 0, 0}, + observer_eid(entt::null), + reference_body_eid(entt::null), + transmittance_samples(0), sun_light(nullptr), sky_light(nullptr), moon_light(nullptr), - camera(nullptr), + bounce_light(nullptr), + bounce_albedo{0, 0, 0}, sky_pass(nullptr), - exposure_offset(0.0), - starlight_illuminance(0.0) + starlight_illuminance{0, 0, 0} { - // Construct transformation which transforms coordinates from ENU to EUS + // Construct ENU to EUS transformation enu_to_eus = math::transformation::se3 { {0, 0, 0}, math::quaternion::rotate_x(-math::half_pi) }; - registry.on_construct().connect<&astronomy::on_celestial_body_construct>(this); - registry.on_replace().connect<&astronomy::on_celestial_body_replace>(this); + registry.on_construct().connect<&astronomy::on_observer_modified>(this); + registry.on_replace().connect<&astronomy::on_observer_modified>(this); + registry.on_destroy().connect<&astronomy::on_observer_destroyed>(this); + registry.on_construct().connect<&astronomy::on_celestial_body_modified>(this); + registry.on_replace().connect<&astronomy::on_celestial_body_modified>(this); + registry.on_destroy().connect<&astronomy::on_celestial_body_destroyed>(this); + registry.on_construct().connect<&astronomy::on_orbit_modified>(this); + registry.on_replace().connect<&astronomy::on_orbit_modified>(this); + registry.on_destroy().connect<&astronomy::on_orbit_destroyed>(this); + registry.on_construct().connect<&astronomy::on_atmosphere_modified>(this); + registry.on_replace().connect<&astronomy::on_atmosphere_modified>(this); + registry.on_destroy().connect<&astronomy::on_atmosphere_destroyed>(this); } astronomy::~astronomy() { - registry.on_construct().disconnect<&astronomy::on_celestial_body_construct>(this); - registry.on_replace().disconnect<&astronomy::on_celestial_body_replace>(this); + registry.on_construct().disconnect<&astronomy::on_observer_modified>(this); + registry.on_replace().disconnect<&astronomy::on_observer_modified>(this); + registry.on_destroy().disconnect<&astronomy::on_observer_destroyed>(this); + registry.on_construct().disconnect<&astronomy::on_celestial_body_modified>(this); + registry.on_replace().disconnect<&astronomy::on_celestial_body_modified>(this); + registry.on_destroy().disconnect<&astronomy::on_celestial_body_destroyed>(this); + registry.on_construct().disconnect<&astronomy::on_orbit_modified>(this); + registry.on_replace().disconnect<&astronomy::on_orbit_modified>(this); + registry.on_destroy().disconnect<&astronomy::on_orbit_destroyed>(this); + registry.on_construct().disconnect<&astronomy::on_atmosphere_modified>(this); + registry.on_replace().disconnect<&astronomy::on_atmosphere_modified>(this); + registry.on_destroy().disconnect<&astronomy::on_atmosphere_destroyed>(this); } void astronomy::update(double t, double dt) { - double total_illuminance = 0.0; - double sky_light_illuminance = 0.0; + double3 sky_light_illuminance = {0.0, 0.0, 0.0}; // Add scaled timestep to current time - set_time(time + dt * time_scale); + set_time(time_days + dt * time_scale); - // Abort if no reference body - if (reference_entity == entt::null) + // Abort if no valid observer entity or reference body entity + if (observer_eid == entt::null || reference_body_eid == entt::null) return; - - // Abort if either reference body or orbit have not been set - if (!registry.has(reference_entity) || !registry.has(reference_entity)) - return; - const entity::component::orbit& reference_orbit = registry.get(reference_entity); - const entity::component::celestial_body& reference_body = registry.get(reference_entity); - math::transformation::se3 icrf_to_bci{{-reference_orbit.position}, math::quaternion::identity}; + // Get pointer to observer component + const auto observer = registry.try_get(observer_eid); - const double days_from_epoch = time; - const double centuries_from_epoch = days_from_epoch / 36525.0; + // Abort if no observer component + if (!observer) + return; - // Evaluate reference body orientation polynomials - const double reference_body_pole_ra = math::polynomial::horner(reference_body.pole_ra.begin(), reference_body.pole_ra.end(), centuries_from_epoch); - const double reference_body_pole_dec = math::polynomial::horner(reference_body.pole_dec.begin(), reference_body.pole_dec.end(), centuries_from_epoch); - const double reference_body_prime_meridian = math::polynomial::horner(reference_body.prime_meridian.begin(), reference_body.prime_meridian.end(), days_from_epoch); + // Get pointers to reference body components + const auto + [ + reference_body, + reference_orbit, + reference_atmosphere + ] = registry.try_get(reference_body_eid); - // Construct transformation from the ICRF frame to the reference body BCBF frame - icrf_to_bcbf = physics::orbit::frame::bci::to_bcbf - ( - reference_body_pole_ra, - reference_body_pole_dec, - reference_body_prime_meridian - ); - icrf_to_bcbf.t = icrf_to_bcbf.r * -reference_orbit.position; + // Abort if no reference body or reference orbit + if (!reference_body || !reference_orbit) + return; - icrf_to_enu = icrf_to_bcbf * bcbf_to_enu; - icrf_to_eus = icrf_to_enu * enu_to_eus; + // Update ICRF to EUS transformation + update_icrf_to_eus(*reference_body, *reference_orbit); // Set the transform component translations of orbiting bodies to their topocentric positions registry.view().each( [&](entity::id entity_id, const auto& body, const auto& orbit, auto& transform) { - // Skip reference entity - if (entity_id == this->reference_entity) + // Skip reference body entity + if (entity_id == reference_body_eid) return; // Transform orbital Cartesian position (r) from the ICRF frame to the EUS frame const double3 r_eus = icrf_to_eus * orbit.position; // Evaluate body orientation polynomials - const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), centuries_from_epoch); - const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), centuries_from_epoch); - const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), days_from_epoch); + const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); + const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); + const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); // Determine body orientation in the ICRF frame math::quaternion rotation_icrf = physics::orbit::frame::bcbf::to_bci @@ -160,133 +161,80 @@ void astronomy::update(double t, double dt) transform.local.rotation = math::type_cast(rotation_eus); transform.local.scale = {1.0f, 1.0f, 1.0f}; } - - /* - if (orbit.parent != entt::null) - { - // RA-DEC - const double3 r_bci = icrf_to_bci * orbit.position; - double3 r_bci_spherical = physics::orbit::frame::bci::spherical(r_bci); - if (r_bci_spherical.z < 0.0) - r_bci_spherical.z += math::two_pi; - const double dec = math::degrees(r_bci_spherical.y); - const double ra = math::degrees(r_bci_spherical.z); - - // AZ-EL - const double3 r_enu = icrf_to_enu * orbit.position; - double3 r_enu_spherical = physics::orbit::frame::enu::spherical(r_enu); - if (r_enu_spherical.z < 0.0) - r_enu_spherical.z += math::two_pi; - const double el = math::degrees(r_enu_spherical.y); - const double az = math::degrees(r_enu_spherical.z); - - std::cout << "t: " << this->time << "; ra: " << ra << "; dec: " << dec << std::endl; - std::cout << "t: " << this->time << "; az: " << az << "; el: " << el << std::endl; - } - */ }); + constexpr double3 bounce_normal = {0, 1, 0}; + double3 bounce_illuminance = {0, 0, 0}; + // Update blackbody lighting registry.view().each( - [&](entity::id entity_id, const auto& body, const auto& orbit, const auto& blackbody) + [&](entity::id entity_id, const auto& blackbody_body, const auto& blackbody_orbit, const auto& blackbody) { - // Calculate blackbody inertial basis - //double3 blackbody_forward_icrf = math::normalize(reference_orbit.icrf_position - orbit.icrf_position); - double3 blackbody_up_icrf = {0, 0, 1}; + // Transform blackbody position from ICRF frame to EUS frame + const double3 blackbody_position_eus = icrf_to_eus * blackbody_orbit.position; - // Transform blackbody ICRF position and basis into EUS frame - double3 blackbody_position_eus = icrf_to_eus * orbit.position; - double3 blackbody_position_enu = icrf_to_enu * orbit.position; - double3 blackbody_forward_eus = math::normalize(-blackbody_position_eus); - double3 blackbody_up_eus = icrf_to_eus.r * blackbody_up_icrf; + // Measure distance and direction, in EUS frame, from observer to blackbody + const double observer_blackbody_distance = math::length(blackbody_position_eus); + const double3 observer_blackbody_direction_eus = blackbody_position_eus / observer_blackbody_distance; - // Calculate distance from observer to blackbody - double blackbody_distance = math::length(blackbody_position_eus) - body.radius; + // Measure blackbody solid angle as seen by observer + const double observer_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, observer_blackbody_distance); + const double observer_blackbody_solid_angle = geom::solid_angle::cone(observer_blackbody_angular_radius); - // Calculate blackbody solid angle - const double blackbody_angular_radius = astro::angular_radius(body.radius, blackbody_distance); - const double blackbody_solid_angle = geom::solid_angle::cone(blackbody_angular_radius); + // Calculate illuminance from blackbody reaching observer + const double3 observer_blackbody_illuminance = blackbody.luminance * observer_blackbody_solid_angle; - // Calculate blackbody illuminance - const double3 blackbody_illuminance = blackbody.luminance * blackbody_solid_angle; - - // Init atmospheric transmittance - double3 atmospheric_transmittance = {1.0, 1.0, 1.0}; - - // Get atmosphere component of reference body (if any) - if (this->registry.has(reference_entity)) + // Calculate illuminance from blackbody reaching observer after atmospheric extinction + double3 observer_blackbody_transmitted_illuminance = observer_blackbody_illuminance; + if (reference_atmosphere) { - const entity::component::atmosphere& reference_atmosphere = registry.get(reference_entity); - - // Altitude of observer in meters - geom::ray sample_ray; - sample_ray.origin = {0, reference_body.radius + observer_location[0], 0}; - sample_ray.direction = math::normalize(blackbody_position_eus); - - geom::sphere exosphere; - exosphere.center = {0, 0, 0}; - exosphere.radius = reference_body.radius + reference_atmosphere.upper_limit; - - auto intersection_result = geom::ray_sphere_intersection(sample_ray, exosphere); + // Construct ray at observer pointing towards the blackbody + const geom::ray ray = {{0, 0, 0}, observer_blackbody_direction_eus}; - if (std::get<0>(intersection_result)) - { - double3 sample_start = sample_ray.origin; - double3 sample_end = sample_ray.extrapolate(std::get<2>(intersection_result)); - - double optical_depth_r = physics::gas::atmosphere::optical_depth_exp(sample_start, sample_end, reference_body.radius, reference_atmosphere.rayleigh_scale_height, 16); - double optical_depth_m = physics::gas::atmosphere::optical_depth_exp(sample_start, sample_end, reference_body.radius, reference_atmosphere.mie_scale_height, 16); - double optical_depth_o = physics::gas::atmosphere::optical_depth_tri(sample_start, sample_end, reference_body.radius, reference_atmosphere.ozone_lower_limit, reference_atmosphere.ozone_upper_limit, reference_atmosphere.ozone_mode, 16); - - atmospheric_transmittance = transmittance(optical_depth_r, optical_depth_m, optical_depth_o, reference_atmosphere.rayleigh_scattering, reference_atmosphere.mie_scattering + reference_atmosphere.mie_absorption, reference_atmosphere.ozone_absorption); - } + // Integrate atmospheric spectral transmittance factor between observer and blackbody + const double3 transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray); - // Add airglow to sky light illuminance - sky_light_illuminance += reference_atmosphere.airglow; + // Attenuate illuminance from blackbody reaching observer by spectral transmittance factor + observer_blackbody_transmitted_illuminance *= transmittance; } - // Blackbody illuminance transmitted through the atmosphere - const double3 transmitted_blackbody_illuminance = blackbody_illuminance * atmospheric_transmittance; - - // Add atmosphere-transmitted blackbody illuminance to total illuminance - total_illuminance += (transmitted_blackbody_illuminance.x + transmitted_blackbody_illuminance.y + transmitted_blackbody_illuminance.z) / 3.0; - // Update sun light if (sun_light != nullptr) { - sun_light->set_translation(math::normalize(math::type_cast(blackbody_position_eus))); + const double3 blackbody_up_eus = icrf_to_eus.r * double3{0, 0, 1}; sun_light->set_rotation ( math::look_rotation ( - math::type_cast(blackbody_forward_eus), + math::type_cast(-observer_blackbody_direction_eus), math::type_cast(blackbody_up_eus) ) ); - sun_light->set_color(math::type_cast(transmitted_blackbody_illuminance)); + sun_light->set_color(math::type_cast(observer_blackbody_transmitted_illuminance)); + + // Bounce sun light + bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_blackbody_direction_eus)) * observer_blackbody_transmitted_illuminance * bounce_albedo; } // Update sky light if (sky_light != nullptr) { // Calculate sky illuminance - double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(blackbody_position_enu); + double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(enu_to_eus.inverse() * blackbody_position_eus); const double sky_illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y)); // Add sky illuminance to sky light illuminance - sky_light_illuminance += sky_illuminance; + sky_light_illuminance += {sky_illuminance, sky_illuminance, sky_illuminance}; // Add starlight illuminance to sky light illuminance sky_light_illuminance += starlight_illuminance; - // Add sky light illuminance to total illuminance - total_illuminance += sky_light_illuminance; - - //std::cout << "sky light illum: " << sky_light_illuminance << std::endl; - // Update sky light - sky_light->set_color(float3{1.0f, 1.0f, 1.0f} * static_cast(sky_light_illuminance)); + sky_light->set_color(math::type_cast(sky_light_illuminance)); + + // Bounce sky light + bounce_illuminance += sky_light_illuminance * bounce_albedo; } // Upload blackbody params to sky pass @@ -294,143 +242,109 @@ void astronomy::update(double t, double dt) { this->sky_pass->set_sun_position(math::type_cast(blackbody_position_eus)); this->sky_pass->set_sun_luminance(math::type_cast(blackbody.luminance)); - this->sky_pass->set_sun_illuminance(math::type_cast(blackbody_illuminance)); - this->sky_pass->set_sun_angular_radius(static_cast(blackbody_angular_radius)); + this->sky_pass->set_sun_illuminance(math::type_cast(observer_blackbody_illuminance), math::type_cast(observer_blackbody_transmitted_illuminance)); + this->sky_pass->set_sun_angular_radius(static_cast(observer_blackbody_angular_radius)); } - const double3& blackbody_icrf_position = orbit.position; - // Update diffuse reflectors this->registry.view().each( [&](entity::id entity_id, const auto& reflector_body, const auto& reflector_orbit, const auto& reflector, const auto& transform) { - // Calculate distance to blackbody and direction of incoming light - double3 blackbody_light_direction_icrf = reflector_orbit.position - blackbody_icrf_position; - double blackbody_distance = math::length(blackbody_light_direction_icrf); - blackbody_light_direction_icrf = blackbody_light_direction_icrf / blackbody_distance; + // Transform reflector position from ICRF frame to EUS frame + const double3 reflector_position_eus = icrf_to_eus * reflector_orbit.position; + + // Measure distance and direction, in EUS frame, from observer to reflector + const double observer_reflector_distance = math::length(reflector_position_eus); + const double3 observer_reflector_direction_eus = reflector_position_eus / observer_reflector_distance; - // Transform blackbody light direction from the ICRF frame to the EUS frame - double3 blackbody_light_direction_eus = icrf_to_eus.r * blackbody_light_direction_icrf; + // Measure distance and direction, in EUS frame, from reflector to blackbody + double3 reflector_blackbody_direction_eus = blackbody_position_eus - reflector_position_eus; + const double reflector_blackbody_distance = math::length(reflector_blackbody_direction_eus); + reflector_blackbody_direction_eus /= reflector_blackbody_distance; - // Calculate blackbody solid angle - const double blackbody_angular_radius = astro::angular_radius(body.radius, blackbody_distance); - const double blackbody_solid_angle = geom::solid_angle::cone(blackbody_angular_radius); + // Measure blackbody solid angle as seen by reflector + const double reflector_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, reflector_blackbody_distance); + const double reflector_blackbody_solid_angle = geom::solid_angle::cone(reflector_blackbody_angular_radius); - // Calculate blackbody illuminance + // Calculate blackbody illuminance reaching reflector + const double3 reflector_blackbody_illuminance = blackbody.luminance * reflector_blackbody_solid_angle; - double3 view_direction_icrf = reflector_orbit.position - reference_orbit.position; - const double reflector_distance = math::length(view_direction_icrf); - view_direction_icrf = view_direction_icrf / reflector_distance; + // Measure reflector solid angle as seen by observer + const double observer_reflector_angular_radius = astro::angular_radius(reflector_body.radius, observer_reflector_distance); + const double observer_reflector_solid_angle = geom::solid_angle::cone(observer_reflector_angular_radius); - const double3 sunlight_illuminance = blackbody.luminance * blackbody_solid_angle; - const double reflector_angular_radius = astro::angular_radius(reflector_body.radius, reflector_distance); - const double reflector_solid_angle = geom::solid_angle::cone(reflector_angular_radius); - const double reflector_phase_factor = dot(view_direction_icrf, blackbody_light_direction_icrf) * 0.5 + 0.5; + // Determine phase factor of reflector as seen by observer + const double observer_reflector_phase_factor = dot(observer_reflector_direction_eus, -reflector_blackbody_direction_eus) * 0.5 + 0.5; - const double3 planet_luminance = (sunlight_illuminance * reference_body.albedo) / math::pi; - const double planet_angular_radius = astro::angular_radius(reference_body.radius, reflector_distance); - const double planet_solid_angle = geom::solid_angle::cone(planet_angular_radius); - const double planet_phase_factor = math::dot(-view_direction_icrf, math::normalize(reference_orbit.position - blackbody_icrf_position)) * 0.5 + 0.5; - const double3 planetlight_illuminance = planet_luminance * planet_solid_angle * planet_phase_factor; - double3 planetlight_direction_eus = math::normalize(icrf_to_eus.r * view_direction_icrf); + // Measure observer reference body solid angle as seen by reflector + const double reflector_observer_angular_radius = astro::angular_radius(reference_body->radius, observer_reflector_distance); + const double reflector_observer_solid_angle = geom::solid_angle::cone(reflector_observer_angular_radius); - const double3 reflected_sunlight_luminance = (sunlight_illuminance * reflector.albedo) / math::pi; - const double3 reflected_sunlight_illuminance = reflected_sunlight_luminance * reflector_solid_angle * reflector_phase_factor; - const double3 reflected_planetlight_luminance = (planetlight_illuminance * reflector.albedo) / math::pi; - const double3 reflected_planetlight_illuminance = reflected_planetlight_luminance * reflector_solid_angle; + // Determine phase factor of observer reference body as by reflector + const double reflector_observer_phase_factor = dot(-observer_reflector_direction_eus, -observer_blackbody_direction_eus) * 0.5 + 0.5; - /* - std::cout << "reflected sunlight illuminance: " << reflected_sunlight_illuminance << std::endl; - std::cout << "planetlight illuminance: " << planetlight_illuminance << std::endl; - std::cout << "planet luminance: " << planet_luminance << std::endl; - std::cout << "reflected planetlight luminance: " << reflected_planetlight_luminance << std::endl; - std::cout << "reflected planetlight illuminance: " << reflected_planetlight_illuminance << std::endl; - std::cout << "reflector phase: " << reflector_phase_factor << std::endl; - std::cout << "planet phase: " << planet_phase_factor << std::endl; - */ + // Calculate spectral transmittance between observer and reflector factor due to atmospheric extinction + double3 observer_reflector_transmittance = {1, 1, 1}; + if (reference_atmosphere) + { + const geom::ray ray = {{0, 0, 0}, observer_reflector_direction_eus}; + observer_reflector_transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray); + } + // Measure luminance of observer reference body as seen by reflector + const double3 reflector_observer_luminance = observer_blackbody_illuminance * reference_body->albedo * observer_reflector_transmittance * reflector_observer_phase_factor * math::inverse_pi; + + // Measure illuminance from observer reference body reaching reflector + const double3 reflector_observer_illuminance = reflector_observer_luminance * reflector_observer_solid_angle; + + // Measure luminance of reflector as seen by observer + const double3 observer_reflector_luminance = (reflector_blackbody_illuminance * observer_reflector_phase_factor + reflector_observer_illuminance) * reflector.albedo * observer_reflector_transmittance * math::inverse_pi; + + // Measure illuminance from reflector reaching observer + const double3 observer_reflector_illuminance = observer_reflector_luminance * observer_reflector_solid_angle; if (this->sky_pass) { this->sky_pass->set_moon_position(transform.local.translation); this->sky_pass->set_moon_rotation(transform.local.rotation); - this->sky_pass->set_moon_angular_radius(static_cast(reflector_angular_radius)); - this->sky_pass->set_moon_sunlight_direction(math::type_cast(blackbody_light_direction_eus)); - this->sky_pass->set_moon_sunlight_illuminance(math::type_cast(sunlight_illuminance)); - this->sky_pass->set_moon_planetlight_direction(math::type_cast(planetlight_direction_eus)); - this->sky_pass->set_moon_planetlight_illuminance(math::type_cast(planetlight_illuminance)); + this->sky_pass->set_moon_angular_radius(static_cast(observer_reflector_angular_radius)); + this->sky_pass->set_moon_sunlight_direction(math::type_cast(-reflector_blackbody_direction_eus)); + this->sky_pass->set_moon_sunlight_illuminance(math::type_cast(reflector_blackbody_illuminance * observer_reflector_transmittance)); + this->sky_pass->set_moon_planetlight_direction(math::type_cast(observer_reflector_direction_eus)); + this->sky_pass->set_moon_planetlight_illuminance(math::type_cast(reflector_observer_illuminance * observer_reflector_transmittance)); + this->sky_pass->set_moon_illuminance(math::type_cast(observer_reflector_illuminance / observer_reflector_transmittance), math::type_cast(observer_reflector_illuminance)); } if (this->moon_light) { - float3 reflector_up_eus = math::type_cast(icrf_to_eus.r * double3{0, 0, 1}); - - double3 reflected_illuminance = reflected_sunlight_illuminance + reflected_planetlight_illuminance; - //reflected_illuminance *= std::max(0.0, std::sin(transform.local.translation.y)); - - total_illuminance += (reflected_illuminance.x + reflected_illuminance.y + reflected_illuminance.z) / 3.0; + const float3 reflector_up_eus = math::type_cast(icrf_to_eus.r * double3{0, 0, 1}); - this->moon_light->set_color(math::type_cast(reflected_illuminance)); + this->moon_light->set_color(math::type_cast(observer_reflector_illuminance)); this->moon_light->set_rotation ( math::look_rotation ( - math::normalize(-transform.local.translation), + math::type_cast(-observer_reflector_direction_eus), reflector_up_eus ) ); + + // Bounce moon light + bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_reflector_direction_eus)) * observer_reflector_illuminance * bounce_albedo; } - - /* - std::cout << "moon: sun solid angle: " << blackbody_solid_angle << std::endl; - std::cout << "moon: sun illuminance: " << blackbody_illuminance << std::endl; - std::cout << "moon: moon luminance: " << reflector_luminance << std::endl; - std::cout << "sun brightness: " << sun_brightness << std::endl; - std::cout << "vega brightness: " << vega_brightness << std::endl; - std::cout << "earth: moon distance: " << reflector_distance << std::endl; - std::cout << "earth: moon solid angle: " << reflector_solid_angle << std::endl; - std::cout << "earth: moon phase: " << reflector_phase << std::endl; - std::cout << "earth: moon phase angle: " << math::degrees(reflector_phase_angle) << std::endl; - std::cout << "earth: moon illum %: " << reflector_illumination_factor * 100.0 << std::endl; - std::cout << "earth: moon illuminance: " << reflector_illuminance << std::endl; - std::cout << "earth: moon phase-modulated illuminance: " << reflector_illuminance * reflector_illumination_factor << std::endl; - */ }); }); - // Update sky pass topocentric frame - if (sky_pass != nullptr) + if (bounce_light) { - // Upload topocentric frame to sky pass - sky_pass->set_icrf_to_eus - ( - math::transformation::se3 - { - math::type_cast(icrf_to_eus.t), - math::type_cast(icrf_to_eus.r) - } - ); - - // Upload observer elevation to sky pass - sky_pass->set_observer_elevation(observer_location[0]); - - sky_pass->set_planet_radius(static_cast(reference_body.radius)); - } - - // Auto-exposure - if (camera) - { - const double calibration = 250.0; - const double ev100 = std::log2((total_illuminance * 100.0) / calibration); - //std::cout << "LUX: " << total_illuminance << std::endl; - //std::cout << "EV100: " << ev100 << std::endl; - //camera->set_exposure(exposure_offset); + bounce_light->set_color(math::type_cast(bounce_illuminance)); } } -void astronomy::set_time(double time) +void astronomy::set_time(double t) { - this->time = time; + time_days = t; + time_centuries = time_days * physics::time::jd::centuries_per_day; } void astronomy::set_time_scale(double scale) @@ -438,16 +352,22 @@ void astronomy::set_time_scale(double scale) time_scale = scale; } -void astronomy::set_reference_body(entity::id entity_id) +void astronomy::set_observer(entity::id eid) { - reference_entity = entity_id; - update_bcbf_to_enu(); + if (observer_eid != eid) + { + observer_eid = eid; + + if (observer_eid != entt::null) + observer_modified(); + else + reference_body_eid = entt::null; + } } -void astronomy::set_observer_location(const double3& location) +void astronomy::set_transmittance_samples(std::size_t samples) { - observer_location = location; - update_bcbf_to_enu(); + transmittance_samples = samples; } void astronomy::set_sun_light(scene::directional_light* light) @@ -465,17 +385,17 @@ void astronomy::set_moon_light(scene::directional_light* light) moon_light = light; } -void astronomy::set_camera(scene::camera* camera) +void astronomy::set_bounce_light(scene::directional_light* light) { - this->camera = camera; + bounce_light = light; } -void astronomy::set_exposure_offset(float offset) +void astronomy::set_bounce_albedo(const double3& albedo) { - exposure_offset = offset; + bounce_albedo = albedo; } -void astronomy::set_starlight_illuminance(double illuminance) +void astronomy::set_starlight_illuminance(const double3& illuminance) { starlight_illuminance = illuminance; } @@ -483,37 +403,225 @@ void astronomy::set_starlight_illuminance(double illuminance) void astronomy::set_sky_pass(::render::sky_pass* pass) { this->sky_pass = pass; + + if (sky_pass) + { + if (observer_eid != entt::null) + { + // Get pointer to observer + const auto observer = registry.try_get(reference_body_eid); + + sky_pass->set_observer_elevation(static_cast(observer->elevation)); + } + + if (reference_body_eid != entt::null) + { + // Get pointer to reference celestial body + const auto reference_body = registry.try_get(reference_body_eid); + + if (reference_body) + sky_pass->set_planet_radius(static_cast(reference_body->radius)); + else + sky_pass->set_planet_radius(0.0f); + } + } } -void astronomy::on_celestial_body_construct(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body) +void astronomy::on_observer_modified(entity::registry& registry, entity::id entity_id, entity::component::observer& component) { - if (entity_id == reference_entity) - update_bcbf_to_enu(); + if (entity_id == observer_eid) + observer_modified(); } -void astronomy::on_celestial_body_replace(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body) +void astronomy::on_observer_destroyed(entity::registry& registry, entity::id entity_id) { - if (entity_id == reference_entity) - update_bcbf_to_enu(); + if (entity_id == observer_eid) + observer_modified(); } -void astronomy::update_bcbf_to_enu() +void astronomy::on_celestial_body_modified(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& component) { - double radial_distance = observer_location[0]; + if (entity_id == reference_body_eid) + reference_body_modified(); +} + +void astronomy::on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_body_modified(); +} + +void astronomy::on_orbit_modified(entity::registry& registry, entity::id entity_id, entity::component::orbit& component) +{ + if (entity_id == reference_body_eid) + reference_orbit_modified(); +} + +void astronomy::on_orbit_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_orbit_modified(); +} + +void astronomy::on_atmosphere_modified(entity::registry& registry, entity::id entity_id, entity::component::atmosphere& component) +{ + if (entity_id == reference_body_eid) + reference_atmosphere_modified(); +} + +void astronomy::on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_atmosphere_modified(); +} + +void astronomy::observer_modified() +{ + // Get pointer to observer component + const auto observer = registry.try_get(observer_eid); - if (reference_entity) + if (observer) { - if (registry.has(reference_entity)) - radial_distance += registry.get(reference_entity).radius; + if (reference_body_eid != observer->reference_body_eid) + { + // Reference body changed + reference_body_eid = observer->reference_body_eid; + reference_body_modified(); + reference_orbit_modified(); + reference_atmosphere_modified(); + } + + if (reference_body_eid != entt::null) + { + // Get pointer to reference celestial body + const auto reference_body = registry.try_get(reference_body_eid); + + // Update BCBF to EUS transformation + if (reference_body) + update_bcbf_to_eus(*observer, *reference_body); + } + + // Upload observer elevation to sky pass + if (sky_pass) + sky_pass->set_observer_elevation(static_cast(observer->elevation)); + } +} + +void astronomy::reference_body_modified() +{ + // Get pointer to reference celestial body + const auto reference_body = registry.try_get(reference_body_eid); + + if (reference_body) + { + // Get pointer to observer + const auto observer = registry.try_get(observer_eid); + + // Update BCBF to EUS transformation + if (observer) + update_bcbf_to_eus(*observer, *reference_body); } - // Construct reference frame which transforms coordinates from a BCBF frame to a horizontal frame - bcbf_to_enu = physics::orbit::frame::bcbf::to_enu + // Update reference celestial body-related sky pass parameters + if (sky_pass) + { + if (reference_body) + sky_pass->set_planet_radius(static_cast(reference_body->radius)); + else + sky_pass->set_planet_radius(0.0f); + } +} + +void astronomy::reference_orbit_modified() +{ + +} + +void astronomy::reference_atmosphere_modified() +{ + +} + +void astronomy::update_bcbf_to_eus(const entity::component::observer& observer, const entity::component::celestial_body& body) +{ + // Construct BCBF to EUS transformation + bcbf_to_eus = physics::orbit::frame::bcbf::to_enu ( - radial_distance, - observer_location[1], - observer_location[2] + body.radius + observer.elevation, + observer.latitude, + observer.longitude + ) * enu_to_eus; +} + +void astronomy::update_icrf_to_eus(const entity::component::celestial_body& body, const entity::component::orbit& orbit) +{ + // Evaluate reference body orientation polynomials + const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); + const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); + const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); + + // Construct ICRF frame to BCBF transformation + math::transformation::se3 icrf_to_bcbf = physics::orbit::frame::bci::to_bcbf + ( + body_pole_ra, + body_pole_dec, + body_prime_meridian ); + icrf_to_bcbf.t = icrf_to_bcbf.r * -orbit.position; + + /// Construct ICRF to EUS transformation + icrf_to_eus = icrf_to_bcbf * bcbf_to_eus; + + // Pass ICRF to EUS transformation to sky pass + if (sky_pass) + { + // Upload topocentric frame to sky pass + sky_pass->set_icrf_to_eus + ( + math::transformation::se3 + { + math::type_cast(icrf_to_eus.t), + math::type_cast(icrf_to_eus.r) + } + ); + } +} + +double3 astronomy::integrate_transmittance(const entity::component::observer& observer, const entity::component::celestial_body& body, const entity::component::atmosphere& atmosphere, geom::ray ray) const +{ + double3 transmittance = {1, 1, 1}; + + // Make ray height relative to center of reference body + ray.origin.y += body.radius + observer.elevation; + + // Construct sphere representing upper limit of the atmosphere + geom::sphere atmosphere_sphere; + atmosphere_sphere.center = {0, 0, 0}; + atmosphere_sphere.radius = body.radius + atmosphere.upper_limit; + + // Check for intersection between the ray and atmosphere + auto intersection = geom::ray_sphere_intersection(ray, atmosphere_sphere); + if (std::get<0>(intersection)) + { + // Get point of intersection + const double3 intersection_point = ray.extrapolate(std::get<2>(intersection)); + + // Integrate optical of Rayleigh, Mie, and ozone particles + const double optical_depth_r = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.rayleigh_scale_height, transmittance_samples); + const double optical_depth_m = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.mie_scale_height, transmittance_samples); + const double optical_depth_o = physics::gas::atmosphere::optical_depth_tri(ray.origin, intersection_point, body.radius, atmosphere.ozone_lower_limit, atmosphere.ozone_upper_limit, atmosphere.ozone_mode, transmittance_samples); + + // Calculate transmittance factor due to scattering and absorption + const double3 extinction_r = atmosphere.rayleigh_scattering * optical_depth_r; + const double extinction_m = atmosphere.mie_extinction * optical_depth_m; + const double3 extinction_o = atmosphere.ozone_absorption * optical_depth_o; + transmittance = extinction_r + double3{extinction_m, extinction_m, extinction_m} + extinction_o; + transmittance.x = std::exp(-transmittance.x); + transmittance.y = std::exp(-transmittance.y); + transmittance.z = std::exp(-transmittance.z); + } + + return transmittance; } } // namespace system diff --git a/src/entity/systems/astronomy.hpp b/src/entity/systems/astronomy.hpp index 83599f4..db08a94 100644 --- a/src/entity/systems/astronomy.hpp +++ b/src/entity/systems/astronomy.hpp @@ -24,19 +24,20 @@ #include "entity/id.hpp" #include "scene/directional-light.hpp" #include "scene/ambient-light.hpp" -#include "scene/camera.hpp" #include "utility/fundamental-types.hpp" #include "math/se3.hpp" #include "render/passes/sky-pass.hpp" +#include "entity/components/observer.hpp" #include "entity/components/atmosphere.hpp" #include "entity/components/celestial-body.hpp" #include "entity/components/orbit.hpp" +#include "geom/ray.hpp" namespace entity { namespace system { /** - * Calculates apparent properties of celestial bodies relative to an observer. + * Calculates apparent properties of celestial bodies as seen by an observer. */ class astronomy: public updatable @@ -46,7 +47,7 @@ public: ~astronomy(); /** - * Scales then adds the timestep `dt` to the current time, then recalculates the positions of celestial bodies. + * Adds the timestep `dt`, scaled by set time scale, to the current time, then calculates apparent properties of celestial bodies as seen by an observer. * * @param t Time, in seconds. * @param dt Delta time, in seconds. @@ -56,9 +57,9 @@ public: /** * Sets the current time. * - * @param time Time, in days. + * @param t Time since epoch, in days. */ - void set_time(double time); + void set_time(double t); /** * Sets the factor by which the timestep `dt` will be scaled before being added to the current time. @@ -68,57 +69,99 @@ public: void set_time_scale(double scale); /** - * Sets the reference body entity, from which observations are taking place. + * Sets the observer entity. * - * @param entity Entity of the reference body. + * @param eid Entity ID of the observer. */ - void set_reference_body(entity::id entity_id); + void set_observer(entity::id eid); /** - * Sets the location of the observer using spherical coordinates in BCBF space. + * Sets the number of samples to take when integrating atmospheric transmittance. * - * @param location Spherical coordinates of the observer, in reference body BCBF space, in the ISO order of altitude (meters), latitude (radians), and longitude (radians). + * @param samples Number of integration samples. */ - void set_observer_location(const double3& location); + void set_transmittance_samples(std::size_t samples); void set_sun_light(scene::directional_light* light); void set_sky_light(scene::ambient_light* light); void set_moon_light(scene::directional_light* light); - void set_camera(scene::camera* camera); - void set_exposure_offset(float offset); + void set_bounce_light(scene::directional_light* light); + void set_bounce_albedo(const double3& albedo); + void set_starlight_illuminance(const double3& illuminance); + void set_sky_pass(::render::sky_pass* pass); - void set_starlight_illuminance(double illuminance); +private: + void on_observer_modified(entity::registry& registry, entity::id entity_id, entity::component::observer& component); + void on_observer_destroyed(entity::registry& registry, entity::id entity_id); + void on_celestial_body_modified(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& component); + void on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id); + void on_orbit_modified(entity::registry& registry, entity::id entity_id, entity::component::orbit& component); + void on_orbit_destroyed(entity::registry& registry, entity::id entity_id); + void on_atmosphere_modified(entity::registry& registry, entity::id entity_id, entity::component::atmosphere& component); + void on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id); - inline float get_exposure_offset() const { return exposure_offset; }; + /// Called each time the observer is modified. + void observer_modified(); - void set_sky_pass(::render::sky_pass* pass); + /// Called each time the celestial body of the reference body is modified. + void reference_body_modified(); -private: - void on_celestial_body_construct(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body); - void on_celestial_body_replace(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body); + /// Called each time the orbit of the reference body is modified. + void reference_orbit_modified(); - void update_bcbf_to_enu(); + /// Called each time the atmosphere of the reference body is modified. + void reference_atmosphere_modified(); - double time; + /// Updates the BCBF to EUS transformation. + void update_bcbf_to_eus(const entity::component::observer& observer, const entity::component::celestial_body& body); + + /// Updates the ICRF to EUS transformation. + void update_icrf_to_eus(const entity::component::celestial_body& body, const entity::component::orbit& orbit); + + /** + * Integrates a transmittance factor due to atmospheric extinction along a ray. + * + * @param ray Ray to cast, in the EUS frame. + * @param samples Number of samples to integrate. + * + * @return Spectral transmittance factor. + */ + double3 integrate_transmittance(const entity::component::observer& observer, const entity::component::celestial_body& body, const entity::component::atmosphere& atmosphere, geom::ray ray) const; + + /// Time since epoch, in days. + double time_days; + + /// Time since epoch, in centuries. + double time_centuries; + + /// Time scale. double time_scale; - entity::id reference_entity; + /// Number of transmittance integration samples. + std::size_t transmittance_samples; + + /// Entity ID of the observer. + entity::id observer_eid; - double3 observer_location; + /// Entity ID of the reference body. + entity::id reference_body_eid; - math::transformation::se3 icrf_to_bcbf; - math::transformation::se3 bcbf_to_enu; - math::transformation::se3 icrf_to_enu; + /// ENU to EUS transformation. math::transformation::se3 enu_to_eus; + + /// BCBF to EUS transformation. + math::transformation::se3 bcbf_to_eus; + + /// ICRF to EUS tranformation. math::transformation::se3 icrf_to_eus; scene::directional_light* sun_light; scene::ambient_light* sky_light; scene::directional_light* moon_light; - scene::camera* camera; + scene::directional_light* bounce_light; + double3 bounce_albedo; ::render::sky_pass* sky_pass; - float exposure_offset; - double starlight_illuminance; + double3 starlight_illuminance; }; } // namespace system diff --git a/src/entity/systems/atmosphere.cpp b/src/entity/systems/atmosphere.cpp index 78ac0e1..4a16002 100644 --- a/src/entity/systems/atmosphere.cpp +++ b/src/entity/systems/atmosphere.cpp @@ -21,7 +21,6 @@ #include "physics/gas/atmosphere.hpp" #include "physics/gas/ozone.hpp" #include "physics/number-density.hpp" -#include "color/srgb.hpp" namespace entity { namespace system { @@ -44,12 +43,15 @@ void atmosphere::update(double t, double dt) void atmosphere::set_rgb_wavelengths(const double3& wavelengths) { rgb_wavelengths = wavelengths; - atmosphere_modified(); -} - -void atmosphere::set_rgb_ozone_cross_sections(const double3& cross_sections) -{ - rgb_ozone_cross_sections = cross_sections; + + // Update ozone cross sections + rgb_ozone_cross_sections = + { + physics::gas::ozone::cross_section_293k(wavelengths.x * 1e9), + physics::gas::ozone::cross_section_293k(wavelengths.y * 1e9), + physics::gas::ozone::cross_section_293k(wavelengths.z * 1e9) + }; + atmosphere_modified(); } @@ -67,37 +69,31 @@ void atmosphere::atmosphere_modified() // Get atmosphere component of the entity entity::component::atmosphere& component = *atmosphere_component; - // Calculate Rayleigh scattering coefficients for sRGB wavelengths - const double rayleigh_polarization = physics::gas::atmosphere::polarization(component.index_of_refraction, component.rayleigh_density); - const double3 rayleigh_scattering_srgb = + // Calculate Rayleigh scattering coefficients + const double rayleigh_density = physics::number_density(component.rayleigh_concentration); + const double rayleigh_polarization = physics::gas::atmosphere::polarization(component.index_of_refraction, rayleigh_density); + component.rayleigh_scattering = { - physics::gas::atmosphere::scattering_rayleigh(rgb_wavelengths.x, component.rayleigh_density, rayleigh_polarization), - physics::gas::atmosphere::scattering_rayleigh(rgb_wavelengths.y, component.rayleigh_density, rayleigh_polarization), - physics::gas::atmosphere::scattering_rayleigh(rgb_wavelengths.z, component.rayleigh_density, rayleigh_polarization) + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x), + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y), + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.z) }; - // Transform Rayleigh scattering coefficients from sRGB to ACEScg - component.rayleigh_scattering = color::srgb::to_acescg(rayleigh_scattering_srgb); - - // Calculate Mie scattering coefficient - const double mie_polarization = physics::gas::atmosphere::polarization(component.index_of_refraction, component.mie_density); - component.mie_scattering = physics::gas::atmosphere::scattering_mie(component.mie_density, mie_polarization); + // Calculate Mie scattering and extinction coefficients + const double mie_density = physics::number_density(component.mie_concentration); + const double mie_polarization = physics::gas::atmosphere::polarization(component.index_of_refraction, mie_density); + component.mie_scattering = physics::gas::atmosphere::scattering(mie_density, mie_polarization); + component.mie_extinction = physics::gas::atmosphere::extinction(component.mie_scattering, component.mie_albedo); - // Calculate Mie absorption coefficient - component.mie_absorption = physics::gas::atmosphere::absorption_mie(component.mie_scattering); - - // Calculate ozone absorption coefficients for sRGB wavelengths - const double air_number_density = physics::number_density(component.air_concentration); - const double3 ozone_absorption_srgb = + // Calculate ozone absorption coefficients + const double ozone_density = physics::number_density(component.ozone_concentration); + component.ozone_absorption = { - physics::gas::ozone::absorption(rgb_ozone_cross_sections.x, air_number_density, component.ozone_concentration), - physics::gas::ozone::absorption(rgb_ozone_cross_sections.y, air_number_density, component.ozone_concentration), - physics::gas::ozone::absorption(rgb_ozone_cross_sections.z, air_number_density, component.ozone_concentration) + physics::gas::ozone::absorption(rgb_ozone_cross_sections.x, ozone_density), + physics::gas::ozone::absorption(rgb_ozone_cross_sections.y, ozone_density), + physics::gas::ozone::absorption(rgb_ozone_cross_sections.z, ozone_density) }; - // Transform ozone absorption coefficients from sRGB to ACEScg - component.ozone_absorption = color::srgb::to_acescg(ozone_absorption_srgb); - // Pass atmosphere parameters to sky pass update_sky_pass(); } @@ -111,8 +107,9 @@ void atmosphere::update_sky_pass() sky_pass->set_atmosphere_upper_limit(static_cast(component.upper_limit)); sky_pass->set_rayleigh_parameters(static_cast(component.rayleigh_scale_height), math::type_cast(component.rayleigh_scattering)); - sky_pass->set_mie_parameters(static_cast(component.mie_scale_height), static_cast(component.mie_scattering), static_cast(component.mie_absorption), static_cast(component.mie_anisotropy)); + sky_pass->set_mie_parameters(static_cast(component.mie_scale_height), static_cast(component.mie_scattering), static_cast(component.mie_extinction), static_cast(component.mie_anisotropy)); sky_pass->set_ozone_parameters(static_cast(component.ozone_lower_limit), static_cast(component.ozone_upper_limit), static_cast(component.ozone_mode), math::type_cast(component.ozone_absorption)); + sky_pass->set_airglow_illuminance(math::type_cast(component.airglow_illuminance)); } void atmosphere::on_atmosphere_construct(entity::registry& registry, entity::id entity_id, entity::component::atmosphere& component) diff --git a/src/entity/systems/atmosphere.hpp b/src/entity/systems/atmosphere.hpp index 1557200..2ca28e7 100644 --- a/src/entity/systems/atmosphere.hpp +++ b/src/entity/systems/atmosphere.hpp @@ -47,13 +47,6 @@ public: */ void set_rgb_wavelengths(const double3& wavelengths); - /** - * Sets ozone cross sections for red, green, and blue wavelengths. - * - * @param cross_sections Vector containing the ozone cross sections of red (x), green (y), and blue (z) light, in m-2/molecule. - */ - void set_rgb_ozone_cross_sections(const double3& cross_sections); - void set_sky_pass(::render::sky_pass* pass); private: diff --git a/src/entity/systems/blackbody.cpp b/src/entity/systems/blackbody.cpp index 5eb4cd4..7ba8fd7 100644 --- a/src/entity/systems/blackbody.cpp +++ b/src/entity/systems/blackbody.cpp @@ -27,7 +27,8 @@ namespace entity { namespace system { blackbody::blackbody(entity::registry& registry): - updatable(registry) + updatable(registry), + illuminant(color::illuminant::deg2::d50) { // Construct a range of sample wavelengths in the visible spectrum visible_wavelengths_nm.resize(780 - 280); @@ -43,6 +44,11 @@ blackbody::blackbody(entity::registry& registry): void blackbody::update(double t, double dt) {} +void blackbody::set_illuminant(const math::vector2& illuminant) +{ + this->illuminant = illuminant; +} + void blackbody::update_luminance(entity::id entity_id) { // Abort if entity has no blackbody component @@ -62,8 +68,11 @@ void blackbody::update_luminance(entity::id entity_id) // Get celestial body component of the entity const component::celestial_body& celestial_body = registry.get(entity_id); + // Construct chromatic adaptation transform + const double3x3 cat = color::cat::matrix(illuminant, color::aces::white_point); + // Construct a lambda function which calculates the blackbody's RGB luminance of a given wavelength - auto rgb_luminance = [blackbody](double wavelength_nm) -> double3 + auto rgb_luminance = [blackbody, cat](double wavelength_nm) -> double3 { // Convert wavelength from nanometers to meters const double wavelength_m = wavelength_nm * 1e-9; @@ -71,8 +80,9 @@ void blackbody::update_luminance(entity::id entity_id) // Calculate the spectral intensity of the wavelength const double spectral_radiance = physics::light::blackbody::spectral_radiance(blackbody.temperature, wavelength_m); + // Calculate the ACEScg color of the wavelength using CIE color matching functions - double3 spectral_color = color::xyz::to_acescg(color::xyz::match(wavelength_nm)); + double3 spectral_color = color::aces::ap1.from_xyz * cat * color::xyz::match(wavelength_nm); // Scale the spectral color by spectral intensity return spectral_color * spectral_radiance * 1e-9 * physics::light::max_luminous_efficacy; diff --git a/src/entity/systems/blackbody.hpp b/src/entity/systems/blackbody.hpp index 7512398..b38eb75 100644 --- a/src/entity/systems/blackbody.hpp +++ b/src/entity/systems/blackbody.hpp @@ -41,6 +41,13 @@ public: virtual void update(double t, double dt); + /** + * Sets the blackbody illuminant. + * + * @param illuminant CIE chromaticity coordinates of an illuminant. + */ + void set_illuminant(const math::vector2& illuminant); + private: void update_luminance(entity::id entity_id); @@ -50,6 +57,7 @@ private: void on_celestial_body_construct(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body); void on_celestial_body_replace(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body); + math::vector2 illuminant; std::vector visible_wavelengths_nm; }; diff --git a/src/entity/systems/terrain.cpp b/src/entity/systems/terrain.cpp index 0eb9ee7..0664242 100644 --- a/src/entity/systems/terrain.cpp +++ b/src/entity/systems/terrain.cpp @@ -72,6 +72,7 @@ terrain::~terrain() void terrain::update(double t, double dt) { + /* // Refine the level of detail of each terrain quadsphere registry.view().each( [&](entity::id terrain_eid, const auto& terrain_component, const auto& terrain_body) @@ -238,6 +239,7 @@ void terrain::update(double t, double dt) } } }); + */ } void terrain::set_patch_subdivisions(std::uint8_t n) diff --git a/src/game/ant/swarm.cpp b/src/game/ant/swarm.cpp new file mode 100644 index 0000000..79b6c6b --- /dev/null +++ b/src/game/ant/swarm.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/ant/swarm.hpp" +#include "entity/components/transform.hpp" +#include "entity/components/steering.hpp" +#include "entity/components/model.hpp" +#include "resources/resource-manager.hpp" +#include "config.hpp" +#include +#include + +namespace game { +namespace ant { + +/** + * Generates a random point in a unit sphere. + * + * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 + */ +template +static math::vector3 sphere_random(Generator& rng) +{ + const std::uniform_real_distribution distribution(T{-1}, T{1}); + + const T x = distribution(rng); + const T y = distribution(rng); + const T z = distribution(rng); + + const T cbrt_u = std::cbrt(distribution(rng)); + + const T scale = (T{1} / std::sqrt(x * x + y * y + z * z)) * cbrt_u; + + return {x * scale, y * scale, z * scale}; +} + +void create_swarm(game::context& ctx) +{ + // Determine swarm properties + const float3 swarm_center = {0, 100, 0}; + const float swarm_radius = 25.0f; + const std::size_t male_count = 50; + const std::size_t queen_count = 50; + const std::size_t alate_count = male_count + queen_count; + + const float3 male_scale = {0.5, 0.5, 0.5}; + const float3 queen_scale = {1, 1, 1}; + + // Init male model component + entity::component::model male_model; + male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); + male_model.instance_count = 0; + male_model.layers = 1; + + // Init queen model component + entity::component::model queen_model; + queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); + queen_model.instance_count = 0; + queen_model.layers = 1; + + // Init transform component + entity::component::transform transform; + transform.local = math::transform::identity; + transform.world = transform.local; + transform.warp = true; + + // Init steering component + entity::component::steering steering; + steering.agent.mass = 1.0f; + steering.agent.velocity = {0, 0, 0}; + steering.agent.acceleration = {0, 0, 0}; + steering.agent.max_force = 4.0f; + steering.agent.max_speed = 5.0f; + steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; + steering.agent.orientation = math::quaternion::identity; + steering.agent.forward = steering.agent.orientation * config::global_forward; + steering.agent.up = steering.agent.orientation * config::global_up; + steering.wander_weight = 1.0f; + steering.wander_noise = math::radians(2000.0f); + steering.wander_distance = 10.0f; + steering.wander_radius = 8.0f; + steering.wander_angle = 0.0f; + steering.wander_angle2 = 0.0f; + steering.seek_weight = 0.2f; + steering.seek_target = {0, 100, 0}; + steering.flee_weight = 0.0f; + steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; + + // Construct and seed random number generator + std::random_device seed; + std::mt19937 rng(seed()); + + // Create alates + for (std::size_t i = 0; i < alate_count; ++i) + { + // Generate random position in swarm sphere + steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; + transform.local.translation = steering.agent.position; + + entity::id alate_eid = ctx.entity_registry->create(); + ctx.entity_registry->assign(alate_eid, steering); + + if (i < male_count) + { + // Create male + ctx.entity_registry->assign(alate_eid, male_model); + + transform.local.scale = male_scale; + transform.world = transform.local; + ctx.entity_registry->assign(alate_eid, transform); + } + else + { + // Create queen + ctx.entity_registry->assign(alate_eid, queen_model); + + transform.local.scale = queen_scale; + transform.world = transform.local; + ctx.entity_registry->assign(alate_eid, transform); + } + } +} + +void destroy_swarm(game::context& ctx) +{ + auto view = ctx.entity_registry->view(); + ctx.entity_registry->destroy(view.begin(), view.end()); +} + +} // namespace ant +} // namespace game diff --git a/src/game/ant/swarm.hpp b/src/game/ant/swarm.hpp new file mode 100644 index 0000000..3b60400 --- /dev/null +++ b/src/game/ant/swarm.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_ANT_SWARM_HPP +#define ANTKEEPER_GAME_ANT_SWARM_HPP + +#include "game/context.hpp" + +namespace game { +namespace ant { + +void create_swarm(game::context& ctx); +void destroy_swarm(game::context& ctx); + +} // namespace ant +} // namespace game + +#endif // ANTKEEPER_GAME_ANT_SWARM_HPP diff --git a/src/game/context.hpp b/src/game/context.hpp index 91c572c..d4e7488 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -295,9 +295,7 @@ struct context entity::system::orbit* orbit_system; entity::system::proteome* proteome_system; - double elevation; - double latitude; - double longitude; + double3 rgb_wavelengths; }; } // namespace game diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 8db64ac..bb127d2 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -660,4 +660,14 @@ void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration) } } +void enable_orbit_controls(game::context& ctx) +{ + +} + +void disable_orbit_controls(game::context& ctx) +{ + +} + } // namespace game diff --git a/src/game/controls.hpp b/src/game/controls.hpp index 37f817c..6479df9 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -87,6 +87,9 @@ bool save_gamepad_calibration(const game::context& ctx, const input::gamepad& ga */ void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration); +void enable_orbit_controls(game::context& ctx); +void disable_orbit_controls(game::context& ctx); + } // namespace game #endif // ANTKEEPER_GAME_CONTROLS_HPP diff --git a/src/game/load.cpp b/src/game/load.cpp index c5d6000..89a60d0 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -86,6 +86,19 @@ void biome(game::context& ctx, const std::filesystem::path& path) { ctx.logger->warning("Biome terrain material undefined"); } + + // Setup lighting + double3 terrain_albedo = {0, 0, 0}; + if (terrain->contains("albedo")) + { + const auto& albedo_element = (*terrain)["albedo"]; + + terrain_albedo[0] = albedo_element[0].get(); + terrain_albedo[1]= albedo_element[1].get(); + terrain_albedo[2] = albedo_element[2].get(); + + } + ctx.astronomy_system->set_bounce_albedo(terrain_albedo); } else { diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 39d33f4..0f88ef3 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -93,7 +93,6 @@ #include "game/graphics.hpp" #include "utility/timestamp.hpp" #include "color/color.hpp" -#include "physics/gas/ozone.hpp" #include #include #include @@ -834,20 +833,6 @@ void boot::setup_systems() const auto& viewport_dimensions = ctx.app->get_viewport_dimensions(); float4 viewport = {0.0f, 0.0f, static_cast(viewport_dimensions[0]), static_cast(viewport_dimensions[1])}; - // RGB wavelengths - const double3 rgb_wavelengths_nm = {680, 550, 440}; - const double3 rgb_wavelengths_m = rgb_wavelengths_nm * 1e-9; - - // Ozone cross sections - double3 rgb_ozone_cross_sections_293k = - { - physics::gas::ozone::cross_section_680nm_293k, - physics::gas::ozone::cross_section_550nm_293k, - physics::gas::ozone::cross_section_440nm_293k - }; - - rgb_ozone_cross_sections_293k = rgb_ozone_cross_sections_293k; - // Setup terrain system ctx.terrain_system = new entity::system::terrain(*ctx.entity_registry); ctx.terrain_system->set_patch_subdivisions(30); @@ -904,15 +889,19 @@ void boot::setup_systems() // Setup blackbody system ctx.blackbody_system = new entity::system::blackbody(*ctx.entity_registry); + ctx.blackbody_system->set_illuminant(color::illuminant::deg2::d55); + + // RGB wavelengths for atmospheric scatteering + ctx.rgb_wavelengths = {645, 575, 440}; // Setup atmosphere system ctx.atmosphere_system = new entity::system::atmosphere(*ctx.entity_registry); - ctx.atmosphere_system->set_rgb_wavelengths(rgb_wavelengths_m); - ctx.atmosphere_system->set_rgb_ozone_cross_sections(rgb_ozone_cross_sections_293k); + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); ctx.atmosphere_system->set_sky_pass(ctx.sky_pass); // Setup astronomy system ctx.astronomy_system = new entity::system::astronomy(*ctx.entity_registry); + ctx.astronomy_system->set_transmittance_samples(16); ctx.astronomy_system->set_sky_pass(ctx.sky_pass); // Setup proteome system diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index 62e67d9..e8a60b1 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -21,8 +21,13 @@ #include "game/state/options-menu.hpp" #include "game/state/extras-menu.hpp" #include "game/state/nuptial-flight.hpp" +#include "game/world.hpp" +#include "game/load.hpp" #include "game/menu.hpp" +#include "game/ant/swarm.hpp" #include "render/passes/clear-pass.hpp" +#include "render/passes/ground-pass.hpp" +#include "render/passes/sky-pass.hpp" #include "resources/resource-manager.hpp" #include "render/model.hpp" #include "animation/animation.hpp" @@ -31,6 +36,10 @@ #include "animation/ease.hpp" #include "application.hpp" #include "config.hpp" +#include "physics/light/exposure.hpp" +#include "entity/components/model.hpp" +#include "entity/components/steering.hpp" +#include "entity/components/transform.hpp" #include namespace game { @@ -233,6 +242,40 @@ main_menu::main_menu(game::context& ctx, bool fade_in): game::menu::fade_in(ctx, nullptr); } + if (ctx.entities.find("earth") == ctx.entities.end()) + { + game::world::cosmogenesis(ctx); + game::world::create_observer(ctx); + game::load::biome(ctx, "debug.bio"); + } + + // Set world time + game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); + + // Set world time scale + game::world::set_time_scale(ctx, 0.0); + + ctx.surface_camera->set_active(true); + const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); + ctx.surface_camera->set_exposure(15.5f); + + ctx.surface_camera->look_at({-55, 36, 10}, {0, 100, 0}, {0, 1, 0}); + ctx.surface_camera->update_tweens(); + + // Setup and enable sky and ground passes + ctx.sky_pass->set_enabled(true); + ctx.ground_pass->set_enabled(true); + + // Disable UI color clear + ctx.ui_clear_pass->set_cleared_buffers(false, true, false); + + // Create mating swarm + game::ant::create_swarm(ctx); + + if (!ctx.menu_bg_billboard->is_active()) + game::menu::fade_in_bg(ctx); + + ctx.logger->pop_task(EXIT_SUCCESS); } @@ -253,6 +296,9 @@ main_menu::~main_menu() // Destruct title text ctx.ui_scene->remove_object(&title_text); + // Destroy swarm + game::ant::destroy_swarm(ctx); + ctx.logger->pop_task(EXIT_SUCCESS); } diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index 3e1cd4d..ce4723c 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -19,9 +19,11 @@ #include "game/state/nuptial-flight.hpp" #include "game/state/pause-menu.hpp" +#include "game/ant/swarm.hpp" #include "entity/archetype.hpp" #include "entity/systems/camera.hpp" #include "entity/systems/astronomy.hpp" +#include "entity/systems/atmosphere.hpp" #include "entity/components/locomotion.hpp" #include "entity/components/transform.hpp" #include "entity/components/terrain.hpp" @@ -45,6 +47,8 @@ #include "game/ant/breed.hpp" #include "game/ant/morphogenesis.hpp" #include "math/interpolation.hpp" +#include "physics/light/exposure.hpp" +#include "color/color.hpp" #include using namespace game::ant; @@ -92,61 +96,20 @@ nuptial_flight::nuptial_flight(game::context& ctx): ctx.ui_clear_pass->set_cleared_buffers(false, true, false); // Create world if not yet created - if (ctx.entities.find("planet") == ctx.entities.end()) + if (ctx.entities.find("earth") == ctx.entities.end()) { // Create cosmos game::world::cosmogenesis(ctx); - // Create boids - for (int i = 0; i < 100; ++i) - { - entity::id boid_eid = ctx.entity_registry->create(); - - // Create transform component - entity::component::transform transform; - transform.local = math::transform::identity; - transform.world = math::transform::identity; - transform.warp = true; - ctx.entity_registry->assign(boid_eid, transform); - - // Create model component - entity::component::model model; - model.render_model = ctx.resource_manager->load("boid.mdl"); - model.instance_count = 0; - model.layers = 1; - ctx.entity_registry->assign(boid_eid, model); - - // Create steering component - entity::component::steering steering; - steering.agent.mass = 1.0f; - steering.agent.position = {0, 100, 0}; - steering.agent.velocity = {0, 0, 0}; - steering.agent.acceleration = {0, 0, 0}; - steering.agent.max_force = 4.0f; - steering.agent.max_speed = 5.0f; - steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; - steering.agent.orientation = math::quaternion::identity; - steering.agent.forward = steering.agent.orientation * config::global_forward; - steering.agent.up = steering.agent.orientation * config::global_up; - steering.wander_weight = 1.0f; - steering.wander_noise = math::radians(2000.0f); - steering.wander_distance = 10.0f; - steering.wander_radius = 8.0f; - steering.wander_angle = 0.0f; - steering.wander_angle2 = 0.0f; - steering.seek_weight = 0.2f; - steering.seek_target = {0, 100, 0}; - steering.flee_weight = 0.0f; - steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; - ctx.entity_registry->assign(boid_eid, steering); - } + // Create observer + game::world::create_observer(ctx); } // Load biome game::load::biome(ctx, "desert-scrub.bio"); // Set world time - game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); + game::world::set_time(ctx, 2022, 10, 9, 12, 0, 0.0); // Set world time scale game::world::set_time_scale(ctx, 60.0); @@ -162,6 +125,13 @@ nuptial_flight::nuptial_flight(game::context& ctx): entity::command::warp_to(*ctx.entity_registry, color_checker_eid, {0, 0, -10}); } + // Create diffuse spheres + { + entity::archetype* diffuse_spheres_archetype = ctx.resource_manager->load("diffuse-spheres.ent"); + auto diffuse_spheres_eid = diffuse_spheres_archetype->create(*ctx.entity_registry); + entity::command::warp_to(*ctx.entity_registry, diffuse_spheres_eid, {0, 0, -20}); + } + // Create ruler { entity::archetype* ruler_10cm_archetype = ctx.resource_manager->load("ruler-10cm.ent"); @@ -169,20 +139,6 @@ nuptial_flight::nuptial_flight(game::context& ctx): entity::command::warp_to(*ctx.entity_registry, ruler_10cm_eid, {0, 0, 10}); } - // Create cocoon - { - entity::archetype* cocoon_archetype = ctx.resource_manager->load("cocoon.ent"); - auto cocoon_eid = cocoon_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, cocoon_eid, {-10, 0, 10}); - } - - // Create larva - { - entity::archetype* larva_archetype = ctx.resource_manager->load("long-neck-larva.ent"); - auto larva_eid = larva_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, larva_eid, {-13, 0, 10}); - } - // Create keeper if not yet created if (ctx.entities.find("keeper") == ctx.entities.end()) { @@ -190,33 +146,6 @@ nuptial_flight::nuptial_flight(game::context& ctx): ctx.entities["keeper"] = keeper_eid; } - // Create ant if not created - if (ctx.entities.find("ant") == ctx.entities.end()) - { - auto boid_eid = ctx.entity_registry->create(); - - entity::component::model model; - model.render_model = worker_model;//ctx.resource_manager->load("ant-test.mdl"); - model.instance_count = 0; - model.layers = 1; - ctx.entity_registry->assign(boid_eid, model); - - entity::component::transform transform; - transform.local = math::transform::identity; - transform.world = math::transform::identity; - transform.warp = true; - ctx.entity_registry->assign(boid_eid, transform); - - entity::component::locomotion locomotion; - locomotion.yaw = 0.0f; - ctx.entity_registry->assign(boid_eid, locomotion); - - entity::command::warp_to(*ctx.entity_registry, boid_eid, {0, 1, 0}); - - // Set target ant - ctx.entities["ant"] = boid_eid; - } - // Start as ant-keeper is_keeper = true; @@ -322,10 +251,12 @@ void nuptial_flight::setup_camera() constraint_stack.head = spring_constraint_eid; ctx.entity_registry->assign(camera_eid, constraint_stack); } - - ctx.surface_camera->set_exposure(15.5f); - ctx.astronomy_system->set_camera(ctx.surface_camera); + game::ant::create_swarm(ctx); + + const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); + const float ev100_looney11 = physics::light::ev::from_settings(11.0f, 1.0f / 100.0f, 100.0f); + ctx.surface_camera->set_exposure(ev100_sunny16); } void nuptial_flight::enable_keeper_controls() @@ -687,6 +618,76 @@ void nuptial_flight::enable_keeper_controls() ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); + + const float wavelength_speed = 20.0 * (1.0 / 60.0); + ctx.controls["dec_red"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.x -= wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + ctx.controls["inc_red"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.x += wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + + ctx.controls["dec_green"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.y -= wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + ctx.controls["inc_green"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.y += wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + + ctx.controls["dec_blue"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.z -= wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + ctx.controls["inc_blue"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.z += wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); } void nuptial_flight::disable_keeper_controls() diff --git a/src/game/tools.cpp b/src/game/tools.cpp index ad80635..14e794e 100644 --- a/src/game/tools.cpp +++ b/src/game/tools.cpp @@ -90,8 +90,8 @@ entity::id build_time_tool(game::context& ctx) // Setup tool active calback tool.active = [&ctx]() { - auto [mouse_x, mouse_y] = ctx.app->get_mouse()->get_current_position(); - auto [window_w, window_h] = ctx.app->get_viewport_dimensions(); + //auto mouse = ctx.app->get_mouse()->get_current_position(); + //auto window = ctx.app->get_viewport_dimensions(); entity::id planet_eid = ctx.entities["planet"]; entity::component::celestial_body body = ctx.entity_registry->get(planet_eid); diff --git a/src/game/world.cpp b/src/game/world.cpp index a463cc6..b9964fe 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -27,6 +27,7 @@ #include "entity/components/orbit.hpp" #include "entity/components/terrain.hpp" #include "entity/components/transform.hpp" +#include "entity/components/observer.hpp" #include "entity/systems/astronomy.hpp" #include "entity/systems/orbit.hpp" #include "entity/commands.hpp" @@ -52,6 +53,7 @@ #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include "render/material-flags.hpp" +#include "geom/solid-angle.hpp" #include "config.hpp" #include @@ -88,15 +90,79 @@ void cosmogenesis(game::context& ctx) ctx.logger->pop_task(EXIT_SUCCESS); } -void set_location(game::context& ctx, double elevation, double latitude, double longitude) +void create_observer(game::context& ctx) { - // Update context location - ctx.elevation = elevation; - ctx.latitude = latitude; - ctx.longitude = longitude; + ctx.logger->push_task("Creating observer"); - // Pass location to astronomy system - ctx.astronomy_system->set_observer_location({elevation, latitude, longitude}); + try + { + // Create observer entity + entity::id observer_eid = ctx.entity_registry->create(); + ctx.entities["observer"] = observer_eid; + + // Construct observer component + entity::component::observer observer; + + // Set observer reference body + if (auto it = ctx.entities.find("earth"); it != ctx.entities.end()) + observer.reference_body_eid = it->second; + else + observer.reference_body_eid = entt::null; + + // Set observer location + observer.elevation = 0.0; + observer.latitude = 0.0; + observer.longitude = 0.0; + + // Assign observer component to observer entity + ctx.entity_registry->assign(observer_eid, observer); + + // Set astronomy system observer + ctx.astronomy_system->set_observer(observer_eid); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + + ctx.logger->pop_task(EXIT_SUCCESS); +} + +void set_location(game::context& ctx, double elevation, double latitude, double longitude) +{ + if (auto it = ctx.entities.find("observer"); it != ctx.entities.end()) + { + entity::id observer_eid = it->second; + + if (observer_eid != entt::null) + { + // Get pointer to observer component + const auto observer = ctx.entity_registry->try_get(observer_eid); + + // Set observer location + if (observer) + { + observer->elevation = elevation; + observer->latitude = latitude; + observer->longitude = longitude; + ctx.entity_registry->replace(observer_eid, *observer); + } + + /* + ctx.entity_registry->patch + ( + observer_eid, + [&](auto& component) + { + component.elevation = elevation; + component.latitude = latitude; + component.longitude = longitude; + } + ); + */ + } + } } void set_time(game::context& ctx, double t) @@ -117,28 +183,36 @@ void set_time(game::context& ctx, double t) void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second) { - const double utc_offset = physics::time::utc::offset(ctx.longitude); + double longitude = 0.0; + + // Get longitude of observer + if (auto it = ctx.entities.find("observer"); it != ctx.entities.end()) + { + entity::id observer_eid = it->second; + if (observer_eid != entt::null) + { + const auto observer = ctx.entity_registry->try_get(observer_eid); + if (observer) + longitude = observer->longitude; + } + } + + // Calculate UTC offset at longitude + const double utc_offset = physics::time::utc::offset(longitude); + + // Convert time from Gregorian to UT1 const double t = physics::time::gregorian::to_ut1(year, month, day, hour, minute, second, utc_offset); + set_time(ctx, t); } void set_time_scale(game::context& ctx, double scale) { - ctx.logger->push_task("Setting time scale to " + std::to_string(scale)); - try - { - // Convert time scale from seconds to days - const double astronomical_scale = scale / physics::time::seconds_per_day; - - ctx.orbit_system->set_time_scale(astronomical_scale); - ctx.astronomy_system->set_time_scale(astronomical_scale); - } - catch (const std::exception&) - { - ctx.logger->pop_task(EXIT_FAILURE); - return; - } - ctx.logger->pop_task(EXIT_SUCCESS); + // Convert time scale from seconds to days + const double astronomical_scale = scale / physics::time::seconds_per_day; + + ctx.orbit_system->set_time_scale(astronomical_scale); + ctx.astronomy_system->set_time_scale(astronomical_scale); } void load_ephemeris(game::context& ctx) @@ -208,7 +282,7 @@ void create_stars(game::context& ctx) float* star_vertex = star_vertex_data; // Init starlight illuminance - double starlight_illuminance = 0.0; + double3 starlight_illuminance = {0, 0, 0}; // Build star catalog vertex data for (std::size_t i = 1; i < star_catalog->size(); ++i) @@ -239,9 +313,6 @@ void create_stars(game::context& ctx) // Convert ICRF coordinates from spherical to Cartesian double3 position = physics::orbit::frame::bci::cartesian(double3{1.0, dec, ra}); - // Convert apparent magnitude to brightness factor relative to a 0th magnitude star - double brightness = physics::light::vmag::to_brightness(vmag); - // Convert color index to color temperature double cct = color::index::bv_to_cct(bv); @@ -249,10 +320,10 @@ void create_stars(game::context& ctx) double3 color_xyz = color::cct::to_xyz(cct); // Transform XYZ color to ACEScg colorspace - double3 color_acescg = color::xyz::to_acescg(color_xyz); + double3 color_acescg = color::aces::ap1.from_xyz * color_xyz; - // Scale color by relative brightness - color_acescg *= brightness; + // Convert apparent magnitude to brightness factor relative to a 0th magnitude star + double brightness = physics::light::vmag::to_brightness(vmag); // Build vertex *(star_vertex++) = static_cast(position.x); @@ -263,8 +334,11 @@ void create_stars(game::context& ctx) *(star_vertex++) = static_cast(color_acescg.z); *(star_vertex++) = static_cast(brightness); - // Convert apparent magnitude to illuminance and add to total starlight - starlight_illuminance += physics::light::vmag::to_illuminance(vmag); + // Calculate spectral illuminance + double3 illuminance = color_acescg * physics::light::vmag::to_illuminance(vmag); + + // Add spectral illuminance to total starlight illuminance + starlight_illuminance += illuminance; } // Unload star catalog @@ -347,14 +421,22 @@ void create_sun(game::context& ctx) sky_light->set_color({0, 0, 0}); sky_light->update_tweens(); + // Create bounce directional light scene object + scene::directional_light* bounce_light = new scene::directional_light(); + bounce_light->set_color({0, 0, 0}); + bounce_light->look_at({0, 0, 0}, {0, 1, 0}, {1, 0, 0}); + bounce_light->update_tweens(); + // Add sun light scene objects to surface scene ctx.surface_scene->add_object(sun_light); ctx.surface_scene->add_object(sky_light); + ctx.surface_scene->add_object(bounce_light); // Pass direct sun light scene object to shadow map pass and astronomy system ctx.surface_shadow_map_pass->set_light(sun_light); ctx.astronomy_system->set_sun_light(sun_light); ctx.astronomy_system->set_sky_light(sky_light); + ctx.astronomy_system->set_bounce_light(bounce_light); } catch (const std::exception&) { @@ -415,9 +497,6 @@ void create_earth(game::context& ctx) terrain.max_lod = 0; terrain.patch_material = nullptr; //ctx.entity_registry->assign(earth_eid, terrain); - - // Pass earth to astronomy system as reference body - ctx.astronomy_system->set_reference_body(earth_eid); } catch (const std::exception&) { diff --git a/src/game/world.hpp b/src/game/world.hpp index 5cb705e..c76f99b 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -30,6 +30,9 @@ namespace world { /// Creates the cosmos. void cosmogenesis(game::context& ctx); +/// Creates the observer. +void create_observer(game::context& ctx); + /** * Sets the location of the observer. * diff --git a/src/geom/sphere.hpp b/src/geom/sphere.hpp index ea4bb48..bbff112 100644 --- a/src/geom/sphere.hpp +++ b/src/geom/sphere.hpp @@ -69,7 +69,7 @@ template bool sphere::intersects(const sphere& sphere) const { vector_type d = center - sphere.center; - float r = radius + sphere.radius; + T r = radius + sphere.radius; return (math::dot(d, d) <= r * r); } @@ -82,7 +82,7 @@ bool sphere::intersects(const aabb& aabb) const template bool sphere::contains(const sphere& sphere) const { - float containment_radius = radius - sphere.radius; + T containment_radius = radius - sphere.radius; if (containment_radius < T(0)) return false; @@ -115,4 +115,3 @@ bool sphere::contains(const vector_type& point) const } // namespace geom #endif // ANTKEEPER_GEOM_SPHERE_HPP - diff --git a/src/gl/texture-2d.cpp b/src/gl/texture-2d.cpp index 5e8d55b..8f286f3 100644 --- a/src/gl/texture-2d.cpp +++ b/src/gl/texture-2d.cpp @@ -33,6 +33,11 @@ void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_typ texture::resize(width, height, type, format, color_space, data); } +void texture_2d::resize(std::uint16_t width, std::uint16_t height, const void* data) +{ + texture::resize(width, height, get_pixel_type(), get_pixel_format(), get_color_space(), data); +} + void texture_2d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t) { texture::set_wrapping(wrap_s, wrap_t); diff --git a/src/gl/texture-2d.hpp b/src/gl/texture-2d.hpp index 4f5c824..4fa76a9 100644 --- a/src/gl/texture-2d.hpp +++ b/src/gl/texture-2d.hpp @@ -39,6 +39,15 @@ public: /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + /** + * Resizes the texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. + * @param data Pointer to pixel data. + */ + void resize(std::uint16_t width, std::uint16_t height, const void* data); + /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping) virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); }; diff --git a/src/math/matrix-functions.hpp b/src/math/matrix-functions.hpp index d4edf5d..c335899 100644 --- a/src/math/matrix-functions.hpp +++ b/src/math/matrix-functions.hpp @@ -35,15 +35,15 @@ namespace math { * @return Sum of the two matrices. */ template -matrix add(const matrix& x, const matrix& y); +constexpr matrix add(const matrix& x, const matrix& y); /// @copydoc add(const matrix&, const matrix&) template -matrix add(const matrix& x, const matrix& y); +constexpr matrix add(const matrix& x, const matrix& y); /// @copydoc add(const matrix&, const matrix&) template -matrix add(const matrix& x, const matrix& y); +constexpr matrix add(const matrix& x, const matrix& y); /** * Reinterprets data as an `NxM` matrix of type `T`. @@ -54,7 +54,7 @@ matrix add(const matrix& x, const matrix& y); * @param data Data to reinterpret. */ template -matrix& as_matrix(T& data); +constexpr matrix& as_matrix(T& data); /** * Calculates the determinant of a matrix. @@ -62,15 +62,15 @@ matrix& as_matrix(T& data); * @param m Matrix of which to take the determinant. */ template -T determinant(const matrix& m); +constexpr T determinant(const matrix& m); /// @copydoc determinant(const matrix&) template -T determinant(const matrix& m); +constexpr T determinant(const matrix& m); /// @copydoc determinant(const matrix&) template -T determinant(const matrix& m); +constexpr T determinant(const matrix& m); /** * Calculates the inverse of a matrix. @@ -78,15 +78,15 @@ T determinant(const matrix& m); * @param m Matrix of which to take the inverse. */ template -matrix inverse(const matrix& m); +constexpr matrix inverse(const matrix& m); /// @copydoc inverse(const matrix&) template -matrix inverse(const matrix& m); +constexpr matrix inverse(const matrix& m); /// @copydoc inverse(const matrix&) template -matrix inverse(const matrix& m); +constexpr matrix inverse(const matrix& m); /** * Performs a component-wise multiplication of two matrices. @@ -95,15 +95,15 @@ matrix inverse(const matrix& m); * @param y Second matrix multiplicand. */ template -matrix componentwise_mul(const matrix& x, const matrix& y); +constexpr matrix componentwise_mul(const matrix& x, const matrix& y); /// @copydoc componentwise_mul(const matrix&, const matrix&) template -matrix componentwise_mul(const matrix& x, const matrix& y); +constexpr matrix componentwise_mul(const matrix& x, const matrix& y); /// @copydoc componentwise_mul(const matrix&, const matrix&) template -matrix componentwise_mul(const matrix& x, const matrix& y); +constexpr matrix componentwise_mul(const matrix& x, const matrix& y); /** * Creates a viewing transformation matrix. @@ -114,7 +114,7 @@ matrix componentwise_mul(const matrix& x, const matrix -matrix look_at(const vector& position, const vector& target, vector up); +constexpr matrix look_at(const vector& position, const vector& target, vector up); /** * Multiplies two matrices. @@ -124,15 +124,15 @@ matrix look_at(const vector& position, const vector& target * @return Product of the two matrices. */ template -matrix mul(const matrix& x, const matrix& y); +constexpr matrix mul(const matrix& x, const matrix& y); /// @copydoc mul(const matrix&, const matrix&); template -matrix mul(const matrix& x, const matrix& y); +constexpr matrix mul(const matrix& x, const matrix& y); /// @copydoc mul(const matrix&, const matrix&); template -matrix mul(const matrix& x, const matrix& y); +constexpr matrix mul(const matrix& x, const matrix& y); /** * Multiplies a matrix by a scalar. @@ -142,7 +142,7 @@ matrix mul(const matrix& x, const matrix& y); * @return Product of the matrix and the scalar.. */ template -matrix mul(const matrix& m, T s); +constexpr matrix mul(const matrix& m, T s); /** * Transforms a vector by a matrix. @@ -152,15 +152,15 @@ matrix mul(const matrix& m, T s); * @return Transformed vector. */ template -vector mul(const matrix& m, const vector& v); +constexpr vector mul(const matrix& m, const vector& v); /// @copydoc mul(const matrix&, const vector&) template -vector mul(const matrix& m, const vector& v); +constexpr vector mul(const matrix& m, const vector& v); /// @copydoc mul(const matrix&, const vector&) template -vector mul(const matrix& m, const vector& v); +constexpr vector mul(const matrix& m, const vector& v); /** * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. @@ -174,7 +174,7 @@ vector mul(const matrix& m, const vector& v); * @return Orthographic projection matrix. */ template -matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far); +constexpr matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far); /** * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. @@ -188,7 +188,7 @@ matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far); * @return Orthographic projection matrix. */ template -matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far); +constexpr matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far); /** * Calculates the outer product of a pair of vectors. @@ -197,15 +197,15 @@ matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far * @param r Parameter to be treated as a row vector. */ template -matrix outer_product(const vector& c, const vector& r); +constexpr matrix outer_product(const vector& c, const vector& r); /// @copydoc outer_product(const vector&, const vector&) template -matrix outer_product(const vector& c, const vector& r); +constexpr matrix outer_product(const vector& c, const vector& r); /// @copydoc outer_product(const vector&, const vector&) template -matrix outer_product(const vector& c, const vector r); +constexpr matrix outer_product(const vector& c, const vector r); /** * Creates a perspective projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. @@ -238,7 +238,7 @@ matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z * @return Resized matrix. */ template -matrix resize(const matrix& m); +constexpr matrix resize(const matrix& m); /** * Constructs a rotation matrix. @@ -285,7 +285,7 @@ matrix3 rotate_z(T angle); * @return Scaled matrix. */ template -matrix scale(const matrix& m, const vector& v); +constexpr matrix scale(const matrix& m, const vector& v); /** * Subtracts a matrix from another matrix. @@ -295,15 +295,15 @@ matrix scale(const matrix& m, const vector& v); * @return Difference between the two matrices. */ template -matrix sub(const matrix& x, const matrix& y); +constexpr matrix sub(const matrix& x, const matrix& y); /// @copydoc sub(const matrix&, const matrix&) template -matrix sub(const matrix& x, const matrix& y); +constexpr matrix sub(const matrix& x, const matrix& y); /// @copydoc sub(const matrix&, const matrix&) template -matrix sub(const matrix& x, const matrix& y); +constexpr matrix sub(const matrix& x, const matrix& y); /** * Translates a matrix. @@ -313,7 +313,7 @@ matrix sub(const matrix& x, const matrix& y); * @return Translated matrix. */ template -matrix translate(const matrix& m, const vector& v); +constexpr matrix translate(const matrix& m, const vector& v); /** * Calculates the transpose of a matrix. @@ -321,15 +321,15 @@ matrix translate(const matrix& m, const vector& v); * @param m Matrix of which to take the transpose. */ template -matrix transpose(const matrix& m); +constexpr matrix transpose(const matrix& m); /// @copydoc transpose(const matrix&) template -matrix transpose(const matrix& m); +constexpr matrix transpose(const matrix& m); /// @copydoc transpose(const matrix&) template -matrix transpose(const matrix& m); +constexpr matrix transpose(const matrix& m); /** * Types casts each matrix element and returns a matrix of the casted type. @@ -342,10 +342,10 @@ matrix transpose(const matrix& m); * @return Type-casted matrix. */ template -matrix type_cast(const matrix& m); +constexpr matrix type_cast(const matrix& m); template -matrix add(const matrix& x, const matrix& y) +constexpr matrix add(const matrix& x, const matrix& y) { return {{ @@ -355,7 +355,7 @@ matrix add(const matrix& x, const matrix& y) } template -matrix add(const matrix& x, const matrix& y) +constexpr matrix add(const matrix& x, const matrix& y) { return {{ @@ -366,7 +366,7 @@ matrix add(const matrix& x, const matrix& y) } template -matrix add(const matrix& x, const matrix& y) +constexpr matrix add(const matrix& x, const matrix& y) { return {{ @@ -378,20 +378,20 @@ matrix add(const matrix& x, const matrix& y) } template -inline matrix& as_matrix(T& data) +constexpr inline matrix& as_matrix(T& data) { static_assert(std::is_pod>::value); return reinterpret_cast&>(data); } template -T determinant(const matrix& m) +constexpr T determinant(const matrix& m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } template -T determinant(const matrix& m) +constexpr T determinant(const matrix& m) { return m[0][0] * m [1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + @@ -402,7 +402,7 @@ T determinant(const matrix& m) } template -T determinant(const matrix& m) +constexpr T determinant(const matrix& m) { return m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] - m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] + @@ -419,7 +419,7 @@ T determinant(const matrix& m) } template -matrix inverse(const matrix& m) +constexpr matrix inverse(const matrix& m) { static_assert(std::is_floating_point::value); @@ -432,7 +432,7 @@ matrix inverse(const matrix& m) } template -matrix inverse(const matrix& m) +constexpr matrix inverse(const matrix& m) { static_assert(std::is_floating_point::value); @@ -461,7 +461,7 @@ matrix inverse(const matrix& m) } template -matrix inverse(const matrix& m) +constexpr matrix inverse(const matrix& m) { static_assert(std::is_floating_point::value); @@ -500,7 +500,7 @@ matrix inverse(const matrix& m) } template -matrix componentwise_mul(const matrix& x, const matrix& y) +constexpr matrix componentwise_mul(const matrix& x, const matrix& y) { return {{ @@ -510,7 +510,7 @@ matrix componentwise_mul(const matrix& x, const matrix -matrix componentwise_mul(const matrix& x, const matrix& y) +constexpr matrix componentwise_mul(const matrix& x, const matrix& y) { return {{ @@ -521,7 +521,7 @@ matrix componentwise_mul(const matrix& x, const matrix -matrix componentwise_mul(const matrix& x, const matrix& y) +constexpr matrix componentwise_mul(const matrix& x, const matrix& y) { return {{ @@ -533,7 +533,7 @@ matrix componentwise_mul(const matrix& x, const matrix -matrix look_at(const vector& position, const vector& target, vector up) +constexpr matrix look_at(const vector& position, const vector& target, vector up) { vector forward = normalize(sub(target, position)); vector right = normalize(cross(forward, up)); @@ -551,7 +551,7 @@ matrix look_at(const vector& position, const vector& target } template -matrix mul(const matrix& x, const matrix& y) +constexpr matrix mul(const matrix& x, const matrix& y) { return {{ @@ -561,7 +561,7 @@ matrix mul(const matrix& x, const matrix& y) } template -matrix mul(const matrix& x, const matrix& y) +constexpr matrix mul(const matrix& x, const matrix& y) { return {{ @@ -572,7 +572,7 @@ matrix mul(const matrix& x, const matrix& y) } template -matrix mul(const matrix& x, const matrix& y) +constexpr matrix mul(const matrix& x, const matrix& y) { return {{ @@ -585,19 +585,19 @@ matrix mul(const matrix& x, const matrix& y) /// @private template -inline matrix mul(const matrix& m, T s, std::index_sequence) +constexpr inline matrix mul(const matrix& m, T s, std::index_sequence) { return {{(m[I] * s)...}}; } template -inline matrix mul(const matrix& m, T s) +constexpr inline matrix mul(const matrix& m, T s) { return mul(m, s, std::make_index_sequence{}); } template -vector mul(const matrix& m, const vector& v) +constexpr vector mul(const matrix& m, const vector& v) { return { @@ -607,7 +607,7 @@ vector mul(const matrix& m, const vector& v) } template -vector mul(const matrix& m, const vector& v) +constexpr vector mul(const matrix& m, const vector& v) { return { @@ -618,7 +618,7 @@ vector mul(const matrix& m, const vector& v) } template -vector mul(const matrix& m, const vector& v) +constexpr vector mul(const matrix& m, const vector& v) { return { @@ -630,7 +630,7 @@ vector mul(const matrix& m, const vector& v) } template -matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far) +constexpr matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far) { return {{ @@ -642,7 +642,7 @@ matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far) } template -matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far) +constexpr matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far) { return {{ @@ -654,7 +654,7 @@ matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far } template -matrix outer_product(const vector& c, const vector& r) +constexpr matrix outer_product(const vector& c, const vector& r) { return {{ @@ -664,7 +664,7 @@ matrix outer_product(const vector& c, const vector& r) } template -matrix outer_product(const vector& c, const vector& r) +constexpr matrix outer_product(const vector& c, const vector& r) { return {{ @@ -675,7 +675,7 @@ matrix outer_product(const vector& c, const vector& r) } template -matrix outer_product(const vector& c, const vector r) +constexpr matrix outer_product(const vector& c, const vector r) { return {{ @@ -717,7 +717,7 @@ matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z } template -matrix resize(const matrix& m) +constexpr matrix resize(const matrix& m) { matrix resized; @@ -796,7 +796,7 @@ matrix3 rotate_z(T angle) } template -matrix scale(const matrix& m, const vector& v) +constexpr matrix scale(const matrix& m, const vector& v) { return mul(m, matrix {{ @@ -808,7 +808,7 @@ matrix scale(const matrix& m, const vector& v) } template -matrix sub(const matrix& x, const matrix& y) +constexpr matrix sub(const matrix& x, const matrix& y) { return {{ @@ -818,7 +818,7 @@ matrix sub(const matrix& x, const matrix& y) } template -matrix sub(const matrix& x, const matrix& y) +constexpr matrix sub(const matrix& x, const matrix& y) { return {{ @@ -829,7 +829,7 @@ matrix sub(const matrix& x, const matrix& y) } template -matrix sub(const matrix& x, const matrix& y) +constexpr matrix sub(const matrix& x, const matrix& y) { return {{ @@ -841,7 +841,7 @@ matrix sub(const matrix& x, const matrix& y) } template -matrix translate(const matrix& m, const vector& v) +constexpr matrix translate(const matrix& m, const vector& v) { return mul(m, matrix {{ @@ -853,7 +853,7 @@ matrix translate(const matrix& m, const vector& v) } template -matrix transpose(const matrix& m) +constexpr matrix transpose(const matrix& m) { return @@ -869,7 +869,7 @@ matrix transpose(const matrix& m) } template -matrix transpose(const matrix& m) +constexpr matrix transpose(const matrix& m) { return @@ -889,7 +889,7 @@ matrix transpose(const matrix& m) } template -matrix transpose(const matrix& m) +constexpr matrix transpose(const matrix& m) { return @@ -914,13 +914,13 @@ matrix transpose(const matrix& m) /// @private template -inline matrix type_cast(const matrix& m, std::index_sequence) +constexpr inline matrix type_cast(const matrix& m, std::index_sequence) { return {type_cast(m[I])...}; } template -matrix type_cast(const matrix& m) +constexpr matrix type_cast(const matrix& m) { return type_cast(m, std::make_index_sequence{}); } diff --git a/src/math/matrix-operators.hpp b/src/math/matrix-operators.hpp index 316b16f..f885f6c 100644 --- a/src/math/matrix-operators.hpp +++ b/src/math/matrix-operators.hpp @@ -25,60 +25,60 @@ /// @copydoc math::add(const math::matrix&, const math::matrix&) template -math::matrix operator+(const math::matrix& x, const math::matrix& y); +constexpr math::matrix operator+(const math::matrix& x, const math::matrix& y); /// @copydoc math::mul(const math::matrix&, const math::matrix&) template -math::matrix operator*(const math::matrix& x, const math::matrix& y); +constexpr math::matrix operator*(const math::matrix& x, const math::matrix& y); /// @copydoc math::mul(const math::matrix&, T) template -math::matrix operator*(const math::matrix& m, T s); +constexpr math::matrix operator*(const math::matrix& m, T s); /// @copydoc math::mul(const math::matrix&, T) template -math::matrix operator*(T s, const math::matrix& m); +constexpr math::matrix operator*(T s, const math::matrix& m); /// @copydoc math::mul(const math::matrix&, const math::vector&) template -math::vector operator*(const math::matrix& m, const math::vector& v); +constexpr math::vector operator*(const math::matrix& m, const math::vector& v); /// @copydoc math::sub(const math::matrix&, const math::matrix&) template -math::matrix operator-(const math::matrix& x, const math::matrix& y); +constexpr math::matrix operator-(const math::matrix& x, const math::matrix& y); template -inline math::matrix operator+(const math::matrix& x, const math::matrix& y) +constexpr inline math::matrix operator+(const math::matrix& x, const math::matrix& y) { return add(x, y); } template -inline math::matrix operator*(const math::matrix& x, const math::matrix& y) +constexpr inline math::matrix operator*(const math::matrix& x, const math::matrix& y) { return mul(x, y); } template -inline math::matrix operator*(const math::matrix& m, T s) +constexpr inline math::matrix operator*(const math::matrix& m, T s) { return mul(m, s); } template -inline math::matrix operator*(T s, const math::matrix& m) +constexpr inline math::matrix operator*(T s, const math::matrix& m) { return mul(m, s); } template -inline math::vector operator*(const math::matrix& m, const math::vector& v) +constexpr inline math::vector operator*(const math::matrix& m, const math::vector& v) { return mul(m, v); } template -inline math::matrix operator-(const math::matrix& x, const math::matrix& y) +constexpr inline math::matrix operator-(const math::matrix& x, const math::matrix& y) { return sub(x, y); } diff --git a/src/math/matrix-type.hpp b/src/math/matrix-type.hpp index ad2576e..0ecdd04 100644 --- a/src/math/matrix-type.hpp +++ b/src/math/matrix-type.hpp @@ -66,14 +66,26 @@ constexpr matrix matrix::identity = identity_matrix(std::ma template using matrix2 = matrix; +/// 2x2 matrix. +template +using matrix2x2 = matrix; + /// 3x3 matrix. template using matrix3 = matrix; +/// 3x3 matrix. +template +using matrix3x3 = matrix; + /// 4x4 matrix. template using matrix4 = matrix; +/// 4x4 matrix. +template +using matrix4x4 = matrix; + } // namespace math #endif // ANTKEEPER_MATH_MATRIX_TYPE_HPP diff --git a/src/math/vector-functions.hpp b/src/math/vector-functions.hpp index 8281bcc..2a541b0 100644 --- a/src/math/vector-functions.hpp +++ b/src/math/vector-functions.hpp @@ -36,7 +36,7 @@ namespace math { * @return Sum of the two vectors. */ template -vector add(const vector& x, const vector& y); +constexpr vector add(const vector& x, const vector& y); /** * Checks if all elements of a boolean vector are `true`. @@ -45,7 +45,7 @@ vector add(const vector& x, const vector& y); * @return `true` if all elements are `true`, `false` otherwise. */ template -bool all(const vector& x); +constexpr bool all(const vector& x); /** * Checks if any elements of a boolean vector are `true`. @@ -54,7 +54,7 @@ bool all(const vector& x); * @return `true` if any elements are `true`, `false` otherwise. */ template -bool any(const vector& x); +constexpr bool any(const vector& x); /** * Reinterprets data as an `N`-dimensional vector of type `T`. @@ -64,7 +64,7 @@ bool any(const vector& x); * @param data Data to reinterpret. */ template -vector& as_vector(T& data); +constexpr vector& as_vector(T& data); /** * Clamps the values of a vector's elements. @@ -75,7 +75,7 @@ vector& as_vector(T& data); * @return Clamped vector. */ template -vector clamp(const vector& x, T min_value, T max_value); +constexpr vector clamp(const vector& x, T min_value, T max_value); /** * Clamps the length of a vector. @@ -95,7 +95,7 @@ vector clamp_length(const vector& x, T max_length); * @return Cross product of the two vectors. */ template -vector cross(const vector& x, const vector& y); +constexpr vector cross(const vector& x, const vector& y); /** * Calculates the distance between two points. @@ -115,7 +115,7 @@ vector distance(const vector& p0, const vector& p1); * @return Squared distance between the two points. */ template -vector distance_squared(const vector& p0, const vector& p1); +constexpr vector distance_squared(const vector& p0, const vector& p1); /** * Divides a vector by another vector. @@ -125,7 +125,7 @@ vector distance_squared(const vector& p0, const vector& p1); * @return Result of the division. */ template -vector div(const vector& x, const vector& y); +constexpr vector div(const vector& x, const vector& y); /** * Divides a vector by a scalar. @@ -135,7 +135,7 @@ vector div(const vector& x, const vector& y); * @return Result of the division. */ template -vector div(const vector& v, T s); +constexpr vector div(const vector& v, T s); /** * Calculates the dot product of two vectors. @@ -145,7 +145,7 @@ vector div(const vector& v, T s); * @return Dot product of the two vectors. */ template -T dot(const vector& x, const vector& y); +constexpr T dot(const vector& x, const vector& y); /** * Compares two vectors for equality @@ -155,7 +155,7 @@ T dot(const vector& x, const vector& y); * @return Boolean vector containing the result of the element comparisons. */ template -vector equal(const vector& x, const vector& y); +constexpr vector equal(const vector& x, const vector& y); /** * Performs a component-wise greater-than comparison of two vectors. @@ -165,7 +165,7 @@ vector equal(const vector& x, const vector& y); * @return Boolean vector containing the result of the element comparisons. */ template -vector greater_than(const vector& x, const vector& y); +constexpr vector greater_than(const vector& x, const vector& y); /** * Performs a component-wise greater-than or equal-to comparison of two vectors. @@ -175,7 +175,7 @@ vector greater_than(const vector& x, const vector& y); * @return Boolean vector containing the result of the element comparisons. */ template -vector greater_than_equal(const vector& x, const vector& y); +constexpr vector greater_than_equal(const vector& x, const vector& y); /** * Calculates the length of a vector. @@ -193,7 +193,7 @@ T length(const vector& x); * @return Squared length of the vector. */ template -T length_squared(const vector& x); +constexpr T length_squared(const vector& x); /** * Performs a component-wise less-than comparison of two vectors. @@ -203,7 +203,7 @@ T length_squared(const vector& x); * @return Boolean vector containing the result of the element comparisons. */ template -vector less_than(const vector& x, const vector& y); +constexpr vector less_than(const vector& x, const vector& y); /** * Performs a component-wise less-than or equal-to comparison of two vectors. @@ -213,7 +213,7 @@ vector less_than(const vector& x, const vector& y); * @return Boolean vector containing the result of the element comparisons. */ template -vector less_than_equal(const vector& x, const vector& y); +constexpr vector less_than_equal(const vector& x, const vector& y); /** * Multiplies two vectors. @@ -223,7 +223,7 @@ vector less_than_equal(const vector& x, const vector& y); * @return Product of the two vectors. */ template -vector mul(const vector& x, const vector& y); +constexpr vector mul(const vector& x, const vector& y); /** * Multiplies a vector by a scalar. @@ -233,7 +233,7 @@ vector mul(const vector& x, const vector& y); * @return Product of the vector and scalar. */ template -vector mul(const vector& v, T s); +constexpr vector mul(const vector& v, T s); /** * Negates a vector. @@ -242,7 +242,7 @@ vector mul(const vector& v, T s); * @return Negated vector. */ template -vector negate(const vector& x); +constexpr vector negate(const vector& x); /** * Calculates the unit vector in the same direction as the original vector. @@ -260,7 +260,7 @@ vector normalize(const vector& x); * @return Logically inverted vector. */ template -vector not(const vector& x); +constexpr vector not(const vector& x); /** * Compares two vectors for inequality @@ -270,7 +270,7 @@ vector not(const vector& x); * @return Boolean vector containing the result of the element comparisons. */ template -vector not_equal(const vector& x, const vector& y); +constexpr vector not_equal(const vector& x, const vector& y); /** * Resizes a vector. Any new elements will be set to `0`. @@ -279,7 +279,7 @@ vector not_equal(const vector& x, const vector& y); * @return Resized vector. */ template -vector resize(const vector& v); +constexpr vector resize(const vector& v); /** * Subtracts a vector from another vector. @@ -289,7 +289,7 @@ vector resize(const vector& v); * @return Difference between the two vectors. */ template -vector sub(const vector& x, const vector& y); +constexpr vector sub(const vector& x, const vector& y); /** * Makes an m-dimensional vector by rearranging and/or duplicating elements of an n-dimensional vector. @@ -300,7 +300,7 @@ vector sub(const vector& x, const vector& y); * @return Vector containing elements from vector `v` in the order specified by `Indices`. The size of the returned vector is equivalent to the number of indices in `Indices`. */ template -vector swizzle(const vector& v); +constexpr vector swizzle(const vector& v); /** * Types casts each vector component and returns a vector of the casted type. @@ -312,49 +312,49 @@ vector swizzle(const vector& v); * @return Type-casted vector. */ template -vector type_cast(const vector& v); +constexpr vector type_cast(const vector& v); /// @private template -inline vector add(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector add(const vector& x, const vector& y, std::index_sequence) { return {(x[I] + y[I])...}; } template -inline vector add(const vector& x, const vector& y) +constexpr inline vector add(const vector& x, const vector& y) { return add(x, y, std::make_index_sequence{}); } /// @private template -inline bool all(const vector& x, std::index_sequence) +constexpr inline bool all(const vector& x, std::index_sequence) { return (x[I] && ...); } template -inline bool all(const vector& x) +constexpr inline bool all(const vector& x) { return all(x, std::make_index_sequence{}); } /// @private template -inline bool any(const vector& x, std::index_sequence) +constexpr inline bool any(const vector& x, std::index_sequence) { return (x[I] || ...); } template -inline bool any(const vector& x) +constexpr inline bool any(const vector& x) { return any(x, std::make_index_sequence{}); } template -inline vector& as_vector(T& data) +constexpr inline vector& as_vector(T& data) { static_assert(std::is_pod>::value); return reinterpret_cast&>(data); @@ -362,13 +362,13 @@ inline vector& as_vector(T& data) /// @private template -inline vector clamp(const vector& x, T min_value, T max_value, std::index_sequence) +constexpr inline vector clamp(const vector& x, T min_value, T max_value, std::index_sequence) { return {std::min(max_value, std::max(min_value, x[I]))...}; } template -inline vector clamp(const vector& x, T min_value, T max_value) +constexpr inline vector clamp(const vector& x, T min_value, T max_value) { return clamp(x, min_value, max_value, std::make_index_sequence{}); } @@ -381,7 +381,7 @@ vector clamp_length(const vector& x, T max_length) } template -inline vector cross(const vector& x, const vector& y) +constexpr inline vector cross(const vector& x, const vector& y) { return { @@ -399,7 +399,7 @@ inline vector distance(const vector& p0, const vector& p1) } template -inline vector distance_squared(const vector& p0, const vector& p1) +constexpr inline vector distance_squared(const vector& p0, const vector& p1) { static_assert(std::is_floating_point::value); return length_squared(sub(p0, p1)); @@ -407,78 +407,78 @@ inline vector distance_squared(const vector& p0, const vector& /// @private template -inline vector div(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector div(const vector& x, const vector& y, std::index_sequence) { return {(x[I] / y[I])...}; } template -inline vector div(const vector& x, const vector& y) +constexpr inline vector div(const vector& x, const vector& y) { return div(x, y, std::make_index_sequence{}); } /// @private template -inline vector div(const vector& v, T s, std::index_sequence) +constexpr inline vector div(const vector& v, T s, std::index_sequence) { return {(v[I] / s)...}; } template -inline vector div(const vector& v, T s) +constexpr inline vector div(const vector& v, T s) { return div(v, s, std::make_index_sequence{}); } /// @private template -inline T dot(const vector& x, const vector& y, std::index_sequence) +constexpr inline T dot(const vector& x, const vector& y, std::index_sequence) { return ((x[I] * y[I]) + ...); } template -inline T dot(const vector& x, const vector& y) +constexpr inline T dot(const vector& x, const vector& y) { return dot(x, y, std::make_index_sequence{}); } /// @private template -inline vector equal(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector equal(const vector& x, const vector& y, std::index_sequence) { return {(x[I] == y[I])...}; } template -inline vector equal(const vector& x, const vector& y) +constexpr inline vector equal(const vector& x, const vector& y) { return equal(x, y, std::make_index_sequence{}); } /// @private template -inline vector greater_than(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector greater_than(const vector& x, const vector& y, std::index_sequence) { return {(x[I] > y[I])...}; } template -inline vector greater_than(const vector& x, const vector& y) +constexpr inline vector greater_than(const vector& x, const vector& y) { return greater_than(x, y, std::make_index_sequence{}); } /// @private template -inline vector greater_than_equal(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector greater_than_equal(const vector& x, const vector& y, std::index_sequence) { return {(x[I] >= y[I])...}; } template -inline vector greater_than_equal(const vector& x, const vector& y) +constexpr inline vector greater_than_equal(const vector& x, const vector& y) { return greater_than_equal(x, y, std::make_index_sequence{}); } @@ -491,73 +491,72 @@ inline T length(const vector& x) } template -inline T length_squared(const vector& x) +constexpr inline T length_squared(const vector& x) { - static_assert(std::is_floating_point::value); return dot(x, x); } /// @private template -inline vector less_than(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector less_than(const vector& x, const vector& y, std::index_sequence) { return {(x[I] < y[I])...}; } template -inline vector less_than(const vector& x, const vector& y) +constexpr inline vector less_than(const vector& x, const vector& y) { return less_than(x, y, std::make_index_sequence{}); } /// @private template -inline vector less_than_equal(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector less_than_equal(const vector& x, const vector& y, std::index_sequence) { return {(x[I] <= y[I])...}; } template -inline vector less_than_equal(const vector& x, const vector& y) +constexpr inline vector less_than_equal(const vector& x, const vector& y) { return less_than_equal(x, y, std::make_index_sequence{}); } /// @private template -inline vector mul(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector mul(const vector& x, const vector& y, std::index_sequence) { return {(x[I] * y[I])...}; } template -inline vector mul(const vector& x, const vector& y) +constexpr inline vector mul(const vector& x, const vector& y) { return mul(x, y, std::make_index_sequence{}); } /// @private template -inline vector mul(const vector& v, T s, std::index_sequence) +constexpr inline vector mul(const vector& v, T s, std::index_sequence) { return {(v[I] * s)...}; } template -inline vector mul(const vector& v, T s) +constexpr inline vector mul(const vector& v, T s) { return mul(v, s, std::make_index_sequence{}); } /// @private template -inline vector negate(const vector& x, std::index_sequence) +constexpr inline vector negate(const vector& x, std::index_sequence) { return {(-x[I])...}; } template -inline vector negate(const vector& x) +constexpr inline vector negate(const vector& x) { return negate(x, std::make_index_sequence{}); } @@ -571,32 +570,32 @@ inline vector normalize(const vector& x) /// @private template -inline vector not(const vector& x, std::index_sequence) +constexpr inline vector not(const vector& x, std::index_sequence) { return {!x[I]...}; } template -inline vector not(const vector& x) +constexpr inline vector not(const vector& x) { return not(x, std::make_index_sequence{}); } /// @private template -inline vector not_equal(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector not_equal(const vector& x, const vector& y, std::index_sequence) { return {(x[I] != y[I])...}; } template -inline vector not_equal(const vector& x, const vector& y) +constexpr inline vector not_equal(const vector& x, const vector& y) { return not_equal(x, y, std::make_index_sequence{}); } template -vector resize(const vector& v) +constexpr vector resize(const vector& v) { vector resized; @@ -611,32 +610,32 @@ vector resize(const vector& v) /// @private template -inline vector sub(const vector& x, const vector& y, std::index_sequence) +constexpr inline vector sub(const vector& x, const vector& y, std::index_sequence) { return {(x[I] - y[I])...}; } template -inline vector sub(const vector& x, const vector& y) +constexpr inline vector sub(const vector& x, const vector& y) { return sub(x, y, std::make_index_sequence{}); } template -inline vector swizzle(const vector& v) +constexpr inline vector swizzle(const vector& v) { return { v[Indices]... }; } /// @private template -inline vector type_cast(const vector& v, std::index_sequence) +constexpr inline vector type_cast(const vector& v, std::index_sequence) { return {static_cast(v[I])...}; } template -inline vector type_cast(const vector& v) +constexpr inline vector type_cast(const vector& v) { return type_cast(v, std::make_index_sequence{}); } @@ -644,4 +643,3 @@ inline vector type_cast(const vector& v) } // namespace math #endif // ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP - diff --git a/src/math/vector-operators.hpp b/src/math/vector-operators.hpp index af7f899..b4d4b9e 100644 --- a/src/math/vector-operators.hpp +++ b/src/math/vector-operators.hpp @@ -25,35 +25,35 @@ /// @copydoc math::add(const math::vector&, const math::vector&) template -math::vector operator+(const math::vector& x, const math::vector& y); +constexpr math::vector operator+(const math::vector& x, const math::vector& y); /// @copydoc math::div(const math::vector&, const math::vector&) template -math::vector operator/(const math::vector& x, const math::vector& y); +constexpr math::vector operator/(const math::vector& x, const math::vector& y); /// @copydoc math::div(const math::vector&, T) template -math::vector operator/(const math::vector& v, T s); +constexpr math::vector operator/(const math::vector& v, T s); /// @copydoc math::mul(const math::vector&, const math::vector&) template -math::vector operator*(const math::vector& x, const math::vector& y); +constexpr math::vector operator*(const math::vector& x, const math::vector& y); /// @copydoc math::mul(const math::vector&, T) template -math::vector operator*(const math::vector& v, T s); +constexpr math::vector operator*(const math::vector& v, T s); /// @copydoc math::mul(const math::vector&, T) template -math::vector operator*(T s, const math::vector& v); +constexpr math::vector operator*(T s, const math::vector& v); /// @copydoc math::negate(const math::vector&) template -math::vector operator-(const math::vector& x); +constexpr math::vector operator-(const math::vector& x); /// @copydoc math::sub(const math::vector&, const math::vector&) template -math::vector operator-(const math::vector& x, const math::vector& y); +constexpr math::vector operator-(const math::vector& x, const math::vector& y); /** * Adds two vectors and stores the result in the first vector. @@ -63,7 +63,7 @@ math::vector operator-(const math::vector& x, const math::vector -math::vector& operator+=(math::vector& x, const math::vector& y); +constexpr math::vector& operator+=(math::vector& x, const math::vector& y); /** * Subtracts two vectors and stores the result in the first vector. @@ -73,7 +73,7 @@ math::vector& operator+=(math::vector& x, const math::vector& * @return Reference to the first vector. */ template -math::vector& operator-=(math::vector& x, const math::vector& y); +constexpr math::vector& operator-=(math::vector& x, const math::vector& y); /** * Multiplies two vectors and stores the result in the first vector. @@ -83,7 +83,7 @@ math::vector& operator-=(math::vector& x, const math::vector& * @return Reference to the first vector. */ template -math::vector& operator*=(math::vector& x, const math::vector& y); +constexpr math::vector& operator*=(math::vector& x, const math::vector& y); /** * Multiplies a vector and a scalar and stores the result in the vector. @@ -93,7 +93,7 @@ math::vector& operator*=(math::vector& x, const math::vector& * @return Reference to the vector. */ template -math::vector& operator*=(math::vector& v, T s); +constexpr math::vector& operator*=(math::vector& v, T s); /** * Divides the first vector by the second vector the result in the first vector. @@ -103,7 +103,7 @@ math::vector& operator*=(math::vector& v, T s); * @return Reference to the first vector. */ template -math::vector& operator/=(math::vector& x, const math::vector& y); +constexpr math::vector& operator/=(math::vector& x, const math::vector& y); /** * Divides a vector by a scalar and stores the result in the vector. @@ -113,91 +113,90 @@ math::vector& operator/=(math::vector& x, const math::vector& * @return Reference to the vector. */ template -math::vector& operator/=(math::vector& v, T s); +constexpr math::vector& operator/=(math::vector& v, T s); template -inline math::vector operator+(const math::vector& x, const math::vector& y) +constexpr inline math::vector operator+(const math::vector& x, const math::vector& y) { return add(x, y); } template -inline math::vector operator-(const math::vector& x) +constexpr inline math::vector operator-(const math::vector& x) { return negate(x); } template -inline math::vector operator-(const math::vector& x, const math::vector& y) +constexpr inline math::vector operator-(const math::vector& x, const math::vector& y) { return sub(x, y); } template -inline math::vector operator*(const math::vector& x, const math::vector& y) +constexpr inline math::vector operator*(const math::vector& x, const math::vector& y) { return mul(x, y); } template -inline math::vector operator*(const math::vector& v, T s) +constexpr inline math::vector operator*(const math::vector& v, T s) { return mul(v, s); } template -inline math::vector operator*(T s, const math::vector& v) +constexpr inline math::vector operator*(T s, const math::vector& v) { return mul(v, s); } template -inline math::vector operator/(const math::vector& x, const math::vector& y) +constexpr inline math::vector operator/(const math::vector& x, const math::vector& y) { return div(x, y); } template -inline math::vector operator/(const math::vector& v, T s) +constexpr inline math::vector operator/(const math::vector& v, T s) { return div(v, s); } template -inline math::vector& operator+=(math::vector& x, const math::vector& y) +constexpr inline math::vector& operator+=(math::vector& x, const math::vector& y) { return (x = x + y); } template -inline math::vector& operator-=(math::vector& x, const math::vector& y) +constexpr inline math::vector& operator-=(math::vector& x, const math::vector& y) { return (x = x - y); } template -inline math::vector& operator*=(math::vector& x, const math::vector& y) +constexpr inline math::vector& operator*=(math::vector& x, const math::vector& y) { return (x = x * y); } template -inline math::vector& operator*=(math::vector& v, T s) +constexpr inline math::vector& operator*=(math::vector& v, T s) { return (v = v * s); } template -inline math::vector& operator/=(math::vector& x, const math::vector& y) +constexpr inline math::vector& operator/=(math::vector& x, const math::vector& y) { return (x = x * y); } template -inline math::vector& operator/=(math::vector& v, T s) +constexpr inline math::vector& operator/=(math::vector& v, T s) { return (v = v / s); } #endif // ANTKEEPER_MATH_VECTOR_OPERATORS_HPP - diff --git a/src/math/vector-type.hpp b/src/math/vector-type.hpp index 84f5a84..2d7c4a4 100644 --- a/src/math/vector-type.hpp +++ b/src/math/vector-type.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_MATH_VECTOR_TYPE_HPP #define ANTKEEPER_MATH_VECTOR_TYPE_HPP -#include #include namespace math { @@ -34,87 +33,107 @@ namespace math { template struct vector { - typedef T scalar_type; - typedef std::array array_type; - scalar_type components[N]; - - inline constexpr scalar_type& operator[](std::size_t i) noexcept { return components[i]; } - inline constexpr const scalar_type& operator[](std::size_t i) const noexcept { return components[i]; } - inline constexpr operator array_type&() noexcept { return reinterpret_cast(components[0]); } - inline constexpr operator const array_type&() const noexcept { return reinterpret_cast(components[0]); } - inline constexpr const scalar_type* data() const noexcept { return components; }; - inline constexpr scalar_type* data() noexcept { return components; }; + typedef T element_type; + element_type elements[N]; + + inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } + inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } + inline constexpr const element_type* data() const noexcept { return elements; }; + inline constexpr element_type* data() noexcept { return elements; }; inline constexpr std::size_t size() const noexcept { return N; }; }; template struct vector { - typedef T scalar_type; - typedef std::array array_type; - scalar_type x; - - inline constexpr scalar_type& operator[](std::size_t i) noexcept { return *((&x) + i); } - inline constexpr const scalar_type& operator[](std::size_t i) const noexcept { return *((&x) + i); } - inline constexpr operator array_type&() noexcept { return reinterpret_cast(x); } - inline constexpr operator const array_type&() const noexcept { return reinterpret_cast(x); } - inline constexpr const scalar_type* data() const noexcept { return &x; }; - inline constexpr scalar_type* data() noexcept { return &x; }; + typedef T element_type; + + union + { + element_type elements[1]; + + struct + { + element_type x; + }; + }; + + inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } + inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } + inline constexpr const element_type* data() const noexcept { return elements; }; + inline constexpr element_type* data() noexcept { return elements; }; inline constexpr std::size_t size() const noexcept { return 1; }; }; template struct vector { - typedef T scalar_type; - typedef std::array array_type; - scalar_type x; - scalar_type y; - - inline constexpr scalar_type& operator[](std::size_t i) noexcept { return *((&x) + i); } - inline constexpr const scalar_type& operator[](std::size_t i) const noexcept { return *((&x) + i); } - inline constexpr operator array_type&() noexcept { return reinterpret_cast(x); } - inline constexpr operator const array_type&() const noexcept { return reinterpret_cast(x); } - inline constexpr const scalar_type* data() const noexcept { return &x; }; - inline constexpr scalar_type* data() noexcept { return &x; }; + typedef T element_type; + + union + { + element_type elements[2]; + + struct + { + element_type x; + element_type y; + }; + }; + + inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } + inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } + inline constexpr const element_type* data() const noexcept { return elements; }; + inline constexpr element_type* data() noexcept { return elements; }; inline constexpr std::size_t size() const noexcept { return 2; }; }; template struct vector { - typedef T scalar_type; - typedef std::array array_type; - scalar_type x; - scalar_type y; - scalar_type z; - - inline constexpr scalar_type& operator[](std::size_t i) noexcept { return *((&x) + i); } - inline constexpr const scalar_type& operator[](std::size_t i) const noexcept { return *((&x) + i); } - inline constexpr operator array_type&() noexcept { return reinterpret_cast(x); } - inline constexpr operator const array_type&() const noexcept { return reinterpret_cast(x); } - inline constexpr const scalar_type* data() const noexcept { return &x; }; - inline constexpr scalar_type* data() noexcept { return &x; }; + typedef T element_type; + + union + { + element_type elements[3]; + + struct + { + element_type x; + element_type y; + element_type z; + }; + }; + + inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } + inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } + inline constexpr const element_type* data() const noexcept { return elements; }; + inline constexpr element_type* data() noexcept { return elements; }; inline constexpr std::size_t size() const noexcept { return 3; }; - }; template struct vector { - typedef T scalar_type; - typedef std::array array_type; - scalar_type x; - scalar_type y; - scalar_type z; - scalar_type w; - - inline constexpr scalar_type& operator[](std::size_t i) noexcept { return *((&x) + i); } - inline constexpr const scalar_type& operator[](std::size_t i) const noexcept { return *((&x) + i); } - inline constexpr operator array_type&() noexcept { return reinterpret_cast(x); } - inline constexpr operator const array_type&() const noexcept { return reinterpret_cast(x); } - inline constexpr const scalar_type* data() const noexcept { return &x; }; - inline constexpr scalar_type* data() noexcept { return &x; }; + typedef T element_type; + + union + { + element_type elements[4]; + + struct + { + element_type x; + element_type y; + element_type z; + element_type w; + }; + }; + + inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } + inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } + inline constexpr const element_type* data() const noexcept { return elements; }; + inline constexpr element_type* data() noexcept { return elements; }; inline constexpr std::size_t size() const noexcept { return 4; }; }; @@ -133,4 +152,3 @@ using vector4 = vector; } // namespace math #endif // ANTKEEPER_MATH_VECTOR_TYPE_HPP - diff --git a/src/physics/gas/atmosphere.hpp b/src/physics/gas/atmosphere.hpp index 9420fc1..cd92348 100644 --- a/src/physics/gas/atmosphere.hpp +++ b/src/physics/gas/atmosphere.hpp @@ -32,101 +32,95 @@ namespace gas { namespace atmosphere { /** - * Calculates a particle polarizability factor used in computing scattering coefficients. + * Calculates a particle polarizability factor. * * @param ior Atmospheric index of refraction. - * @param density Molecular density. - * @return Polarization factor. + * @param density Molecular number density, in mol/m-3. + * @return Polarizability factor. * + * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. - * @see Real-Time Spectral Scattering in Large-Scale Natural Participating Media. */ template T polarization(T ior, T density) { - const T ior2m1 = ior * ior - T(1.0); - const T num = T(2) * math::pi * math::pi * ior2m1 * ior2m1; + constexpr T k = T(2) * math::pi * math::pi; + const T ior2m1 = ior * ior - T(1); + const T num = k * ior2m1 * ior2m1; const T den = T(3) * density * density; return num / den; } /** - * Calculates a Rayleigh scattering coefficient (wavelength-dependent). + * Calculates a wavelength-dependent scattering coefficient. * + * @param density Molecular number density of the particles, in mol/m-3. + * @param polarization Particle polarizability factor. * @param wavelength Wavelength of light, in meters. - * @param density Molecular density of Rayleigh particles. - * @param polarization Rayleigh particle polarization factor. + * + * @return Scattering coefficient. * * @see atmosphere::polarization * + * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. - * @see Real-Time Spectral Scattering in Large-Scale Natural Participating Media. */ template -T scattering_rayleigh(T wavelength, T density, T polarization) +T scattering(T density, T polarization, T wavelength) { const T wavelength2 = wavelength * wavelength; - return math::four_pi * density / (wavelength2 * wavelength2) * polarization; + return math::four_pi * (density / (wavelength2 * wavelength2)) * polarization; } /** - * Calculates a Mie scattering coefficient (wavelength-independent). + * Calculates a wavelength-independent scattering coefficient. * - * @param density Molecular density of Mie particles. - * @param polarization Mie particle polarization factor. + * @param density Molecular number density of the particles, in mol/m-3. + * @param polarization Particle polarizability factor. * - * @return Mie scattering coefficient. + * @return Scattering coefficient. * * @see atmosphere::polarization * + * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. - * @see Real-Time Spectral Scattering in Large-Scale Natural Participating Media. */ template -T scattering_mie(T density, T polarization) +T scattering(T density, T polarization) { return math::four_pi * density * polarization; } /** - * Calculates a Mie absorption coefficient (wavelength-independent). + * Calculates an absorption coefficient. * - * @param scattering Mie scattering coefficient. + * @param scattering Scattering coefficient. + * @param albedo Single-scattering albedo. * - * @return Mie absorption coefficient. + * @return Absorption coefficient. * - * @see Bruneton, E. and Neyret, F. (2008), Precomputed Atmospheric Scattering. Computer Graphics Forum, 27: 1079-1086. https://doi.org/10.1111/j.1467-8659.2008.01245.x + * @see https://en.wikipedia.org/wiki/Single-scattering_albedo */ template -T absorption_mie(T scattering) +T absorption(T scattering, T albedo) { - return scattering / T(9); + return scattering * (T(1) / albedo - T(1)); } /** - * Calculates attenuation due to extinction (absorption + out-scattering). + * Calculates an extinction coefficient. * - * @param ec Extinction coefficient (absorption coefficient + scattering coefficient). - * @param s Scale factor. - * @return Attenuation factor. - */ -template -T extinction(T ec, T s) -{ - return std::exp(-(ec * s)); -} - -/** - * Calculates the single-scattering albedo (SSA) given a scattering coefficient and an extinction coefficient. + * @param scattering Scattering coefficient. + * @param albedo Single-scattering albedo. + * + * @return Extinction coefficient. * - * @param s Scattering coefficient. - * @param e Extinction coefficient. - * @return Single-scattering albedo. + * @see https://en.wikipedia.org/wiki/Single-scattering_albedo */ template -T albedo(T s, T e) +T extinction(T scattering, T albedo) { - return s / e; + return scattering / albedo; } /** diff --git a/src/physics/gas/ozone.hpp b/src/physics/gas/ozone.hpp index c8d17f4..f3a2459 100644 --- a/src/physics/gas/ozone.hpp +++ b/src/physics/gas/ozone.hpp @@ -20,51 +20,559 @@ #ifndef ANTKEEPER_PHYSICS_GAS_OZONE_HPP #define ANTKEEPER_PHYSICS_GAS_OZONE_HPP +#include "math/interpolation.hpp" + namespace physics { namespace gas { - + /// Ozone-related constants and functions. namespace ozone { /** - * Cross section of ozone at wavelength 680nm and temperature 293K, in m-2/molecule. - * - * @see https://www.iup.uni-bremen.de/gruppen/molspec/databases/referencespectra/o3spectra2011/index.html - */ -template -constexpr T cross_section_680nm_293k = T(1.36820899679147e-25); - -/** - * Cross section of ozone at wavelength 550nm and temperature 293K, in m-2/molecule. + * Cross sections of ozone from wavelength 280nm to 780nm, in increments of 1nm, at a temperature of 293K, in m-2/molecule. * * @see https://www.iup.uni-bremen.de/gruppen/molspec/databases/referencespectra/o3spectra2011/index.html */ -template -constexpr T cross_section_550nm_293k = T(3.31405330400124e-25); +constexpr double cross_sections_280nm_780nm_293k[781 - 280] = +{ + 4.08020162329358e-22, + 3.71300005805609e-22, + 3.33329613050011e-22, + 3.08830771169272e-22, + 2.7952452827933e-22, + 2.54507006594824e-22, + 2.31059906830132e-22, + 2.06098663896454e-22, + 1.77641837487505e-22, + 1.60649162987982e-22, + 1.43573256268989e-22, + 1.28028940869356e-22, + 1.11222715354512e-22, + 1.00812840644175e-22, + 8.62757275283743e-23, + 7.74492225274189e-23, + 6.74998572150431e-23, + 5.92584261096169e-23, + 5.09573191001311e-23, + 4.58263122360992e-23, + 3.90608822487099e-23, + 3.4712884682176e-23, + 3.05004947986011e-23, + 2.61948471583207e-23, + 2.35950204633063e-23, + 2.01696279467763e-23, + 1.77716671613151e-23, + 1.57884103804348e-23, + 1.35332466460157e-23, + 1.23148992566335e-23, + 1.00674476685553e-23, + 9.14966132477946e-24, + 8.0373280016411e-24, + 6.82346538467894e-24, + 6.42747947320284e-24, + 5.12300259416845e-24, + 4.77060350699273e-24, + 4.0011431423959e-24, + 3.72184265884461e-24, + 2.71828160861414e-24, + 3.14980819763314e-24, + 2.01180905161078e-24, + 2.23741500460792e-24, + 2.04586872483057e-24, + 1.14774957440809e-24, + 1.7873766001915e-24, + 1.14326155695545e-24, + 7.61393733215954e-25, + 1.30841854222986e-24, + 6.50013011248201e-25, + 4.47253301018733e-25, + 7.22254319278391e-25, + 4.42923420258652e-25, + 3.22168537281097e-25, + 5.60700006481047e-25, + 2.51991724359347e-25, + 1.64975530971913e-25, + 2.69863618345909e-25, + 2.2962216595934e-25, + 1.2768604186372e-25, + 1.6428080690911e-25, + 1.15252741016694e-25, + 6.88123014503947e-26, + 9.01478526179051e-26, + 1.38671804168295e-25, + 7.28648586727799e-26, + 6.3468766437083e-26, + 4.1705109317344e-26, + 3.22298756116943e-26, + 2.52476541455397e-26, + 2.96595291121762e-26, + 2.10981495904366e-26, + 3.0696184107227e-26, + 2.38627682184272e-26, + 1.35645160962203e-26, + 1.14737436955784e-26, + 1.00293019429341e-26, + 1.42699666872085e-26, + 1.03892014298915e-26, + 7.46029500400895e-27, + 7.86729405487508e-27, + 6.49493671023213e-27, + 3.90586420068501e-27, + 3.3969327080211e-27, + 2.69156849765275e-27, + 4.89998022220222e-27, + 4.18363151355665e-27, + 2.41505691668684e-27, + 1.52926807176976e-27, + 1.74010836686791e-27, + 1.43997701486263e-27, + 1.61611242687813e-27, + 1.09444991940235e-27, + 8.68337506495441e-28, + 1.28660044051837e-27, + 1.07534571825705e-27, + 7.59223090396554e-28, + 6.75850905941831e-28, + 6.05594086115429e-28, + 7.40387066310015e-28, + 6.04999720618854e-28, + 5.17923583652293e-28, + 4.0858846895433e-28, + 6.47448369216067e-28, + 5.29992493931534e-28, + 5.63128808710364e-28, + 4.17695119955099e-28, + 5.15330384762416e-28, + 5.25185850011986e-28, + 6.17618171380047e-28, + 6.37119303086457e-28, + 6.8496876006444e-28, + 5.9229625229341e-28, + 5.99998795876176e-28, + 7.06136396740756e-28, + 9.93040926727483e-28, + 1.03668944015898e-27, + 9.60158757124091e-28, + 9.00247643555494e-28, + 1.01801330597124e-27, + 1.0893103182854e-27, + 1.30096252080506e-27, + 1.42018105948242e-27, + 1.53477820553937e-27, + 1.60452920542724e-27, + 1.45613806382105e-27, + 1.63671034304492e-27, + 1.88680147053984e-27, + 2.26659932677371e-27, + 2.57113808884621e-27, + 2.73584069155576e-27, + 2.84524835939992e-27, + 2.65294565386989e-27, + 2.52007318042733e-27, + 2.56347774832346e-27, + 2.73272493525925e-27, + 3.21972108535573e-27, + 3.64879308272645e-27, + 3.86875166703077e-27, + 3.77657059174972e-27, + 3.67917967079418e-27, + 3.84603321414093e-27, + 4.47110813921024e-27, + 5.19879478264214e-27, + 6.13707395634462e-27, + 6.88229484256763e-27, + 7.11935416561536e-27, + 7.15812015493665e-27, + 6.67142397990209e-27, + 6.35218112458219e-27, + 6.34510826220203e-27, + 6.90321809802859e-27, + 7.82871587803871e-27, + 8.52938477234511e-27, + 8.74335964163491e-27, + 8.6718822639457e-27, + 8.59495406258221e-27, + 8.90719500501358e-27, + 9.90176156668504e-27, + 1.14857395824068e-26, + 1.36017282525383e-26, + 1.53232356175871e-26, + 1.75024000506424e-26, + 1.8179765858316e-26, + 1.80631911188605e-26, + 1.70102948254892e-26, + 1.56536231218255e-26, + 1.51141962474665e-26, + 1.57847025841346e-26, + 1.72161452840856e-26, + 1.90909798194127e-26, + 1.96774621921165e-26, + 1.99812678813396e-26, + 1.96900102296014e-26, + 1.95126617395258e-26, + 2.06408915381352e-26, + 2.28866836725858e-26, + 2.57662977048169e-26, + 2.96637551360212e-26, + 3.34197426701549e-26, + 3.73735792971477e-26, + 4.03453827718193e-26, + 4.1291323324037e-26, + 4.07643587970195e-26, + 3.84067732691303e-26, + 3.6271000065179e-26, + 3.50502931147362e-26, + 3.48851626868318e-26, + 3.73778737646723e-26, + 3.97091132121154e-26, + 4.21773978585713e-26, + 4.21620738550362e-26, + 4.22087900183437e-26, + 4.27973060694892e-26, + 4.36010682588909e-26, + 4.60584575680881e-26, + 4.91428506793045e-26, + 5.4918846417406e-26, + 6.10573296817762e-26, + 6.83025566941932e-26, + 7.51469261186479e-26, + 8.08197962688924e-26, + 8.44082645474868e-26, + 8.4843465766735e-26, + 8.4126775729461e-26, + 8.14411830190209e-26, + 7.83636723569755e-26, + 7.60974711104334e-26, + 7.57877917471603e-26, + 7.77887132347866e-26, + 8.07522339055262e-26, + 8.32310316258138e-26, + 8.59015818152486e-26, + 8.67834106505007e-26, + 8.72244226406716e-26, + 8.84734167611993e-26, + 9.0711580597939e-26, + 9.51778147590566e-26, + 1.01490385328969e-25, + 1.09448341447976e-25, + 1.18257044868557e-25, + 1.28105938778111e-25, + 1.37732704252934e-25, + 1.47491161151436e-25, + 1.55090701035304e-25, + 1.60575752538508e-25, + 1.62886093543744e-25, + 1.62802718439262e-25, + 1.60288510229631e-25, + 1.57216917046401e-25, + 1.54475957021763e-25, + 1.534341089264e-25, + 1.5409967492982e-25, + 1.56495784865383e-25, + 1.60012763627009e-25, + 1.63952588489707e-25, + 1.67232066912218e-25, + 1.70167894480834e-25, + 1.7335194060265e-25, + 1.7602731663686e-25, + 1.79953556347172e-25, + 1.84084607422109e-25, + 1.89999243847493e-25, + 1.97490644310087e-25, + 2.05279021301286e-25, + 2.14427839223598e-25, + 2.24098369182092e-25, + 2.34362490982003e-25, + 2.44138366650567e-25, + 2.53826212075759e-25, + 2.62577562731292e-25, + 2.70621837640467e-25, + 2.76622780914432e-25, + 2.80661519633223e-25, + 2.82737499370866e-25, + 2.82968166962191e-25, + 2.82659484597549e-25, + 2.81717325255129e-25, + 2.82197485088881e-25, + 2.84600833360439e-25, + 2.88048754046166e-25, + 2.92686210579676e-25, + 2.98551267943544e-25, + 3.04464034622896e-25, + 3.09724266051229e-25, + 3.14551726028915e-25, + 3.18670379817661e-25, + 3.22330249380314e-25, + 3.25463784914917e-25, + 3.2854797958699e-25, + 3.31405330400124e-25, + 3.34398013095565e-25, + 3.38005272562664e-25, + 3.41218557614901e-25, + 3.45555599852459e-25, + 3.5043277615423e-25, + 3.55984911371566e-25, + 3.6264721972979e-25, + 3.70647322984569e-25, + 3.79188179306458e-25, + 3.88981479760571e-25, + 3.98973596432023e-25, + 4.08527421216539e-25, + 4.17464990288797e-25, + 4.25462181228461e-25, + 4.32317712812093e-25, + 4.38401384845366e-25, + 4.44216978945654e-25, + 4.49508440820886e-25, + 4.5516564927479e-25, + 4.60931329475278e-25, + 4.66173637960526e-25, + 4.70665064920011e-25, + 4.74051362440113e-25, + 4.75812011867871e-25, + 4.7570031038151e-25, + 4.73747927545327e-25, + 4.71027119364443e-25, + 4.66860282624977e-25, + 4.6288533265784e-25, + 4.57997871538082e-25, + 4.53548773884213e-25, + 4.49607272653461e-25, + 4.4568818824566e-25, + 4.42881930827398e-25, + 4.40157474149992e-25, + 4.38677564524713e-25, + 4.37182142194489e-25, + 4.37104565551326e-25, + 4.37919711899623e-25, + 4.39683352146027e-25, + 4.42484514000691e-25, + 4.47212673326828e-25, + 4.53157968174371e-25, + 4.60815452736499e-25, + 4.69376508835705e-25, + 4.78054316070552e-25, + 4.87030272266768e-25, + 4.95220907227656e-25, + 5.02898332230558e-25, + 5.09027352924287e-25, + 5.13210890748271e-25, + 5.15374454317407e-25, + 5.15075653533686e-25, + 5.13171378996911e-25, + 5.09303097809138e-25, + 5.03697164998998e-25, + 4.97218676726656e-25, + 4.89461758141549e-25, + 4.80937594526597e-25, + 4.72371968798565e-25, + 4.63859449875443e-25, + 4.55132403268817e-25, + 4.46787830232533e-25, + 4.38816245012175e-25, + 4.31406743009557e-25, + 4.24483372714822e-25, + 4.17474755876221e-25, + 4.11305082072427e-25, + 4.05554427636835e-25, + 4.0030863998631e-25, + 3.95393149188544e-25, + 3.90576318741963e-25, + 3.86085282288514e-25, + 3.81425489414328e-25, + 3.76584427585746e-25, + 3.71837025816073e-25, + 3.66911165810168e-25, + 3.61941817240908e-25, + 3.56533744970388e-25, + 3.51159798289664e-25, + 3.46140984744989e-25, + 3.41173597486151e-25, + 3.36073006491665e-25, + 3.30753716054224e-25, + 3.25252799457143e-25, + 3.19530424634748e-25, + 3.13651908668849e-25, + 3.07841534489121e-25, + 3.01880275032991e-25, + 2.95965464815758e-25, + 2.90194743931008e-25, + 2.84696394478385e-25, + 2.79488978476361e-25, + 2.74800171431914e-25, + 2.70053589638645e-25, + 2.65588371839819e-25, + 2.6091795190684e-25, + 2.56708069403319e-25, + 2.52375403058723e-25, + 2.48382556862202e-25, + 2.44458617524673e-25, + 2.40594587498642e-25, + 2.36627105488787e-25, + 2.32528309566254e-25, + 2.28436716651676e-25, + 2.24424313328781e-25, + 2.20515093141858e-25, + 2.16647251017334e-25, + 2.12914125517962e-25, + 2.09073684368918e-25, + 2.05335637747553e-25, + 2.01912040845123e-25, + 1.98301051757051e-25, + 1.94465669006103e-25, + 1.9057000954812e-25, + 1.86388414183128e-25, + 1.82241187234978e-25, + 1.78160123951627e-25, + 1.74179167768809e-25, + 1.70080577609513e-25, + 1.6615348247565e-25, + 1.62305192083274e-25, + 1.58738563014106e-25, + 1.55171430112041e-25, + 1.51949383874537e-25, + 1.48607225456117e-25, + 1.45360366466218e-25, + 1.42252183610792e-25, + 1.39462753606039e-25, + 1.36820899679147e-25, + 1.34377836247288e-25, + 1.3233906891166e-25, + 1.30492894377563e-25, + 1.28681831161393e-25, + 1.2663174075999e-25, + 1.2420229295933e-25, + 1.21412305909061e-25, + 1.18502869999655e-25, + 1.15382448376731e-25, + 1.12540171803974e-25, + 1.09558453899584e-25, + 1.06672010609186e-25, + 1.03896476876362e-25, + 1.01172047316729e-25, + 9.84602582159865e-26, + 9.58059205575365e-26, + 9.33099533469407e-26, + 9.08936155224937e-26, + 8.8491955813636e-26, + 8.64349280941688e-26, + 8.44550335501804e-26, + 8.25653238461191e-26, + 8.08238949115334e-26, + 7.94009406468206e-26, + 7.80797137891831e-26, + 7.67364989571709e-26, + 7.54965718671858e-26, + 7.43779079827743e-26, + 7.31964666857758e-26, + 7.2232944312979e-26, + 7.12771524608971e-26, + 7.0510155861502e-26, + 6.99911412369698e-26, + 6.96383131319201e-26, + 6.92914966812787e-26, + 6.85928437919081e-26, + 6.74428511228458e-26, + 6.59224480420957e-26, + 6.38840433633278e-26, + 6.15855599572905e-26, + 5.95772659493798e-26, + 5.76494205198861e-26, + 5.60397496087846e-26, + 5.46017309852595e-26, + 5.32285644696103e-26, + 5.22365816180628e-26, + 5.07899578121548e-26, + 4.93592723237266e-26, + 4.79667132541204e-26, + 4.68132550170927e-26, + 4.56404612656666e-26, + 4.48276475068268e-26, + 4.40173864033052e-26, + 4.35417448301629e-26, + 4.30681941574933e-26, + 4.28871372503407e-26, + 4.24230311436515e-26, + 4.22301315090103e-26, + 4.19685390394596e-26, + 4.18917236558952e-26, + 4.17687017488352e-26, + 4.22019512128238e-26, + 4.2557462015274e-26, + 4.31172890769302e-26, + 4.36741466155527e-26, + 4.41740590419353e-26, + 4.44874945269751e-26, + 4.42029497925774e-26, + 4.34317624813875e-26, + 4.21853858585038e-26, + 4.01907542789597e-26, + 3.80281792816081e-26, + 3.60902775684479e-26, + 3.41921953398109e-26, + 3.25291539681547e-26, + 3.10743399295997e-26, + 2.99157340432027e-26, + 2.89660087626517e-26, + 2.82185391364051e-26, + 2.76872520775773e-26, + 2.72983807771732e-26, + 2.65963957090044e-26, + 2.62737083974039e-26, + 2.56625477748644e-26, + 2.53980768456023e-26, + 2.49759827680602e-26, + 2.47017705195044e-26, + 2.47697548770545e-26, + 2.46973660414457e-26, + 2.47714280793403e-26, + 2.50190654239581e-26, + 2.56196471101148e-26, + 2.64525680175745e-26, + 2.72275203692747e-26, + 2.826728922873e-26, + 2.94507453632992e-26, + 3.05247618840877e-26, + 3.1005889405393e-26, + 3.1508637563266e-26, + 3.13148927506362e-26 +}; /** - * Cross section of ozone at wavelength 550nm and temperature 293K, in m-2/molecule. + * Returns the cross section of ozone at temperature 293k and a given wavelength in the visible spectrum. * - * @see https://www.iup.uni-bremen.de/gruppen/molspec/databases/referencespectra/o3spectra2011/index.html + * @param wavelength Wavelength, in nanometers, on `[280, 780)`. + * @return Ozone cross section at temperature 293k and the given wavelength, in m-2/molecule. */ template -constexpr T cross_section_440nm_293k = T(1.36017282525383e-26); +constexpr T cross_section_293k(T wavelength) +{ + int i = static_cast(wavelength); + int j = static_cast(wavelength + T(1)); + + if (i < 280 || j > 780) + return T(0); + + const T x = static_cast(cross_sections_280nm_780nm_293k[i - 280]); + const T y = static_cast(cross_sections_280nm_780nm_293k[j - 280]); + + return math::lerp(x, y, wavelength - static_cast(i)); +} /** * Calculates an ozone absorption coefficient (wavelength-dependent). * * @param cross_section Ozone cross section of a wavelength, in m-2/molecule. - * @param n Molecular number density of standard air, in molecules/m-3. - * @param c Concentration of ozone in the atmosphere, unitless. + * @param density Molecular number density of ozone, in mol/m-3. * * @return Ozone absorption coefficient. * * @see https://skyrenderer.blogspot.com/2012/10/ozone-absorption.html */ template -T absorption(T cross_section, T n, T c) +T absorption(T cross_section, T density) { - return cross_section * n * c; + return cross_section * density; } } // namespace ozone diff --git a/src/physics/light/exposure.hpp b/src/physics/light/exposure.hpp index f74db89..fa93e87 100644 --- a/src/physics/light/exposure.hpp +++ b/src/physics/light/exposure.hpp @@ -93,16 +93,16 @@ T to_luminance(T ev, T s, T k) } /** - * Exposure value to luminance. + * Exposure value to illuminance. * * @param ev Exposure value. * @param s ISO arithmetic speed. A value of `100` corresponds to ISO 100. - * @param k Incident-light meter calibration constant. A common value is `250`. + * @param c Incident-light meter calibration constant. A common value is `250`. * * @return Illuminance, in lux. */ template -T to_luminance(T ev, T s, T c) +T to_illuminance(T ev, T s, T c) { return (c * std::exp2(ev)) / s; } diff --git a/src/physics/time/jd.hpp b/src/physics/time/jd.hpp index 3b1a3b8..258877b 100644 --- a/src/physics/time/jd.hpp +++ b/src/physics/time/jd.hpp @@ -26,6 +26,14 @@ namespace time { /// Julian day (JD). namespace jd { +/// Number of Julian days per Julian century. +template +constexpr T days_per_century = T{36525}; + +/// Number of Julian centuries per Julian day. +template +constexpr T centuries_per_day = T{1} / T{36525}; + /** * Converts JD to UT1. * diff --git a/src/render/context.hpp b/src/render/context.hpp index dcc6c7e..15311bc 100644 --- a/src/render/context.hpp +++ b/src/render/context.hpp @@ -53,9 +53,18 @@ struct context /// Camera culling volume. const geom::bounding_volume* camera_culling_volume; - /// Near clipping plane of the camera + /// Near clipping plane of the camera. geom::plane clip_near; + /// Camera view matrix. + float4x4 view; + + /// Camera projection matrix. + float4x4 projection; + + /// Camera view projection matrix. + float4x4 view_projection; + /// Camera exposure normalization factor. float exposure; diff --git a/src/render/passes/ground-pass.cpp b/src/render/passes/ground-pass.cpp index 1a8b5c8..05c6f57 100644 --- a/src/render/passes/ground-pass.cpp +++ b/src/render/passes/ground-pass.cpp @@ -86,10 +86,10 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; float4x4 model = math::scale(math::matrix4::identity, model_scale); - float4x4 view = math::resize<4, 4>(math::resize<3, 3>(camera.get_view_tween().interpolate(ctx.alpha))); + float4x4 view = math::resize<4, 4>(math::resize<3, 3>(ctx.view)); float4x4 model_view = view * model; - float4x4 projection = camera.get_projection_tween().interpolate(ctx.alpha); - float4x4 view_projection = camera.get_view_projection_tween().interpolate(ctx.alpha); + const float4x4& projection = ctx.projection; + const float4x4& view_projection = ctx.view_projection; float4x4 model_view_projection = projection * model_view; diff --git a/src/render/passes/material-pass.cpp b/src/render/passes/material-pass.cpp index 4448a55..1b5f18b 100644 --- a/src/render/passes/material-pass.cpp +++ b/src/render/passes/material-pass.cpp @@ -120,9 +120,9 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; const float3& camera_position = ctx.camera_transform.translation; - float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); - float4x4 projection = ctx.camera->get_projection_tween().interpolate(ctx.alpha); - float4x4 view_projection = projection * view; + const float4x4& view = ctx.view; + const float4x4& projection = ctx.projection; + const float4x4& view_projection = ctx.view_projection; float4x4 model_view_projection; float4x4 model; float4x4 model_view; diff --git a/src/render/passes/sky-pass.cpp b/src/render/passes/sky-pass.cpp index f040914..adfca21 100644 --- a/src/render/passes/sky-pass.cpp +++ b/src/render/passes/sky-pass.cpp @@ -46,6 +46,7 @@ #include #include #include +#include namespace render { @@ -64,10 +65,6 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe stars_model_vao(nullptr), star_material(nullptr), star_shader_program(nullptr), - clouds_model(nullptr), - clouds_model_vao(nullptr), - cloud_material(nullptr), - cloud_shader_program(nullptr), observer_position_tween({0, 0, 0}, math::lerp), sun_position_tween(float3{1.0f, 0.0f, 0.0f}, math::lerp), sun_luminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), @@ -81,6 +78,8 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe moon_sunlight_illuminance_tween(float3{0, 0, 0}, math::lerp), moon_planetlight_direction_tween(float3{0, 0, 0}, math::lerp), moon_planetlight_illuminance_tween(float3{0, 0, 0}, math::lerp), + moon_illuminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), + render_transmittance_lut(false), magnification(1.0f) { // Build quad VBO and VAO @@ -107,15 +106,13 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe quad_vao->bind(render::vertex_attribute::position, quad_position_attribute); // Create transmittance LUT texture and framebuffer (32F color, no depth) - int transmittance_width = 256; - int transmittance_height = 64; - transmittance_inverse_lut_resolution = {1.0f / static_cast(transmittance_width), 1.0f / static_cast(transmittance_height)}; - transmittance_texture = new gl::texture_2d(transmittance_width, transmittance_height, gl::pixel_type::float_32, gl::pixel_format::rgb); - transmittance_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); - transmittance_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); - transmittance_texture->set_max_anisotropy(0.0f); - transmittance_framebuffer = new gl::framebuffer(transmittance_width, transmittance_height); - transmittance_framebuffer->attach(gl::framebuffer_attachment_type::color, transmittance_texture); + transmittance_lut_texture = new gl::texture_2d(256, 64, gl::pixel_type::float_32, gl::pixel_format::rgb); + transmittance_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + transmittance_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + transmittance_lut_texture->set_max_anisotropy(0.0f); + transmittance_lut_framebuffer = new gl::framebuffer({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); + transmittance_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, transmittance_lut_texture); + transmittance_lut_resolution = {static_cast(transmittance_lut_texture->get_width()), static_cast(transmittance_lut_texture->get_height())}; // Load transmittance LUT shader transmittance_shader_program = resource_manager->load("transmittance-lut.glsl"); @@ -124,13 +121,41 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe transmittance_mie_parameters_input = transmittance_shader_program->get_input("mie_parameters"); transmittance_ozone_distribution_input = transmittance_shader_program->get_input("ozone_distribution"); transmittance_ozone_absorption_input = transmittance_shader_program->get_input("ozone_absorption"); - transmittance_inverse_lut_resolution_input = transmittance_shader_program->get_input("inverse_lut_resolution"); + transmittance_resolution_input = transmittance_shader_program->get_input("resolution"); + + // Create sky LUT texture and framebuffer (32F color, no depth) + int sky_lut_width = 200; + int sky_lut_height = 100; + sky_lut_resolution = {static_cast(sky_lut_width), static_cast(sky_lut_height)}; + sky_lut_texture = new gl::texture_2d(sky_lut_width, sky_lut_height, gl::pixel_type::float_32, gl::pixel_format::rgb); + sky_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + sky_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + sky_lut_texture->set_max_anisotropy(0.0f); + sky_lut_framebuffer = new gl::framebuffer(sky_lut_width, sky_lut_height); + sky_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, sky_lut_texture); + + // Load sky LUT shader + sky_lut_shader_program = resource_manager->load("sky-illuminance-lut.glsl"); + sky_lut_light_direction_input = sky_lut_shader_program->get_input("light_direction"); + sky_lut_light_illuminance_input = sky_lut_shader_program->get_input("light_illuminance"); + sky_lut_atmosphere_radii_input = sky_lut_shader_program->get_input("atmosphere_radii"); + sky_lut_observer_position_input = sky_lut_shader_program->get_input("observer_position"); + sky_lut_rayleigh_parameters_input = sky_lut_shader_program->get_input("rayleigh_parameters"); + sky_lut_mie_parameters_input = sky_lut_shader_program->get_input("mie_parameters"); + sky_lut_ozone_distribution_input = sky_lut_shader_program->get_input("ozone_distribution"); + sky_lut_ozone_absorption_input = sky_lut_shader_program->get_input("ozone_absorption"); + sky_lut_airglow_illuminance_input = sky_lut_shader_program->get_input("airglow_illuminance"); + sky_lut_resolution_input = sky_lut_shader_program->get_input("resolution"); + sky_lut_transmittance_lut_input = sky_lut_shader_program->get_input("transmittance_lut"); + sky_lut_transmittance_lut_resolution_input = sky_lut_shader_program->get_input("transmittance_lut_resolution"); } sky_pass::~sky_pass() { - delete transmittance_framebuffer; - delete transmittance_texture; + delete sky_lut_framebuffer; + delete sky_lut_texture; + delete transmittance_lut_framebuffer; + delete transmittance_lut_texture; delete quad_vao; delete quad_vbo; } @@ -143,33 +168,35 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const glEnable(GL_CULL_FACE); glCullFace(GL_BACK); - // Render transmittance LUT - auto transmittance_viewport = transmittance_framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(transmittance_viewport), std::get<1>(transmittance_viewport)); - rasterizer->use_framebuffer(*transmittance_framebuffer); - rasterizer->use_program(*transmittance_shader_program); - transmittance_atmosphere_radii_input->upload(atmosphere_radii); - transmittance_rayleigh_parameters_input->upload(rayleigh_parameters); - transmittance_mie_parameters_input->upload(mie_parameters); - transmittance_ozone_distribution_input->upload(ozone_distribution); - transmittance_ozone_absorption_input->upload(ozone_absorption); - if (transmittance_inverse_lut_resolution_input) - transmittance_inverse_lut_resolution_input->upload(transmittance_inverse_lut_resolution); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - - rasterizer->use_framebuffer(*framebuffer); - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; + // Render transmittance LUT if transmittance parameters have been altered. + if (render_transmittance_lut) + { + // Render transmittance LUT + rasterizer->set_viewport(0, 0, transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()); + rasterizer->use_framebuffer(*transmittance_lut_framebuffer); + rasterizer->use_program(*transmittance_shader_program); + transmittance_atmosphere_radii_input->upload(atmosphere_radii); + transmittance_rayleigh_parameters_input->upload(rayleigh_parameters); + transmittance_mie_parameters_input->upload(mie_parameters); + transmittance_ozone_distribution_input->upload(ozone_distribution); + transmittance_ozone_absorption_input->upload(ozone_absorption); + if (transmittance_resolution_input) + transmittance_resolution_input->upload(transmittance_lut_resolution); + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + + // Don't render transmittance LUT next frame unless parameters have changed. + render_transmittance_lut = false; + } + // Construct matrices const scene::camera& camera = *ctx.camera; float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; float4x4 model = math::scale(math::matrix4::identity, model_scale); - float4x4 view = math::resize<4, 4>(math::resize<3, 3>(camera.get_view_tween().interpolate(ctx.alpha))); + float4x4 view = math::resize<4, 4>(math::resize<3, 3>(ctx.view)); float4x4 model_view = view * model; - float4x4 projection = camera.get_projection_tween().interpolate(ctx.alpha); + const float4x4& projection = ctx.projection; float4x4 view_projection = projection * view; float4x4 model_view_projection = projection * model_view; @@ -191,6 +218,47 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const float3 sun_luminance = sun_luminance_tween.interpolate(ctx.alpha) * ctx.exposure; float3 sun_illuminance = sun_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure; + float3 moon_position = moon_position_tween.interpolate(ctx.alpha); + float3 moon_direction = math::normalize(moon_position); + float3 moon_illuminance = moon_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure; + float moon_angular_radius = moon_angular_radius_tween.interpolate(ctx.alpha) * magnification; + + float sun_y = color::aces::ap1.luminance(sun_transmitted_illuminance); + float moon_y = color::aces::ap1.luminance(moon_transmitted_illuminance); + float3 dominant_light_direction = (sun_y > moon_y) ? sun_direction : moon_direction; + float3 dominant_light_illuminance = (sun_y > moon_y) ? sun_illuminance : moon_illuminance; + + if (moon_y > sun_y) + sun_luminance *= 0.0f; + + // Render sky illuminance LUT + auto sky_lut_viewport = sky_lut_framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(sky_lut_viewport), std::get<1>(sky_lut_viewport)); + rasterizer->use_framebuffer(*sky_lut_framebuffer); + rasterizer->use_program(*sky_lut_shader_program); + sky_lut_light_direction_input->upload(dominant_light_direction); + sky_lut_light_illuminance_input->upload(dominant_light_illuminance); + sky_lut_atmosphere_radii_input->upload(atmosphere_radii); + sky_lut_observer_position_input->upload(observer_position); + sky_lut_rayleigh_parameters_input->upload(rayleigh_parameters); + sky_lut_mie_parameters_input->upload(mie_parameters); + sky_lut_ozone_distribution_input->upload(ozone_distribution); + sky_lut_ozone_absorption_input->upload(ozone_absorption); + sky_lut_airglow_illuminance_input->upload(airglow_illuminance * ctx.exposure); + if (sky_lut_resolution_input) + sky_lut_resolution_input->upload(sky_lut_resolution); + sky_lut_transmittance_lut_input->upload(transmittance_lut_texture); + if (sky_lut_transmittance_lut_resolution_input) + sky_lut_transmittance_lut_resolution_input->upload(transmittance_lut_resolution); + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + + + + rasterizer->use_framebuffer(*framebuffer); + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; + // Draw atmosphere if (sky_model) { @@ -203,61 +271,26 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const mouse_input->upload(mouse_position); if (resolution_input) resolution_input->upload(resolution); - if (time_input) - time_input->upload(ctx.t); - if (exposure_input) - exposure_input->upload(ctx.exposure); - if (sun_direction_input) - sun_direction_input->upload(sun_direction); + if (light_direction_input) + light_direction_input->upload(dominant_light_direction); if (sun_luminance_input) sun_luminance_input->upload(sun_luminance); - if (sun_illuminance_input) - sun_illuminance_input->upload(sun_illuminance); if (sun_angular_radius_input) sun_angular_radius_input->upload(sun_angular_radius * magnification); if (atmosphere_radii_input) atmosphere_radii_input->upload(atmosphere_radii); if (observer_position_input) observer_position_input->upload(observer_position); - if (rayleigh_parameters_input) - rayleigh_parameters_input->upload(rayleigh_parameters); - if (mie_parameters_input) - mie_parameters_input->upload(mie_parameters); - if (ozone_distribution_input) - ozone_distribution_input->upload(ozone_distribution); - if (ozone_absorption_input) - ozone_absorption_input->upload(ozone_absorption); - if (transmittance_lut_input) - transmittance_lut_input->upload(transmittance_texture); - if (inverse_transmittance_lut_resolution_input) - inverse_transmittance_lut_resolution_input->upload(transmittance_inverse_lut_resolution); + if (sky_illuminance_lut_input) + sky_illuminance_lut_input->upload(sky_lut_texture); + if (sky_illuminance_lut_resolution_input) + sky_illuminance_lut_resolution_input->upload(sky_lut_resolution); sky_material->upload(ctx.alpha); rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count); } - // Draw clouds - if (clouds_model) - { - rasterizer->use_program(*cloud_shader_program); - - if (cloud_model_view_projection_input) - cloud_model_view_projection_input->upload(model_view_projection); - if (cloud_sun_direction_input) - cloud_sun_direction_input->upload(sun_direction); - if (cloud_sun_illuminance_input) - cloud_sun_illuminance_input->upload(sun_illuminance); - if (cloud_camera_position_input) - cloud_camera_position_input->upload(ctx.camera_transform.translation); - if (cloud_camera_exposure_input) - cloud_camera_exposure_input->upload(ctx.exposure); - - cloud_material->upload(ctx.alpha); - - rasterizer->draw_arrays(*clouds_model_vao, clouds_model_drawing_mode, clouds_model_start_index, clouds_model_index_count); - } - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); //glBlendFunc(GL_ONE, GL_ONE); @@ -288,8 +321,6 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const } // Draw moon model - float3 moon_position = moon_position_tween.interpolate(ctx.alpha); - float moon_angular_radius = moon_angular_radius_tween.interpolate(ctx.alpha) * magnification; //if (moon_position.y >= -moon_angular_radius) { float moon_distance = (clip_near + clip_far) * 0.5f; @@ -352,20 +383,13 @@ void sky_pass::set_sky_model(const model* model) model_view_projection_input = sky_shader_program->get_input("model_view_projection"); mouse_input = sky_shader_program->get_input("mouse"); resolution_input = sky_shader_program->get_input("resolution"); - time_input = sky_shader_program->get_input("time"); - exposure_input = sky_shader_program->get_input("camera.exposure"); - sun_direction_input = sky_shader_program->get_input("sun_direction"); + light_direction_input = sky_shader_program->get_input("light_direction"); sun_luminance_input = sky_shader_program->get_input("sun_luminance"); - sun_illuminance_input = sky_shader_program->get_input("sun_illuminance"); sun_angular_radius_input = sky_shader_program->get_input("sun_angular_radius"); atmosphere_radii_input = sky_shader_program->get_input("atmosphere_radii"); observer_position_input = sky_shader_program->get_input("observer_position"); - rayleigh_parameters_input = sky_shader_program->get_input("rayleigh_parameters"); - mie_parameters_input = sky_shader_program->get_input("mie_parameters"); - ozone_distribution_input = sky_shader_program->get_input("ozone_distribution"); - ozone_absorption_input = sky_shader_program->get_input("ozone_absorption"); - transmittance_lut_input = sky_shader_program->get_input("transmittance_lut"); - inverse_transmittance_lut_resolution_input = sky_shader_program->get_input("inverse_transmittance_lut_resolution"); + sky_illuminance_lut_input = sky_shader_program->get_input("sky_illuminance_lut"); + sky_illuminance_lut_resolution_input = sky_shader_program->get_input("sky_illuminance_lut_resolution"); } } } @@ -451,43 +475,6 @@ void sky_pass::set_stars_model(const model* model) } } -void sky_pass::set_clouds_model(const model* model) -{ - clouds_model = model; - - if (clouds_model) - { - clouds_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) - { - cloud_material = group->get_material(); - clouds_model_drawing_mode = group->get_drawing_mode(); - clouds_model_start_index = group->get_start_index(); - clouds_model_index_count = group->get_index_count(); - } - - if (cloud_material) - { - cloud_shader_program = cloud_material->get_shader_program(); - - if (cloud_shader_program) - { - cloud_model_view_projection_input = cloud_shader_program->get_input("model_view_projection"); - cloud_sun_direction_input = cloud_shader_program->get_input("sun_direction"); - cloud_sun_illuminance_input = cloud_shader_program->get_input("sun_illuminance"); - cloud_camera_position_input = cloud_shader_program->get_input("camera.position"); - cloud_camera_exposure_input = cloud_shader_program->get_input("camera.exposure"); - } - } - } - else - { - clouds_model = nullptr; - } -} - void sky_pass::update_tweens() { observer_position_tween.update(); @@ -504,6 +491,7 @@ void sky_pass::update_tweens() moon_sunlight_illuminance_tween.update(); moon_planetlight_direction_tween.update(); moon_planetlight_illuminance_tween.update(); + moon_illuminance_tween.update(); } void sky_pass::set_magnification(float magnification) @@ -522,9 +510,10 @@ void sky_pass::set_sun_position(const float3& position) sun_position_tween[1] = position; } -void sky_pass::set_sun_illuminance(const float3& illuminance) +void sky_pass::set_sun_illuminance(const float3& illuminance, const float3& transmitted_illuminance) { sun_illuminance_tween[1] = illuminance; + sun_transmitted_illuminance = transmitted_illuminance; } void sky_pass::set_sun_luminance(const float3& luminance) @@ -543,6 +532,9 @@ void sky_pass::set_planet_radius(float radius) atmosphere_radii.y = atmosphere_radii.x + atmosphere_upper_limit; atmosphere_radii.z = atmosphere_radii.y * atmosphere_radii.y; observer_position_tween[1] = {0.0f, atmosphere_radii.x + observer_elevation, 0.0f}; + + // Trigger transmittance LUT render + render_transmittance_lut = true; } void sky_pass::set_atmosphere_upper_limit(float limit) @@ -550,6 +542,9 @@ void sky_pass::set_atmosphere_upper_limit(float limit) atmosphere_upper_limit = limit; atmosphere_radii.y = atmosphere_radii.x + atmosphere_upper_limit; atmosphere_radii.z = atmosphere_radii.y * atmosphere_radii.y; + + // Trigger transmittance LUT render + render_transmittance_lut = true; } void sky_pass::set_observer_elevation(float elevation) @@ -567,17 +562,23 @@ void sky_pass::set_rayleigh_parameters(float scale_height, const float3& scatter scattering.y, scattering.z }; + + // Trigger transmittance LUT render + render_transmittance_lut = true; } -void sky_pass::set_mie_parameters(float scale_height, float scattering, float absorption, float anisotropy) +void sky_pass::set_mie_parameters(float scale_height, float scattering, float extinction, float anisotropy) { mie_parameters = { -1.0f / scale_height, scattering, - absorption, + extinction, anisotropy }; + + // Trigger transmittance LUT render + render_transmittance_lut = true; } void sky_pass::set_ozone_parameters(float lower_limit, float upper_limit, float mode, const float3& absorption) @@ -589,6 +590,14 @@ void sky_pass::set_ozone_parameters(float lower_limit, float upper_limit, float mode }; ozone_absorption = absorption; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +void sky_pass::set_airglow_illuminance(const float3& illuminance) +{ + airglow_illuminance = illuminance; } void sky_pass::set_moon_position(const float3& position) @@ -626,6 +635,22 @@ void sky_pass::set_moon_planetlight_illuminance(const float3& illuminance) moon_planetlight_illuminance_tween[1] = illuminance; } +void sky_pass::set_moon_illuminance(const float3& illuminance, const float3& transmitted_illuminance) +{ + moon_illuminance_tween[1] = illuminance; + moon_transmitted_illuminance = transmitted_illuminance; +} + +void sky_pass::set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height) +{ + transmittance_lut_texture->resize(width, height, nullptr); + transmittance_lut_framebuffer->resize({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); + transmittance_lut_resolution = {static_cast(width), static_cast(height)}; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + void sky_pass::handle_event(const mouse_moved_event& event) { mouse_position = {static_cast(event.x), static_cast(event.y)}; diff --git a/src/render/passes/sky-pass.hpp b/src/render/passes/sky-pass.hpp index 9216fb7..d561ac0 100644 --- a/src/render/passes/sky-pass.hpp +++ b/src/render/passes/sky-pass.hpp @@ -60,20 +60,20 @@ public: void set_sky_model(const model* model); void set_moon_model(const model* model); void set_stars_model(const model* model); - void set_clouds_model(const model* model); void set_icrf_to_eus(const math::transformation::se3& transformation); void set_sun_position(const float3& position); void set_sun_luminance(const float3& luminance); - void set_sun_illuminance(const float3& illuminance); + void set_sun_illuminance(const float3& illuminance, const float3& transmitted_illuminance); void set_sun_angular_radius(float radius); void set_planet_radius(float radius); void set_atmosphere_upper_limit(float limit); void set_observer_elevation(float elevation); void set_rayleigh_parameters(float scale_height, const float3& scattering); - void set_mie_parameters(float scale_height, float scattering, float absorption, float anisotropy); + void set_mie_parameters(float scale_height, float scattering, float extinction, float anisotropy); void set_ozone_parameters(float lower_limit, float upper_limit, float mode, const float3& absorption); + void set_airglow_illuminance(const float3& illuminance); void set_moon_position(const float3& position); void set_moon_rotation(const math::quaternion& rotation); @@ -82,42 +82,62 @@ public: void set_moon_sunlight_illuminance(const float3& illuminance); void set_moon_planetlight_direction(const float3& direction); void set_moon_planetlight_illuminance(const float3& illuminance); + void set_moon_illuminance(const float3& illuminance, const float3& transmitted_illuminance); + + /** + * Sets the resolution of transmittance LUT. + * + * @param width Transmittance LUT width, in pixels. + * @param height Transmittance LUT height, in pixels. + */ + void set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height); private: virtual void handle_event(const mouse_moved_event& event); gl::vertex_buffer* quad_vbo; gl::vertex_array* quad_vao; - gl::texture_2d* transmittance_texture; - gl::framebuffer* transmittance_framebuffer; - float2 transmittance_inverse_lut_resolution; + gl::texture_2d* transmittance_lut_texture; + gl::framebuffer* transmittance_lut_framebuffer; + float2 transmittance_lut_resolution; gl::shader_program* transmittance_shader_program; const gl::shader_input* transmittance_atmosphere_radii_input; const gl::shader_input* transmittance_rayleigh_parameters_input; const gl::shader_input* transmittance_mie_parameters_input; const gl::shader_input* transmittance_ozone_distribution_input; const gl::shader_input* transmittance_ozone_absorption_input; - const gl::shader_input* transmittance_inverse_lut_resolution_input; + const gl::shader_input* transmittance_resolution_input; + mutable bool render_transmittance_lut; + + gl::texture_2d* sky_lut_texture; + gl::framebuffer* sky_lut_framebuffer; + gl::shader_program* sky_lut_shader_program; + float2 sky_lut_resolution; + const gl::shader_input* sky_lut_light_direction_input; + const gl::shader_input* sky_lut_light_illuminance_input; + const gl::shader_input* sky_lut_atmosphere_radii_input; + const gl::shader_input* sky_lut_observer_position_input; + const gl::shader_input* sky_lut_rayleigh_parameters_input; + const gl::shader_input* sky_lut_mie_parameters_input; + const gl::shader_input* sky_lut_ozone_distribution_input; + const gl::shader_input* sky_lut_ozone_absorption_input; + const gl::shader_input* sky_lut_airglow_illuminance_input; + const gl::shader_input* sky_lut_resolution_input; + const gl::shader_input* sky_lut_transmittance_lut_input; + const gl::shader_input* sky_lut_transmittance_lut_resolution_input; gl::shader_program* sky_shader_program; const gl::shader_input* model_view_projection_input; const gl::shader_input* mouse_input; const gl::shader_input* resolution_input; - const gl::shader_input* time_input; - const gl::shader_input* exposure_input; - const gl::shader_input* sun_direction_input; + const gl::shader_input* light_direction_input; const gl::shader_input* sun_luminance_input; - const gl::shader_input* sun_illuminance_input; const gl::shader_input* sun_angular_radius_input; const gl::shader_input* atmosphere_radii_input; const gl::shader_input* observer_position_input; - const gl::shader_input* rayleigh_parameters_input; - const gl::shader_input* mie_parameters_input; - const gl::shader_input* ozone_distribution_input; - const gl::shader_input* ozone_absorption_input; - const gl::shader_input* transmittance_lut_input; - const gl::shader_input* inverse_transmittance_lut_resolution_input; + const gl::shader_input* sky_illuminance_lut_input; + const gl::shader_input* sky_illuminance_lut_resolution_input; gl::shader_program* moon_shader_program; const gl::shader_input* moon_model_input; @@ -155,13 +175,6 @@ private: const gl::shader_input* star_exposure_input; const gl::shader_input* star_distance_input; - const model* clouds_model; - const material* cloud_material; - const gl::vertex_array* clouds_model_vao; - gl::drawing_mode clouds_model_drawing_mode; - std::size_t clouds_model_start_index; - std::size_t clouds_model_index_count; - gl::shader_program* cloud_shader_program; const gl::shader_input* cloud_model_view_projection_input; const gl::shader_input* cloud_sun_direction_input; @@ -169,14 +182,12 @@ private: const gl::shader_input* cloud_camera_position_input; const gl::shader_input* cloud_camera_exposure_input; - const gl::texture_2d* blue_noise_map; - const gl::texture_2d* sky_gradient; - const gl::texture_2d* sky_gradient2; float2 mouse_position; tween sun_position_tween; tween sun_luminance_tween; tween sun_illuminance_tween; + float3 sun_transmitted_illuminance; tween icrf_to_eus_translation; tween> icrf_to_eus_rotation; @@ -187,6 +198,9 @@ private: tween moon_sunlight_illuminance_tween; tween moon_planetlight_direction_tween; tween moon_planetlight_illuminance_tween; + tween moon_illuminance_tween; + float3 moon_transmitted_illuminance; + float sun_angular_radius; float atmosphere_upper_limit; @@ -197,6 +211,7 @@ private: float4 mie_parameters; float3 ozone_distribution; float3 ozone_absorption; + float3 airglow_illuminance; float magnification; }; diff --git a/src/render/renderer.cpp b/src/render/renderer.cpp index 68016c7..f56fa8f 100644 --- a/src/render/renderer.cpp +++ b/src/render/renderer.cpp @@ -107,6 +107,9 @@ void renderer::render(float t, float dt, float alpha, const scene::collection& c ctx.camera_forward = ctx.camera_transform.rotation * config::global_forward; ctx.camera_up = ctx.camera_transform.rotation * config::global_up; ctx.clip_near = camera->get_view_frustum().get_near(); ///< @TODO: tween this + ctx.view = camera->get_view_tween().interpolate(alpha); + ctx.projection = camera->get_projection_tween().interpolate(alpha); + ctx.view_projection = ctx.projection * ctx.view; ctx.exposure = std::exp2(-camera->get_exposure_tween().interpolate(alpha)); // Create render queue diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp index e9aa406..d42e598 100644 --- a/src/resources/entity-archetype-loader.cpp +++ b/src/resources/entity-archetype-loader.cpp @@ -44,32 +44,37 @@ static bool load_component_atmosphere(entity::archetype& archetype, const json& component.upper_limit = element["upper_limit"].get(); if (element.contains("index_of_refraction")) component.index_of_refraction = element["index_of_refraction"].get(); - if (element.contains("air_concentration")) - component.air_concentration = element["air_concentration"].get(); + if (element.contains("rayleigh_concentration")) + component.rayleigh_concentration = element["rayleigh_concentration"].get(); if (element.contains("rayleigh_scale_height")) component.rayleigh_scale_height = element["rayleigh_scale_height"].get(); - if (element.contains("rayleigh_density")) - component.rayleigh_density = element["rayleigh_density"].get(); + if (element.contains("mie_concentration")) + component.mie_concentration = element["mie_concentration"].get(); if (element.contains("mie_scale_height")) component.mie_scale_height = element["mie_scale_height"].get(); - if (element.contains("mie_density")) - component.mie_density = element["mie_density"].get(); if (element.contains("mie_anisotropy")) component.mie_anisotropy = element["mie_anisotropy"].get(); + if (element.contains("mie_albedo")) + component.mie_albedo = element["mie_albedo"].get(); + if (element.contains("ozone_concentration")) + component.ozone_concentration = element["ozone_concentration"].get(); if (element.contains("ozone_lower_limit")) component.ozone_lower_limit = element["ozone_lower_limit"].get(); if (element.contains("ozone_upper_limit")) component.ozone_upper_limit = element["ozone_upper_limit"].get(); if (element.contains("ozone_mode")) component.ozone_mode = element["ozone_mode"].get(); - if (element.contains("ozone_concentration")) - component.ozone_concentration = element["ozone_concentration"].get(); - if (element.contains("airglow")) - component.airglow = element["airglow"].get(); + if (element.contains("airglow_illuminance")) + { + const auto& airglow_illuminance = element["airglow_illuminance"]; + component.airglow_illuminance.x = airglow_illuminance[0].get(); + component.airglow_illuminance.y = airglow_illuminance[1].get(); + component.airglow_illuminance.z = airglow_illuminance[2].get(); + } archetype.set(component); diff --git a/src/resources/material-loader.cpp b/src/resources/material-loader.cpp index a469037..82ff427 100644 --- a/src/resources/material-loader.cpp +++ b/src/resources/material-loader.cpp @@ -161,7 +161,7 @@ static bool load_vector_property(render::material* material, const std::string& T value; std::size_t j = 0; for (const auto& value_element: vector_element) - value[j++] = value_element.get(); + value[j++] = value_element.get(); // Set property values property->set_value(i++, value); @@ -176,7 +176,7 @@ static bool load_vector_property(render::material* material, const std::string& T value; std::size_t i = 0; for (const auto& value_element: json) - value[i++] = value_element.get(); + value[i++] = value_element.get(); // Set property values property->set_value(value);