aboutsummaryrefslogtreecommitdiff
path: root/src/client/mesh.h
blob: dbc091a06fd716ca64987ab5752d9f77d3fd717c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include "irrlichttypes_extrabloated.h"
#include "nodedef.h"

/*!
 * Applies shading to a color based on the surface's
 * normal vector.
 */
void applyFacesShading(video::SColor &color, const v3f &normal);

/*
	Create a new cube mesh.
	Vertices are at (+-scale.X/2, +-scale.Y/2, +-scale.Z/2).

	The resulting mesh has 6 materials (up, down, right, left, back, front)
	which must be defined by the caller.
*/
scene::IAnimatedMesh* createCubeMesh(v3f scale);

/*
	Multiplies each vertex coordinate by the specified scaling factors
	(componentwise vector multiplication).
*/
void scaleMesh(scene::IMesh *mesh, v3f scale);

/*
	Translate each vertex coordinate by the specified vector.
*/
void translateMesh(scene::IMesh *mesh, v3f vec);

/*!
 * Sets a constant color for all vertices in the mesh buffer.
 */
void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color);

/*
	Set a constant color for all vertices in the mesh
*/
void setMeshColor(scene::IMesh *mesh, const video::SColor &color);


/*
	Sets texture coords for vertices in the mesh buffer.
	`uv[]` must have `count` elements
*/
void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count);

/*
	Set a constant color for an animated mesh
*/
void setAnimatedMeshColor(scene::IAnimatedMeshSceneNode *node, const video::SColor &color);

/*!
 * Overwrites the color of a mesh buffer.
 * The color is darkened based on the normal vector of the vertices.
 */
void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor);

/*
	Set the color of all vertices in the mesh.
	For each vertex, determine the largest absolute entry in
	the normal vector, and choose one of colorX, colorY or
	colorZ accordingly.
*/
void setMeshColorByNormalXYZ(scene::IMesh *mesh,
		const video::SColor &colorX,
		const video::SColor &colorY,
		const video::SColor &colorZ);

void setMeshColorByNormal(scene::IMesh *mesh, const v3f &normal,
		const video::SColor &color);

/*
	Rotate the mesh by 6d facedir value.
	Method only for meshnodes, not suitable for entities.
*/
void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir);

/*
	Rotate the mesh around the axis and given angle in degrees.
*/
void rotateMeshXYby (scene::IMesh *mesh, f64 degrees);
void rotateMeshXZby (scene::IMesh *mesh, f64 degrees);
void rotateMeshYZby (scene::IMesh *mesh, f64 degrees);

/*
 *  Clone the mesh buffer.
 *  The returned pointer should be dropped.
 */
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);

/*
	Clone the mesh.
*/
scene::SMesh* cloneMesh(scene::IMesh *src_mesh);

/*
	Convert nodeboxes to mesh. Each tile goes into a different buffer.
	boxes - set of nodeboxes to be converted into cuboids
	uv_coords[24] - table of texture uv coords for each cuboid face
	expand - factor by which cuboids will be resized
*/
scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
		const f32 *uv_coords = NULL, float expand = 0);

/*
	Update bounding box for a mesh.
*/
void recalculateBoundingBox(scene::IMesh *src_mesh);

/*
	Check if mesh has valid normals and return true if it does.
	We assume normal to be valid when it's 0 < length < Inf. and not NaN
 */
bool checkMeshNormals(scene::IMesh *mesh);

/*
	Vertex cache optimization according to the Forsyth paper:
	http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
	Ported from irrlicht 1.8
*/
scene::IMesh* createForsythOptimizedMesh(const scene::IMesh *mesh);
547'>547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
#include "sky.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "ICameraSceneNode.h"
#include "S3DVertex.h"
#include "client/tile.h"
#include "noise.h"            // easeCurve
#include "profiler.h"
#include "util/numeric.h"
#include <cmath>
#include "settings.h"
#include "camera.h"           // CameraModes

//! constructor
Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
		ITextureSource *tsrc):
		scene::ISceneNode(parent, mgr, id),
		m_visible(true),
		m_fallback_bg_color(255,255,255,255),
		m_first_update(true),
		m_brightness(0.5),
		m_cloud_brightness(0.5),
		m_bgcolor_bright_f(1,1,1,1),
		m_skycolor_bright_f(1,1,1,1),
		m_cloudcolor_bright_f(1,1,1,1)
{
	setAutomaticCulling(scene::EAC_OFF);
	m_box.MaxEdge.set(0,0,0);
	m_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;

	m_materials[0] = mat;

	m_materials[1] = mat;
	//m_materials[1].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
	m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;

	m_materials[2] = mat;
	m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png"));
	m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
	//m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;

	m_sun_texture = tsrc->isKnownSourceImage("sun.png") ?
		tsrc->getTextureForMesh("sun.png") : NULL;
	m_moon_texture = tsrc->isKnownSourceImage("moon.png") ?
		tsrc->getTextureForMesh("moon.png") : NULL;
	m_sun_tonemap = tsrc->isKnownSourceImage("sun_tonemap.png") ?
		tsrc->getTexture("sun_tonemap.png") : NULL;
	m_moon_tonemap = tsrc->isKnownSourceImage("moon_tonemap.png") ?
		tsrc->getTexture("moon_tonemap.png") : NULL;

	if (m_sun_texture){
		m_materials[3] = mat;
		m_materials[3].setTexture(0, m_sun_texture);
		m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
		if (m_sun_tonemap)
			m_materials[3].Lighting = true;
	}
	if (m_moon_texture){
		m_materials[4] = mat;
		m_materials[4].setTexture(0, m_moon_texture);
		m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
		if (m_moon_tonemap)
			m_materials[4].Lighting = true;
	}

	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();
	}

	m_directional_colored_fog = g_settings->getBool("directional_colored_fog");
}

void Sky::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this, scene::ESNRP_SKY_BOX);

	scene::ISceneNode::OnRegisterSceneNode();
}

//! renders the node.
void Sky::render()
{
	if(!m_visible)
		return;

	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)
	{
		float sunsize = 0.07;
		video::SColorf suncolor_f(1, 1, 0, 1);
		suncolor_f.r = 1;
		suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.7+m_time_brightness*(0.5)));
		suncolor_f.b = MYMAX(0.0, m_brightness*0.95);
		video::SColorf suncolor2_f(1, 1, 1, 1);
		suncolor_f.r = 1;
		suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.85+m_time_brightness*(0.5)));
		suncolor_f.b = MYMAX(0.0, m_brightness);

		float moonsize = 0.04;
		video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
		video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);
		
		float nightlength = 0.415;
		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);
		/*std::cerr<<"time_of_day="<<m_time_of_day<<" -> "
				<<"wicked_time_of_day="<<wicked_time_of_day<<std::endl;*/

		video::SColor suncolor = suncolor_f.toSColor();
		video::SColor suncolor2 = suncolor2_f.toSColor();
		video::SColor mooncolor = mooncolor_f.toSColor();
		video::SColor mooncolor2 = mooncolor2_f.toSColor();

		// Calculate offset normalized to the X dimension of a 512x1 px tonemap
		float offset=(1.0-fabs(sin((m_time_of_day - 0.5)*irr::core::PI)))*511;

		if (m_sun_tonemap){
			u8 * texels = (u8 *)m_sun_tonemap->lock();
			video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4);
			video::SColor texel_color (255,texel->getRed(),texel->getGreen(), texel->getBlue());
			m_sun_tonemap->unlock();
			m_materials[3].EmissiveColor = texel_color;
		}
		if (m_moon_tonemap){
			u8 * texels = (u8 *)m_moon_tonemap->lock();
			video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4);
			video::SColor texel_color (255,texel->getRed(),texel->getGreen(), texel->getBlue());
			m_moon_tonemap->unlock();
			m_materials[4].EmissiveColor = texel_color;
		}

		const f32 t = 1.0f;
		const f32 o = 0.0f;
		static const u16 indices[4] = {0,1,2,3};
		video::S3DVertex vertices[4];
		
		driver->setMaterial(m_materials[1]);
		
		//video::SColor cloudyfogcolor(255,255,255,255);
		video::SColor cloudyfogcolor = m_bgcolor;
		//video::SColor cloudyfogcolor = m_bgcolor.getInterpolated(m_skycolor, 0.5);
		
		// Draw far cloudy fog thing
		for(u32 j=0; j<4; j++)
		{
			video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45);
			vertices[0] = video::S3DVertex(-1, 0.08,-1, 0,0,1, c, t, t);
			vertices[1] = video::S3DVertex( 1, 0.08,-1, 0,0,1, c, o, t);
			vertices[2] = video::S3DVertex( 1, 0.12,-1, 0,0,1, c, o, o);
			vertices[3] = video::S3DVertex(-1, 0.12,-1, 0,0,1, c, t, o);
			for(u32 i=0; i<4; i++){
				if(j==0)
					// Don't switch
					{}
				else if(j==1)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else if(j==2)
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
				else
					// Switch from -Z (south) to -Z (north)
					vertices[i].Pos.rotateXZBy(-180);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}
		for(u32 j=0; j<4; j++)
		{
			video::SColor c = cloudyfogcolor;
			vertices[0] = video::S3DVertex(-1,-1.0,-1, 0,0,1, c, t, t);
			vertices[1] = video::S3DVertex( 1,-1.0,-1, 0,0,1, c, o, t);
			vertices[2] = video::S3DVertex( 1, 0.08,-1, 0,0,1, c, o, o);
			vertices[3] = video::S3DVertex(-1, 0.08,-1, 0,0,1, c, t, o);
			for(u32 i=0; i<4; i++){
				if(j==0)
					// Don't switch
					{}
				else if(j==1)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else if(j==2)
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
				else
					// Switch from -Z (south) to -Z (north)
					vertices[i].Pos.rotateXZBy(-180);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}

		driver->setMaterial(m_materials[2]);

		{
			float mid1 = 0.25;
			float mid = (wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1));
			float a_ = 1.0 - fabs(wicked_time_of_day - mid) * 35.0;
			float a = easeCurve(MYMAX(0, MYMIN(1, a_)));
			//std::cerr<<"a_="<<a_<<" a="<<a<<std::endl;
			video::SColor c(255,255,255,255);
			float y = -(1.0 - a) * 0.2;
			vertices[0] = video::S3DVertex(-1,-0.05+y,-1, 0,0,1, c, t, t);
			vertices[1] = video::S3DVertex( 1,-0.05+y,-1, 0,0,1, c, o, t);
			vertices[2] = video::S3DVertex( 1, 0.2+y,-1, 0,0,1, c, o, o);
			vertices[3] = video::S3DVertex(-1, 0.2+y,-1, 0,0,1, c, t, o);
			for(u32 i=0; i<4; i++){
				if(wicked_time_of_day < 0.5)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}

		// Draw sun
		if(wicked_time_of_day > 0.15 && wicked_time_of_day < 0.85){
			if (!m_sun_texture){
				driver->setMaterial(m_materials[1]);
				float d = sunsize * 1.7;
				video::SColor c = suncolor;
				c.setAlpha(0.05*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);

				d = sunsize * 1.2;
				c = suncolor;
				c.setAlpha(0.15*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);

				d = sunsize;
				vertices[0] = video::S3DVertex(-d,-d,-1, 0,0,1, suncolor, t, t);
				vertices[1] = video::S3DVertex( d,-d,-1, 0,0,1, suncolor, o, t);
				vertices[2] = video::S3DVertex( d, d,-1, 0,0,1, suncolor, o, o);
				vertices[3] = video::S3DVertex(-d, d,-1, 0,0,1, suncolor, 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);

				d = sunsize * 0.7;
				vertices[0] = video::S3DVertex(-d,-d,-1, 0,0,1, suncolor2, t, t);
				vertices[1] = video::S3DVertex( d,-d,-1, 0,0,1, suncolor2, o, t);
				vertices[2] = video::S3DVertex( d, d,-1, 0,0,1, suncolor2, o, o);
				vertices[3] = video::S3DVertex(-d, d,-1, 0,0,1, suncolor2, 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);
			} else {
				driver->setMaterial(m_materials[3]);
				float d = sunsize * 1.7;
				video::SColor c;
				if (m_sun_tonemap)
					c = video::SColor (0,0,0,0);
				else
					c = video::SColor (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)
		{
			if (!m_moon_texture){
				driver->setMaterial(m_materials[1]);
				float d = moonsize * 1.9;
				video::SColor c = mooncolor;
				c.setAlpha(0.05*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);