voxelands/src/sky.cpp

370 lines
12 KiB
C++

/************************************************************************
* Minetest-c55
* Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
*
* sky.cpp
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2013-2015 <lisa@ltmnet.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>
*
* License updated from GPLv2 or later to GPLv3 or later by Lisa Milne
* for Voxelands.
************************************************************************/
#include "sky.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "ICameraSceneNode.h"
#include "S3DVertex.h"
#include "utility.h" // MYMIN
#include "path.h" // getTexturePath
#include "noise.h" // easeCurve
#include "main.h" // g_profiler
#include "profiler.h"
#include "map.h"
//! constructor
Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id):
scene::ISceneNode(parent, mgr, id),
m_first_update(true),
m_brightness(0.5),
m_cloud_brightness(0.5),
m_moon_phase(0.0),
m_moon_phase_pending(0.0),
m_biome(BIOME_UNKNOWN),
m_bgcolor_bright_f(1,1,1,1),
m_skycolor_bright_f(1,0,0,0),
m_cloudcolor_bright_f(1,1,1,1)
{
char buff[1024];
setAutomaticCulling(scene::EAC_OFF);
Box.MaxEdge.set(0,0,0);
Box.MinEdge.set(0,0,0);
// create material
video::SMaterial mat;
mat.Lighting = false;
mat.ZBuffer = video::ECFN_NEVER;
mat.ZWriteEnable = false;
mat.AntiAliasing=0;
mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
mat.BackfaceCulling = false;
mat.Thickness = 1.0;
m_materials[0] = mat;
m_materials[1] = mat;
m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
m_materials[2] = mat;
if (path_get((char*)"texture",(char*)"sun.png",1,buff,1024))
m_materials[2].setTexture(0, mgr->getVideoDriver()->getTexture(buff));
m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
m_materials[3] = mat;
if (path_get((char*)"texture",(char*)"moon.png",1,buff,1024))
m_materials[3].setTexture(0, mgr->getVideoDriver()->getTexture(buff));
m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
for (u32 i=0; i<SKY_STAR_COUNT; i++) {
m_stars[i] = v3f(
myrand_range(-10000,10000),
myrand_range(-10000,10000),
myrand_range(-10000,10000)
);
m_stars[i].normalize();
}
}
void Sky::OnRegisterSceneNode()
{
if (IsVisible)
SceneManager->registerNodeForRendering(this, scene::ESNRP_SKY_BOX);
scene::ISceneNode::OnRegisterSceneNode();
}
const core::aabbox3d<f32>& Sky::getBoundingBox() const
{
return Box;
}
//! renders the node.
void Sky::render()
{
video::IVideoDriver* driver = SceneManager->getVideoDriver();
scene::ICameraSceneNode* camera = SceneManager->getActiveCamera();
if (!camera || !driver)
return;
ScopeProfiler sp(g_profiler, "Sky::render()", SPT_AVG);
// draw perspective skybox
core::matrix4 translate(AbsoluteTransformation);
translate.setTranslation(camera->getAbsolutePosition());
// Draw the sky box between the near and far clip plane
const f32 viewDistance = (camera->getNearValue() + camera->getFarValue()) * 0.5f;
core::matrix4 scale;
scale.setScale(core::vector3df(viewDistance, viewDistance, viewDistance));
driver->setTransform(video::ETS_WORLD, translate * scale);
if (!m_sunlight_seen)
return;
float sunsize = 0.07;
float moonsize = 0.04;
float nightlength = 0.41;
float wn = nightlength / 2;
float wicked_time_of_day = 0;
if (m_time_of_day > wn && m_time_of_day < 1.0 - wn) {
wicked_time_of_day = (m_time_of_day - wn)/(1.0-wn*2)*0.5 + 0.25;
}else if (m_time_of_day < 0.5) {
wicked_time_of_day = m_time_of_day / wn * 0.25;
}else{
wicked_time_of_day = 1.0 - ((1.0-m_time_of_day) / wn * 0.25);
}
const f32 t = 1.0f;
const f32 o = 0.0f;
static const u16 indices[4] = {0,1,2,3};
video::S3DVertex vertices[4];
// Draw sun
if (wicked_time_of_day > 0.15 && wicked_time_of_day < 0.85) {
driver->setMaterial(m_materials[2]);
float d = sunsize * 1.7;
video::SColor c(255,255,255,255);
vertices[0] = video::S3DVertex(-d,-d,-1, 0,0,1, c, t, t);
vertices[1] = video::S3DVertex( d,-d,-1, 0,0,1, c, o, t);
vertices[2] = video::S3DVertex( d, d,-1, 0,0,1, c, o, o);
vertices[3] = video::S3DVertex(-d, d,-1, 0,0,1, c, t, o);
for (u32 i=0; i<4; i++) {
// Switch from -Z (south) to +X (east)
vertices[i].Pos.rotateXZBy(90);
vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
}
driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
}
// Draw moon
if (wicked_time_of_day < 0.3 || wicked_time_of_day > 0.7) {
int mp = m_moon_phase*10;
if (mp != 0) {
driver->setMaterial(m_materials[3]);
float d = moonsize * 1.9;
video::SColor c(255,255,255,255);
if (mp == 5) {
vertices[0] = video::S3DVertex(-d,-d,-1, 0,0,1, c, t, t);
vertices[1] = video::S3DVertex( d,-d,-1, 0,0,1, c, o, t);
vertices[2] = video::S3DVertex( d, d,-1, 0,0,1, c, o, o);
vertices[3] = video::S3DVertex(-d, d,-1, 0,0,1, c, t, o);
}else if (mp < 5) {
float dp = -d+(d*(m_moon_phase*4));
vertices[0] = video::S3DVertex(-d,-d,-1, 0,0,1, c, t, t);
vertices[1] = video::S3DVertex(dp,-d,-1, 0,0,1, c, 1.0-(m_moon_phase*2), t);
vertices[2] = video::S3DVertex(dp, d,-1, 0,0,1, c, 1.0-(m_moon_phase*2), o);
vertices[3] = video::S3DVertex(-d, d,-1, 0,0,1, c, t, o);
}else{
float dp = d-(d*((1.0-m_moon_phase)*4));
vertices[0] = video::S3DVertex(dp,-d,-1, 0,0,1, c, (1.0-m_moon_phase)*2, t);
vertices[1] = video::S3DVertex( d,-d,-1, 0,0,1, c, o, t);
vertices[2] = video::S3DVertex( d, d,-1, 0,0,1, c, o, o);
vertices[3] = video::S3DVertex(dp, d,-1, 0,0,1, c, (1.0-m_moon_phase)*2, o);
}
for (u32 i=0; i<4; i++) {
// Switch from -Z (south) to -X (west)
vertices[i].Pos.rotateXZBy(-90);
vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
}
driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
}
}else{
m_moon_phase = m_moon_phase_pending;
}
float f = 0.0;
// Stars
if (m_biome == BIOME_SPACE) {
f = 120.0;
}else{
float starbrightness = MYMAX(
0,
MYMIN(
1,
(0.285 - fabs(
wicked_time_of_day < 0.5 ? wicked_time_of_day : (1.0 - wicked_time_of_day)
)) * 10
)
);
f = starbrightness*120;
}
if (f >= m_skycolor.getBlue()) {
driver->setMaterial(m_materials[1]);
video::SColor starcolor(255, f,f,f);
u16 indices[SKY_STAR_COUNT*4];
video::S3DVertex vertices[SKY_STAR_COUNT*4];
for (u32 i=0; i<SKY_STAR_COUNT; i++) {
indices[i] = i;
v3f p = m_stars[i];
core::CMatrix4<f32> a;
p.rotateXYBy(wicked_time_of_day * 360 - 90);
vertices[i].Pos = p;
vertices[i].Color = starcolor;
}
driver->drawVertexPrimitiveList(
vertices,
SKY_STAR_COUNT,
indices,
SKY_STAR_COUNT,
video::EVT_STANDARD,
scene::EPT_POINTS,
video::EIT_16BIT
);
}
}
void Sky::update(
float time_of_day,
float moon_phase,
float time_brightness,
float direct_brightness,
bool sunlight_seen,
uint8_t biome
)
{
// Stabilize initial brightness and color values by flooding updates
if (m_first_update) {
m_first_update = false;
m_moon_phase = moon_phase;
for (u32 i=0; i<100; i++) {
update(time_of_day, moon_phase, time_brightness, direct_brightness, sunlight_seen, biome);
}
return;
}
m_time_of_day = time_of_day;
m_moon_phase_pending = moon_phase;
m_time_brightness = time_brightness;
m_sunlight_seen = sunlight_seen;
bool is_dull = false;
bool is_dawn = false;
m_biome = biome;
if (biome != BIOME_SPACE && biome != BIOME_THEDEEP) {
if (biome == BIOME_WASTELANDS) {
is_dull = true;
}else if (time_brightness >= 0.20 && time_brightness < 0.50) {
is_dawn = true;
}
}
video::SColorf bgcolor_bright_normal_f(170./255,200./255,230./255, 1.0);
video::SColorf bgcolor_bright_indoor_f(100./255,100./255,100./255, 1.0);
video::SColorf bgcolor_bright_dawn_f(0.666*1.2,0.549*1.0,0.220*1.2,1.0);
video::SColorf skycolor_dull_f = video::SColor(255, 170,170,170);
video::SColorf skycolor_bright_normal_f = video::SColor(255, 121, 141, 232);
video::SColorf skycolor_bright_dawn_f = video::SColor(255, 46, 60, 132);
video::SColorf cloudcolor_dull_f = video::SColor(255, 100,100,100);
video::SColorf cloudcolor_bright_normal_f = video::SColor(255, 240,240,255);
video::SColorf cloudcolor_bright_dawn_f(1.0, 0.7, 0.5, 1.0);
m_clouds_visible = true;
if (biome == BIOME_SPACE) {
m_brightness = 0.02;
m_clouds_visible = false;
}else if (biome == BIOME_WASTELANDS) {
m_sunlight_seen = false;
m_brightness = m_brightness * 0.98 + direct_brightness * 0.02;
if (m_brightness > 0.25)
m_brightness = 0.25;
}else if (biome == BIOME_THEDEEP) {
m_sunlight_seen = false;
m_brightness = 0.02;
m_clouds_visible = false;
}else if (sunlight_seen) {
if (fabs(time_brightness - m_brightness) < 0.2) {
m_brightness = m_brightness * 0.95 + time_brightness * 0.05;
}else{
m_brightness = m_brightness * 0.80 + time_brightness * 0.20;
}
}else if (direct_brightness < m_brightness) {
m_brightness = m_brightness * 0.95 + direct_brightness * 0.05;
}else{
m_brightness = m_brightness * 0.98 + direct_brightness * 0.02;
}
float color_change_fraction = 0.98;
if (is_dull) {
m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(bgcolor_bright_indoor_f, color_change_fraction);
m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(cloudcolor_dull_f, color_change_fraction);
m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(skycolor_dull_f, color_change_fraction);
}else if (sunlight_seen) {
if (is_dawn) {
m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(bgcolor_bright_dawn_f, color_change_fraction);
m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(skycolor_bright_dawn_f, color_change_fraction);
m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(cloudcolor_bright_dawn_f, color_change_fraction);
}else{
m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(bgcolor_bright_normal_f, color_change_fraction);
m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(skycolor_bright_normal_f, color_change_fraction);
m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(cloudcolor_bright_normal_f, color_change_fraction);
}
}else{
m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(bgcolor_bright_indoor_f, color_change_fraction);
m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(cloudcolor_bright_normal_f, color_change_fraction);
m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(bgcolor_bright_indoor_f, color_change_fraction);
m_clouds_visible = false;
}
video::SColor bgcolor_bright = m_bgcolor_bright_f.toSColor();
m_bgcolor = video::SColor(
255,
bgcolor_bright.getRed() * m_brightness,
bgcolor_bright.getGreen() * m_brightness,
bgcolor_bright.getBlue() * m_brightness
);
video::SColor skycolor_bright = m_skycolor_bright_f.toSColor();
m_skycolor = video::SColor(
255,
skycolor_bright.getRed() * m_brightness,
skycolor_bright.getGreen() * m_brightness,
skycolor_bright.getBlue() * m_brightness
);
float cloud_direct_brightness = 0;
if (sunlight_seen) {
cloud_direct_brightness = time_brightness;
if (time_brightness >= 0.2 && time_brightness < 0.7)
cloud_direct_brightness *= 1.3;
}else{
cloud_direct_brightness = direct_brightness;
}
m_cloud_brightness = m_cloud_brightness * 0.95 + cloud_direct_brightness * (1.0 - 0.95);
m_cloudcolor_f = video::SColorf(
m_cloudcolor_bright_f.getRed() * m_cloud_brightness,
m_cloudcolor_bright_f.getGreen() * m_cloud_brightness,
m_cloudcolor_bright_f.getBlue() * m_cloud_brightness,
1.0
);
}