sky.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. Copyright (C) 2020 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU Lesser General Public License as published by
  7. the Free Software Foundation; either version 2.1 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public License along
  14. with this program; if not, write to the Free Software Foundation, Inc.,
  15. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. */
  17. #include <cmath>
  18. #include "sky.h"
  19. #include <ITexture.h>
  20. #include <IVideoDriver.h>
  21. #include <ISceneManager.h>
  22. #include <ICameraSceneNode.h>
  23. #include <S3DVertex.h>
  24. #include "client/tile.h"
  25. #include "noise.h" // easeCurve
  26. #include "profiler.h"
  27. #include "util/numeric.h"
  28. #include "client/renderingengine.h"
  29. #include "settings.h"
  30. #include "camera.h" // CameraModes
  31. using namespace irr::core;
  32. static video::SMaterial baseMaterial()
  33. {
  34. video::SMaterial mat;
  35. mat.Lighting = false;
  36. mat.ZBuffer = video::ECFN_DISABLED;
  37. mat.ZWriteEnable = video::EZW_OFF;
  38. mat.AntiAliasing = 0;
  39. mat.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
  40. mat.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
  41. mat.BackfaceCulling = false;
  42. return mat;
  43. }
  44. static inline void disableTextureFiltering(video::SMaterial &mat)
  45. {
  46. mat.forEachTexture([] (auto &tex) {
  47. tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
  48. tex.MagFilter = video::ETMAGF_NEAREST;
  49. tex.AnisotropicFilter = 0;
  50. });
  51. }
  52. Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShaderSource *ssrc) :
  53. scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(),
  54. rendering_engine->get_scene_manager(), id)
  55. {
  56. m_seed = (u64)myrand() << 32 | myrand();
  57. setAutomaticCulling(scene::EAC_OFF);
  58. m_box.MaxEdge.set(0, 0, 0);
  59. m_box.MinEdge.set(0, 0, 0);
  60. m_enable_shaders = g_settings->getBool("enable_shaders");
  61. m_sky_params = SkyboxDefaults::getSkyDefaults();
  62. m_sun_params = SkyboxDefaults::getSunDefaults();
  63. m_moon_params = SkyboxDefaults::getMoonDefaults();
  64. m_star_params = SkyboxDefaults::getStarDefaults();
  65. // Create materials
  66. m_materials[0] = baseMaterial();
  67. // FIXME: shouldn't this check m_enable_shaders?
  68. m_materials[0].MaterialType = ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA)).material;
  69. m_materials[0].Lighting = true;
  70. m_materials[0].ColorMaterial = video::ECM_NONE;
  71. m_materials[1] = baseMaterial();
  72. m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  73. m_materials[2] = baseMaterial();
  74. m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png"));
  75. m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  76. setSunTexture(m_sun_params.texture, m_sun_params.tonemap, tsrc);
  77. setMoonTexture(m_moon_params.texture, m_moon_params.tonemap, tsrc);
  78. for (int i = 5; i < 11; i++) {
  79. m_materials[i] = baseMaterial();
  80. m_materials[i].Lighting = true;
  81. m_materials[i].MaterialType = video::EMT_SOLID;
  82. }
  83. m_directional_colored_fog = g_settings->getBool("directional_colored_fog");
  84. m_sky_params.body_orbit_tilt = g_settings->getFloat("shadow_sky_body_orbit_tilt", -60., 60.);
  85. m_sky_params.fog_start = rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f);
  86. setStarCount(1000);
  87. }
  88. void Sky::OnRegisterSceneNode()
  89. {
  90. if (IsVisible)
  91. SceneManager->registerNodeForRendering(this, scene::ESNRP_SKY_BOX);
  92. scene::ISceneNode::OnRegisterSceneNode();
  93. }
  94. void Sky::render()
  95. {
  96. video::IVideoDriver *driver = SceneManager->getVideoDriver();
  97. scene::ICameraSceneNode *camera = SceneManager->getActiveCamera();
  98. if (!camera || !driver)
  99. return;
  100. ScopeProfiler sp(g_profiler, "Sky::render()", SPT_AVG, PRECISION_MICRO);
  101. // Draw perspective skybox
  102. core::matrix4 translate(AbsoluteTransformation);
  103. translate.setTranslation(camera->getAbsolutePosition());
  104. // Draw the sky box between the near and far clip plane
  105. const f32 viewDistance = (camera->getNearValue() + camera->getFarValue()) * 0.5f;
  106. core::matrix4 scale;
  107. scale.setScale(core::vector3df(viewDistance, viewDistance, viewDistance));
  108. driver->setTransform(video::ETS_WORLD, translate * scale);
  109. if (m_sunlight_seen) {
  110. video::SColorf suncolor_f(1, 1, 0, 1);
  111. //suncolor_f.r = 1;
  112. //suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.7 + m_time_brightness * 0.5));
  113. //suncolor_f.b = MYMAX(0.0, m_brightness * 0.95);
  114. video::SColorf suncolor2_f(1, 1, 1, 1);
  115. // The values below were probably meant to be suncolor2_f instead of a
  116. // reassignment of suncolor_f. However, the resulting colour was chosen
  117. // and is our long-running classic colour. So preserve, but comment-out
  118. // the unnecessary first assignments above.
  119. suncolor_f.r = 1;
  120. suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.85 + m_time_brightness * 0.5));
  121. suncolor_f.b = MYMAX(0.0, m_brightness);
  122. video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
  123. video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);
  124. float wicked_time_of_day = getWickedTimeOfDay(m_time_of_day);
  125. video::SColor suncolor = suncolor_f.toSColor();
  126. video::SColor suncolor2 = suncolor2_f.toSColor();
  127. video::SColor mooncolor = mooncolor_f.toSColor();
  128. video::SColor mooncolor2 = mooncolor2_f.toSColor();
  129. // Calculate offset normalized to the X dimension of a 512x1 px tonemap
  130. float offset = (1.0 - fabs(sin((m_time_of_day - 0.5) * irr::core::PI))) * 511;
  131. if (m_sun_tonemap) {
  132. u8 * texels = (u8 *)m_sun_tonemap->lock();
  133. video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4);
  134. video::SColor texel_color (255, texel->getRed(),
  135. texel->getGreen(), texel->getBlue());
  136. m_sun_tonemap->unlock();
  137. m_materials[3].EmissiveColor = texel_color;
  138. }
  139. if (m_moon_tonemap) {
  140. u8 * texels = (u8 *)m_moon_tonemap->lock();
  141. video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4);
  142. video::SColor texel_color (255, texel->getRed(),
  143. texel->getGreen(), texel->getBlue());
  144. m_moon_tonemap->unlock();
  145. m_materials[4].EmissiveColor = texel_color;
  146. }
  147. const f32 t = 1.0f;
  148. const f32 o = 0.0f;
  149. static const u16 indices[6] = {0, 1, 2, 0, 2, 3};
  150. video::S3DVertex vertices[4];
  151. driver->setMaterial(m_materials[1]);
  152. video::SColor cloudyfogcolor = m_bgcolor;
  153. // Abort rendering if we're in the clouds.
  154. // Stops rendering a pure white hole in the bottom of the skybox.
  155. if (m_in_clouds)
  156. return;
  157. // Draw the six sided skybox,
  158. if (m_sky_params.textures.size() == 6) {
  159. for (u32 j = 5; j < 11; j++) {
  160. video::SColor c(255, 255, 255, 255);
  161. driver->setMaterial(m_materials[j]);
  162. // Use 1.05 rather than 1.0 to avoid colliding with the
  163. // sun, moon and stars, as this is a background skybox.
  164. vertices[0] = video::S3DVertex(-1.05, -1.05, -1.05, 0, 0, 1, c, t, t);
  165. vertices[1] = video::S3DVertex( 1.05, -1.05, -1.05, 0, 0, 1, c, o, t);
  166. vertices[2] = video::S3DVertex( 1.05, 1.05, -1.05, 0, 0, 1, c, o, o);
  167. vertices[3] = video::S3DVertex(-1.05, 1.05, -1.05, 0, 0, 1, c, t, o);
  168. for (video::S3DVertex &vertex : vertices) {
  169. if (j == 5) { // Top texture
  170. vertex.Pos.rotateYZBy(90);
  171. vertex.Pos.rotateXZBy(90);
  172. } else if (j == 6) { // Bottom texture
  173. vertex.Pos.rotateYZBy(-90);
  174. vertex.Pos.rotateXZBy(90);
  175. } else if (j == 7) { // Left texture
  176. vertex.Pos.rotateXZBy(90);
  177. } else if (j == 8) { // Right texture
  178. vertex.Pos.rotateXZBy(-90);
  179. } else if (j == 9) { // Front texture, do nothing
  180. // Irrlicht doesn't like it when vertexes are left
  181. // alone and not rotated for some reason.
  182. vertex.Pos.rotateXZBy(0);
  183. } else {// Back texture
  184. vertex.Pos.rotateXZBy(180);
  185. }
  186. }
  187. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  188. }
  189. }
  190. // Draw far cloudy fog thing blended with skycolor
  191. if (m_visible) {
  192. driver->setMaterial(m_materials[1]);
  193. for (u32 j = 0; j < 4; j++) {
  194. vertices[0] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, m_bgcolor, t, t);
  195. vertices[1] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, m_bgcolor, o, t);
  196. vertices[2] = video::S3DVertex( 1, 0.45, -1, 0, 0, 1, m_skycolor, o, o);
  197. vertices[3] = video::S3DVertex(-1, 0.45, -1, 0, 0, 1, m_skycolor, t, o);
  198. for (video::S3DVertex &vertex : vertices) {
  199. if (j == 0)
  200. // Don't switch
  201. {}
  202. else if (j == 1)
  203. // Switch from -Z (south) to +X (east)
  204. vertex.Pos.rotateXZBy(90);
  205. else if (j == 2)
  206. // Switch from -Z (south) to -X (west)
  207. vertex.Pos.rotateXZBy(-90);
  208. else
  209. // Switch from -Z (south) to +Z (north)
  210. vertex.Pos.rotateXZBy(-180);
  211. }
  212. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  213. }
  214. }
  215. // Draw stars before sun and moon to be behind them
  216. if (m_star_params.visible)
  217. draw_stars(driver, wicked_time_of_day);
  218. // Draw sunrise/sunset horizon glow texture
  219. // (textures/base/pack/sunrisebg.png)
  220. if (m_sun_params.sunrise_visible) {
  221. driver->setMaterial(m_materials[2]);
  222. float mid1 = 0.25;
  223. float mid = wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1);
  224. float a_ = 1.0f - std::fabs(wicked_time_of_day - mid) * 35.0f;
  225. float a = easeCurve(MYMAX(0, MYMIN(1, a_)));
  226. //std::cerr<<"a_="<<a_<<" a="<<a<<std::endl;
  227. video::SColor c(255, 255, 255, 255);
  228. float y = -(1.0 - a) * 0.22;
  229. vertices[0] = video::S3DVertex(-1, -0.05 + y, -1, 0, 0, 1, c, t, t);
  230. vertices[1] = video::S3DVertex( 1, -0.05 + y, -1, 0, 0, 1, c, o, t);
  231. vertices[2] = video::S3DVertex( 1, 0.2 + y, -1, 0, 0, 1, c, o, o);
  232. vertices[3] = video::S3DVertex(-1, 0.2 + y, -1, 0, 0, 1, c, t, o);
  233. for (video::S3DVertex &vertex : vertices) {
  234. if (wicked_time_of_day < 0.5)
  235. // Switch from -Z (south) to +X (east)
  236. vertex.Pos.rotateXZBy(90);
  237. else
  238. // Switch from -Z (south) to -X (west)
  239. vertex.Pos.rotateXZBy(-90);
  240. }
  241. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  242. }
  243. // Draw sun
  244. if (m_sun_params.visible)
  245. draw_sun(driver, suncolor, suncolor2, wicked_time_of_day);
  246. // Draw moon
  247. if (m_moon_params.visible)
  248. draw_moon(driver, mooncolor, mooncolor2, wicked_time_of_day);
  249. // Draw far cloudy fog thing below all horizons in front of sun, moon
  250. // and stars.
  251. if (m_visible) {
  252. driver->setMaterial(m_materials[1]);
  253. for (u32 j = 0; j < 4; j++) {
  254. video::SColor c = cloudyfogcolor;
  255. vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t);
  256. vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t);
  257. vertices[2] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, c, o, o);
  258. vertices[3] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, c, t, o);
  259. for (video::S3DVertex &vertex : vertices) {
  260. if (j == 0)
  261. // Don't switch
  262. {}
  263. else if (j == 1)
  264. // Switch from -Z (south) to +X (east)
  265. vertex.Pos.rotateXZBy(90);
  266. else if (j == 2)
  267. // Switch from -Z (south) to -X (west)
  268. vertex.Pos.rotateXZBy(-90);
  269. else
  270. // Switch from -Z (south) to +Z (north)
  271. vertex.Pos.rotateXZBy(-180);
  272. }
  273. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  274. }
  275. // Draw bottom far cloudy fog thing in front of sun, moon and stars
  276. video::SColor c = cloudyfogcolor;
  277. vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 1, 0, c, t, t);
  278. vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t);
  279. vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o);
  280. vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o);
  281. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  282. }
  283. }
  284. }
  285. void Sky::update(float time_of_day, float time_brightness,
  286. float direct_brightness, bool sunlight_seen,
  287. CameraMode cam_mode, float yaw, float pitch)
  288. {
  289. // Stabilize initial brightness and color values by flooding updates
  290. if (m_first_update) {
  291. /*dstream<<"First update with time_of_day="<<time_of_day
  292. <<" time_brightness="<<time_brightness
  293. <<" direct_brightness="<<direct_brightness
  294. <<" sunlight_seen="<<sunlight_seen<<std::endl;*/
  295. m_first_update = false;
  296. for (u32 i = 0; i < 100; i++) {
  297. update(time_of_day, time_brightness, direct_brightness,
  298. sunlight_seen, cam_mode, yaw, pitch);
  299. }
  300. return;
  301. }
  302. m_time_of_day = time_of_day;
  303. m_time_brightness = time_brightness;
  304. m_sunlight_seen = sunlight_seen;
  305. m_in_clouds = false;
  306. bool is_dawn = (time_brightness >= 0.20 && time_brightness < 0.35);
  307. video::SColorf bgcolor_bright_normal_f = m_sky_params.sky_color.day_horizon;
  308. video::SColorf bgcolor_bright_indoor_f = m_sky_params.sky_color.indoors;
  309. video::SColorf bgcolor_bright_dawn_f = m_sky_params.sky_color.dawn_horizon;
  310. video::SColorf bgcolor_bright_night_f = m_sky_params.sky_color.night_horizon;
  311. video::SColorf skycolor_bright_normal_f = m_sky_params.sky_color.day_sky;
  312. video::SColorf skycolor_bright_dawn_f = m_sky_params.sky_color.dawn_sky;
  313. video::SColorf skycolor_bright_night_f = m_sky_params.sky_color.night_sky;
  314. video::SColorf cloudcolor_bright_normal_f = m_cloudcolor_day_f;
  315. video::SColorf cloudcolor_bright_dawn_f = m_cloudcolor_dawn_f;
  316. float cloud_color_change_fraction = 0.95;
  317. if (sunlight_seen) {
  318. if (std::fabs(time_brightness - m_brightness) < 0.2f) {
  319. m_brightness = m_brightness * 0.95 + time_brightness * 0.05;
  320. } else {
  321. m_brightness = m_brightness * 0.80 + time_brightness * 0.20;
  322. cloud_color_change_fraction = 0.0;
  323. }
  324. } else {
  325. if (direct_brightness < m_brightness)
  326. m_brightness = m_brightness * 0.95 + direct_brightness * 0.05;
  327. else
  328. m_brightness = m_brightness * 0.98 + direct_brightness * 0.02;
  329. }
  330. m_clouds_visible = true;
  331. float color_change_fraction = 0.98f;
  332. if (sunlight_seen) {
  333. if (is_dawn) { // Dawn
  334. m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
  335. bgcolor_bright_dawn_f, color_change_fraction);
  336. m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
  337. skycolor_bright_dawn_f, color_change_fraction);
  338. m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(
  339. cloudcolor_bright_dawn_f, color_change_fraction);
  340. } else {
  341. if (time_brightness < 0.13f) { // Night
  342. m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
  343. bgcolor_bright_night_f, color_change_fraction);
  344. m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
  345. skycolor_bright_night_f, color_change_fraction);
  346. } else { // Day
  347. m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
  348. bgcolor_bright_normal_f, color_change_fraction);
  349. m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
  350. skycolor_bright_normal_f, color_change_fraction);
  351. }
  352. m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(
  353. cloudcolor_bright_normal_f, color_change_fraction);
  354. }
  355. } else {
  356. m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
  357. bgcolor_bright_indoor_f, color_change_fraction);
  358. m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
  359. bgcolor_bright_indoor_f, color_change_fraction);
  360. m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(
  361. cloudcolor_bright_normal_f, color_change_fraction);
  362. m_clouds_visible = false;
  363. }
  364. video::SColor bgcolor_bright = m_bgcolor_bright_f.toSColor();
  365. m_bgcolor = video::SColor(
  366. 255,
  367. bgcolor_bright.getRed() * m_brightness,
  368. bgcolor_bright.getGreen() * m_brightness,
  369. bgcolor_bright.getBlue() * m_brightness
  370. );
  371. video::SColor skycolor_bright = m_skycolor_bright_f.toSColor();
  372. m_skycolor = video::SColor(
  373. 255,
  374. skycolor_bright.getRed() * m_brightness,
  375. skycolor_bright.getGreen() * m_brightness,
  376. skycolor_bright.getBlue() * m_brightness
  377. );
  378. // Horizon coloring based on sun and moon direction during sunset and sunrise
  379. video::SColor pointcolor = video::SColor(m_bgcolor.getAlpha(), 255, 255, 255);
  380. if (m_directional_colored_fog) {
  381. if (m_horizon_blend() != 0) {
  382. // Calculate hemisphere value from yaw, (inverted in third person front view)
  383. s8 dir_factor = 1;
  384. if (cam_mode > CAMERA_MODE_THIRD)
  385. dir_factor = -1;
  386. f32 pointcolor_blend = wrapDegrees_0_360(yaw * dir_factor + 90);
  387. if (pointcolor_blend > 180)
  388. pointcolor_blend = 360 - pointcolor_blend;
  389. pointcolor_blend /= 180;
  390. // Bound view angle to determine where transition starts and ends
  391. pointcolor_blend = rangelim(1 - pointcolor_blend * 1.375, 0, 1 / 1.375) *
  392. 1.375;
  393. // Combine the colors when looking up or down, otherwise turning looks weird
  394. pointcolor_blend += (0.5 - pointcolor_blend) *
  395. (1 - MYMIN((90 - std::fabs(pitch)) / 90 * 1.5, 1));
  396. // Invert direction to match where the sun and moon are rising
  397. if (m_time_of_day > 0.5)
  398. pointcolor_blend = 1 - pointcolor_blend;
  399. // Horizon colors of sun and moon
  400. f32 pointcolor_light = rangelim(m_time_brightness * 3, 0.2, 1);
  401. video::SColorf pointcolor_sun_f(1, 1, 1, 1);
  402. // Use tonemap only if default sun/moon tinting is used
  403. // which keeps previous behavior.
  404. if (m_sun_tonemap && m_default_tint) {
  405. pointcolor_sun_f.r = pointcolor_light *
  406. (float)m_materials[3].EmissiveColor.getRed() / 255;
  407. pointcolor_sun_f.b = pointcolor_light *
  408. (float)m_materials[3].EmissiveColor.getBlue() / 255;
  409. pointcolor_sun_f.g = pointcolor_light *
  410. (float)m_materials[3].EmissiveColor.getGreen() / 255;
  411. } else if (!m_default_tint) {
  412. pointcolor_sun_f = m_sky_params.fog_sun_tint;
  413. } else {
  414. pointcolor_sun_f.r = pointcolor_light * 1;
  415. pointcolor_sun_f.b = pointcolor_light *
  416. (0.25 + (rangelim(m_time_brightness, 0.25, 0.75) - 0.25) * 2 * 0.75);
  417. pointcolor_sun_f.g = pointcolor_light * (pointcolor_sun_f.b * 0.375 +
  418. (rangelim(m_time_brightness, 0.05, 0.15) - 0.05) * 10 * 0.625);
  419. }
  420. video::SColorf pointcolor_moon_f;
  421. if (m_default_tint) {
  422. pointcolor_moon_f = video::SColorf(
  423. 0.5 * pointcolor_light,
  424. 0.6 * pointcolor_light,
  425. 0.8 * pointcolor_light,
  426. 1
  427. );
  428. } else {
  429. pointcolor_moon_f = video::SColorf(
  430. (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light,
  431. (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light,
  432. (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light,
  433. 1
  434. );
  435. }
  436. if (m_moon_tonemap && m_default_tint) {
  437. pointcolor_moon_f.r = pointcolor_light *
  438. (float)m_materials[4].EmissiveColor.getRed() / 255;
  439. pointcolor_moon_f.b = pointcolor_light *
  440. (float)m_materials[4].EmissiveColor.getBlue() / 255;
  441. pointcolor_moon_f.g = pointcolor_light *
  442. (float)m_materials[4].EmissiveColor.getGreen() / 255;
  443. }
  444. video::SColor pointcolor_sun = pointcolor_sun_f.toSColor();
  445. video::SColor pointcolor_moon = pointcolor_moon_f.toSColor();
  446. // Calculate the blend color
  447. pointcolor = m_mix_scolor(pointcolor_moon, pointcolor_sun, pointcolor_blend);
  448. }
  449. m_bgcolor = m_mix_scolor(m_bgcolor, pointcolor, m_horizon_blend() * 0.5);
  450. m_skycolor = m_mix_scolor(m_skycolor, pointcolor, m_horizon_blend() * 0.25);
  451. }
  452. float cloud_direct_brightness = 0.0f;
  453. if (sunlight_seen) {
  454. if (!m_directional_colored_fog) {
  455. cloud_direct_brightness = time_brightness;
  456. // Boost cloud brightness relative to sky, at dawn, dusk and at night
  457. if (time_brightness < 0.7f)
  458. cloud_direct_brightness *= 1.3f;
  459. } else {
  460. cloud_direct_brightness = std::fmin(m_horizon_blend() * 0.15f +
  461. m_time_brightness, 1.0f);
  462. // Set the same minimum cloud brightness at night
  463. if (time_brightness < 0.5f)
  464. cloud_direct_brightness = std::fmax(cloud_direct_brightness,
  465. time_brightness * 1.3f);
  466. }
  467. } else {
  468. cloud_direct_brightness = direct_brightness;
  469. }
  470. m_cloud_brightness = m_cloud_brightness * cloud_color_change_fraction +
  471. cloud_direct_brightness * (1.0 - cloud_color_change_fraction);
  472. m_cloudcolor_f = video::SColorf(
  473. m_cloudcolor_bright_f.r * m_cloud_brightness,
  474. m_cloudcolor_bright_f.g * m_cloud_brightness,
  475. m_cloudcolor_bright_f.b * m_cloud_brightness,
  476. 1.0
  477. );
  478. if (m_directional_colored_fog) {
  479. m_cloudcolor_f = m_mix_scolorf(m_cloudcolor_f,
  480. video::SColorf(pointcolor), m_horizon_blend() * 0.25);
  481. }
  482. }
  483. static v3f getSkyBodyPosition(float horizon_position, float day_position, float orbit_tilt)
  484. {
  485. v3f result = v3f(0, 0, -1);
  486. result.rotateXZBy(horizon_position);
  487. result.rotateXYBy(day_position);
  488. result.rotateYZBy(orbit_tilt);
  489. return result;
  490. }
  491. v3f Sky::getSunDirection()
  492. {
  493. return getSkyBodyPosition(90, getWickedTimeOfDay(m_time_of_day) * 360 - 90, m_sky_params.body_orbit_tilt);
  494. }
  495. v3f Sky::getMoonDirection()
  496. {
  497. return getSkyBodyPosition(270, getWickedTimeOfDay(m_time_of_day) * 360 - 90, m_sky_params.body_orbit_tilt);
  498. }
  499. void Sky::draw_sun(video::IVideoDriver *driver, const video::SColor &suncolor,
  500. const video::SColor &suncolor2, float wicked_time_of_day)
  501. /* Draw sun in the sky.
  502. * driver: Video driver object used to draw
  503. * suncolor: main sun color
  504. * suncolor2: second sun color
  505. * wicked_time_of_day: current time of day, to know where should be the sun in the sky
  506. */
  507. {
  508. // A magic number that contributes to the ratio 1.57 sun/moon size difference.
  509. constexpr float sunsize = 0.07;
  510. static const u16 indices[] = {0, 1, 2, 0, 2, 3};
  511. std::array<video::S3DVertex, 4> vertices;
  512. if (!m_sun_texture) {
  513. driver->setMaterial(m_materials[1]);
  514. const float sunsizes[4] = {
  515. (sunsize * 1.7f) * m_sun_params.scale,
  516. (sunsize * 1.2f) * m_sun_params.scale,
  517. (sunsize) * m_sun_params.scale,
  518. (sunsize * 0.7f) * m_sun_params.scale
  519. };
  520. video::SColor c1 = suncolor;
  521. video::SColor c2 = suncolor;
  522. c1.setAlpha(0.05 * 255);
  523. c2.setAlpha(0.15 * 255);
  524. const video::SColor colors[4] = {c1, c2, suncolor, suncolor2};
  525. for (int i = 0; i < 4; i++) {
  526. draw_sky_body(vertices, -sunsizes[i], sunsizes[i], colors[i]);
  527. place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90);
  528. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  529. }
  530. } else {
  531. driver->setMaterial(m_materials[3]);
  532. // Another magic number that contributes to the ratio 1.57 sun/moon size
  533. // difference.
  534. float d = (sunsize * 1.7) * m_sun_params.scale;
  535. video::SColor c;
  536. if (m_sun_tonemap)
  537. c = video::SColor(0, 0, 0, 0);
  538. else
  539. c = video::SColor(255, 255, 255, 255);
  540. draw_sky_body(vertices, -d, d, c);
  541. place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90);
  542. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  543. }
  544. }
  545. void Sky::draw_moon(video::IVideoDriver *driver, const video::SColor &mooncolor,
  546. const video::SColor &mooncolor2, float wicked_time_of_day)
  547. /*
  548. * Draw moon in the sky.
  549. * driver: Video driver object used to draw
  550. * mooncolor: main moon color
  551. * mooncolor2: second moon color
  552. * wicked_time_of_day: current time of day, to know where should be the moon in
  553. * the sky
  554. */
  555. {
  556. // A magic number that contributes to the ratio 1.57 sun/moon size difference.
  557. constexpr float moonsize = 0.04;
  558. static const u16 indices[] = {0, 1, 2, 0, 2, 3};
  559. std::array<video::S3DVertex, 4> vertices;
  560. if (!m_moon_texture) {
  561. driver->setMaterial(m_materials[1]);
  562. const float moonsizes_1[4] = {
  563. (-moonsize * 1.9f) * m_moon_params.scale,
  564. (-moonsize * 1.3f) * m_moon_params.scale,
  565. (-moonsize) * m_moon_params.scale,
  566. (-moonsize) * m_moon_params.scale
  567. };
  568. const float moonsizes_2[4] = {
  569. (moonsize * 1.9f) * m_moon_params.scale,
  570. (moonsize * 1.3f) * m_moon_params.scale,
  571. (moonsize) *m_moon_params.scale,
  572. (moonsize * 0.6f) * m_moon_params.scale
  573. };
  574. video::SColor c1 = mooncolor;
  575. video::SColor c2 = mooncolor;
  576. c1.setAlpha(0.05 * 255);
  577. c2.setAlpha(0.15 * 255);
  578. const video::SColor colors[4] = {c1, c2, mooncolor, mooncolor2};
  579. for (int i = 0; i < 4; i++) {
  580. draw_sky_body(vertices, moonsizes_1[i], moonsizes_2[i], colors[i]);
  581. place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90);
  582. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  583. }
  584. } else {
  585. driver->setMaterial(m_materials[4]);
  586. // Another magic number that contributes to the ratio 1.57 sun/moon size
  587. // difference.
  588. float d = (moonsize * 1.9) * m_moon_params.scale;
  589. video::SColor c;
  590. if (m_moon_tonemap)
  591. c = video::SColor(0, 0, 0, 0);
  592. else
  593. c = video::SColor(255, 255, 255, 255);
  594. draw_sky_body(vertices, -d, d, c);
  595. place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90);
  596. driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
  597. }
  598. }
  599. void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day)
  600. {
  601. // Tune values so that stars first appear just after the sun
  602. // disappears over the horizon, and disappear just before the sun
  603. // appears over the horizon.
  604. // Also tune so that stars are at full brightness from time 20000
  605. // to time 4000.
  606. float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day);
  607. float day_opacity = clamp(m_star_params.day_opacity, 0.0f, 1.0f);
  608. float starbrightness = (0.25f - std::abs(tod)) * 20.0f;
  609. float alpha = clamp(starbrightness, day_opacity, 1.0f);
  610. video::SColorf color(m_star_params.starcolor);
  611. color.a *= alpha;
  612. if (color.a <= 0.0f) // Stars are only drawn when not fully transparent
  613. return;
  614. m_materials[0].EmissiveColor = color.toSColor();
  615. auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f));
  616. auto world_matrix = driver->getTransform(video::ETS_WORLD);
  617. driver->setTransform(video::ETS_WORLD, world_matrix * sky_rotation);
  618. driver->setMaterial(m_materials[0]);
  619. driver->drawMeshBuffer(m_stars.get());
  620. driver->setTransform(video::ETS_WORLD, world_matrix);
  621. }
  622. void Sky::draw_sky_body(std::array<video::S3DVertex, 4> &vertices, float pos_1, float pos_2, const video::SColor &c)
  623. {
  624. /*
  625. * Create an array of vertices with the dimensions specified.
  626. * pos_1, pos_2: position of the body's vertices
  627. * c: color of the body
  628. */
  629. const f32 t = 1.0f;
  630. const f32 o = 0.0f;
  631. vertices[0] = video::S3DVertex(pos_1, pos_1, -1, 0, 0, 1, c, t, t);
  632. vertices[1] = video::S3DVertex(pos_2, pos_1, -1, 0, 0, 1, c, o, t);
  633. vertices[2] = video::S3DVertex(pos_2, pos_2, -1, 0, 0, 1, c, o, o);
  634. vertices[3] = video::S3DVertex(pos_1, pos_2, -1, 0, 0, 1, c, t, o);
  635. }
  636. void Sky::place_sky_body(
  637. std::array<video::S3DVertex, 4> &vertices, float horizon_position, float day_position)
  638. /*
  639. * Place body in the sky.
  640. * vertices: The body as a rectangle of 4 vertices
  641. * horizon_position: turn the body around the Y axis
  642. * day_position: turn the body around the Z axis, to place it depending of the time of the day
  643. */
  644. {
  645. v3f centrum = getSkyBodyPosition(horizon_position, day_position, m_sky_params.body_orbit_tilt);
  646. v3f untilted_centrum = getSkyBodyPosition(horizon_position, day_position, 0.f);
  647. for (video::S3DVertex &vertex : vertices) {
  648. // Body is directed to -Z (south) by default
  649. vertex.Pos.rotateXZBy(horizon_position);
  650. vertex.Pos.rotateXYBy(day_position);
  651. vertex.Pos += centrum - untilted_centrum;
  652. }
  653. }
  654. void Sky::setSunTexture(const std::string &sun_texture,
  655. const std::string &sun_tonemap, ITextureSource *tsrc)
  656. {
  657. // Ignore matching textures (with modifiers) entirely,
  658. // but lets at least update the tonemap before hand.
  659. m_sun_params.tonemap = sun_tonemap;
  660. m_sun_tonemap = tsrc->isKnownSourceImage(sun_tonemap) ?
  661. tsrc->getTexture(sun_tonemap) : nullptr;
  662. m_materials[3].Lighting = !!m_sun_tonemap;
  663. if (m_sun_params.texture == sun_texture && !m_first_update)
  664. return;
  665. m_sun_params.texture = sun_texture;
  666. m_sun_texture = nullptr;
  667. if (sun_texture == "sun.png") {
  668. // Dumb compatibility fix: sun.png transparently falls back to no texture
  669. m_sun_texture = tsrc->isKnownSourceImage(sun_texture) ?
  670. tsrc->getTexture(sun_texture) : nullptr;
  671. } else if (!sun_texture.empty()) {
  672. m_sun_texture = tsrc->getTextureForMesh(sun_texture);
  673. }
  674. if (m_sun_texture) {
  675. m_materials[3] = baseMaterial();
  676. m_materials[3].setTexture(0, m_sun_texture);
  677. m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  678. disableTextureFiltering(m_materials[3]);
  679. m_materials[3].Lighting = !!m_sun_tonemap;
  680. }
  681. }
  682. void Sky::setSunriseTexture(const std::string &sunglow_texture,
  683. ITextureSource* tsrc)
  684. {
  685. // Ignore matching textures (with modifiers) entirely.
  686. if (m_sun_params.sunrise == sunglow_texture)
  687. return;
  688. m_sun_params.sunrise = sunglow_texture;
  689. m_materials[2].setTexture(0, tsrc->getTextureForMesh(
  690. sunglow_texture.empty() ? "sunrisebg.png" : sunglow_texture)
  691. );
  692. }
  693. void Sky::setMoonTexture(const std::string &moon_texture,
  694. const std::string &moon_tonemap, ITextureSource *tsrc)
  695. {
  696. // Ignore matching textures (with modifiers) entirely,
  697. // but lets at least update the tonemap before hand.
  698. m_moon_params.tonemap = moon_tonemap;
  699. m_moon_tonemap = tsrc->isKnownSourceImage(moon_tonemap) ?
  700. tsrc->getTexture(moon_tonemap) : nullptr;
  701. m_materials[4].Lighting = !!m_moon_tonemap;
  702. if (m_moon_params.texture == moon_texture && !m_first_update)
  703. return;
  704. m_moon_params.texture = moon_texture;
  705. m_moon_texture = nullptr;
  706. if (moon_texture == "moon.png") {
  707. // Dumb compatibility fix: moon.png transparently falls back to no texture
  708. m_moon_texture = tsrc->isKnownSourceImage(moon_texture) ?
  709. tsrc->getTexture(moon_texture) : nullptr;
  710. } else if (!moon_texture.empty()) {
  711. m_moon_texture = tsrc->getTextureForMesh(moon_texture);
  712. }
  713. if (m_moon_texture) {
  714. m_materials[4] = baseMaterial();
  715. m_materials[4].setTexture(0, m_moon_texture);
  716. m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  717. disableTextureFiltering(m_materials[4]);
  718. m_materials[4].Lighting = !!m_moon_tonemap;
  719. }
  720. }
  721. void Sky::setStarCount(u16 star_count)
  722. {
  723. // Allow force updating star count at game init.
  724. if (m_star_params.count != star_count || m_first_update) {
  725. m_star_params.count = star_count;
  726. updateStars();
  727. }
  728. }
  729. void Sky::updateStars()
  730. {
  731. m_stars.reset(new scene::SMeshBuffer());
  732. // Stupid IrrLicht doesn’t allow non-indexed rendering, and indexed quad
  733. // rendering is slow due to lack of hardware support. So as indices are
  734. // 16-bit and there are 4 vertices per star... the limit is 2^16/4 = 0x4000.
  735. // That should be well enough actually.
  736. if (m_star_params.count > 0x4000) {
  737. warningstream << "Requested " << m_star_params.count << " stars but " << 0x4000 << " is the max\n";
  738. m_star_params.count = 0x4000;
  739. }
  740. m_stars->Vertices.reallocate(4 * m_star_params.count);
  741. m_stars->Indices.reallocate(6 * m_star_params.count);
  742. video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders”
  743. PcgRandom rgen(m_seed);
  744. float d = (0.006 / 2) * m_star_params.scale;
  745. for (u16 i = 0; i < m_star_params.count; i++) {
  746. v3f r = v3f(
  747. rgen.range(-10000, 10000),
  748. rgen.range(-10000, 10000),
  749. rgen.range(-10000, 10000)
  750. );
  751. core::CMatrix4<f32> a;
  752. a.buildRotateFromTo(v3f(0, 1, 0), r);
  753. v3f p = v3f(-d, 1, -d);
  754. v3f p1 = v3f(d, 1, -d);
  755. v3f p2 = v3f(d, 1, d);
  756. v3f p3 = v3f(-d, 1, d);
  757. a.rotateVect(p);
  758. a.rotateVect(p1);
  759. a.rotateVect(p2);
  760. a.rotateVect(p3);
  761. m_stars->Vertices.push_back(video::S3DVertex(p, {}, fallback_color, {}));
  762. m_stars->Vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {}));
  763. m_stars->Vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {}));
  764. m_stars->Vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {}));
  765. }
  766. for (u16 i = 0; i < m_star_params.count; i++) {
  767. m_stars->Indices.push_back(i * 4 + 0);
  768. m_stars->Indices.push_back(i * 4 + 1);
  769. m_stars->Indices.push_back(i * 4 + 2);
  770. m_stars->Indices.push_back(i * 4 + 2);
  771. m_stars->Indices.push_back(i * 4 + 3);
  772. m_stars->Indices.push_back(i * 4 + 0);
  773. }
  774. m_stars->setHardwareMappingHint(scene::EHM_STATIC);
  775. }
  776. void Sky::setSkyColors(const SkyColor &sky_color)
  777. {
  778. m_sky_params.sky_color = sky_color;
  779. }
  780. void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint,
  781. const std::string &use_sun_tint)
  782. {
  783. // Change sun and moon tinting:
  784. m_sky_params.fog_sun_tint = sun_tint;
  785. m_sky_params.fog_moon_tint = moon_tint;
  786. // Faster than comparing strings every rendering frame
  787. if (use_sun_tint == "default")
  788. m_default_tint = true;
  789. else if (use_sun_tint == "custom")
  790. m_default_tint = false;
  791. else
  792. m_default_tint = true;
  793. }
  794. void Sky::addTextureToSkybox(const std::string &texture, int material_id,
  795. ITextureSource *tsrc)
  796. {
  797. // Sanity check for more than six textures.
  798. if (material_id + 5 >= SKY_MATERIAL_COUNT)
  799. return;
  800. // Keep a list of texture names handy.
  801. m_sky_params.textures.emplace_back(texture);
  802. video::ITexture *result = tsrc->getTextureForMesh(texture);
  803. m_materials[material_id+5] = baseMaterial();
  804. m_materials[material_id+5].setTexture(0, result);
  805. m_materials[material_id+5].MaterialType = video::EMT_SOLID;
  806. }
  807. float getWickedTimeOfDay(float time_of_day)
  808. {
  809. float nightlength = 0.415f;
  810. float wn = nightlength / 2;
  811. float wicked_time_of_day = 0;
  812. if (time_of_day > wn && time_of_day < 1.0f - wn)
  813. wicked_time_of_day = (time_of_day - wn) / (1.0f - wn * 2) * 0.5f + 0.25f;
  814. else if (time_of_day < 0.5f)
  815. wicked_time_of_day = time_of_day / wn * 0.25f;
  816. else
  817. wicked_time_of_day = 1.0f - ((1.0f - time_of_day) / wn * 0.25f);
  818. return wicked_time_of_day;
  819. }