3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "client/renderingengine.h"
23 #include "constants.h"
30 // Menu clouds are created later
32 Clouds
*g_menuclouds
= NULL
;
33 irr::scene::ISceneManager
*g_menucloudsmgr
= NULL
;
36 static constexpr const float cloud_size
= BS
* 64.0f
;
38 static void cloud_3d_setting_changed(const std::string
&settingname
, void *data
)
40 ((Clouds
*)data
)->readSettings();
43 Clouds::Clouds(scene::ISceneManager
* mgr
,
47 scene::ISceneNode(mgr
->getRootSceneNode(), mgr
, id
),
50 m_material
.setFlag(video::EMF_LIGHTING
, false);
51 //m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
52 m_material
.setFlag(video::EMF_BACK_FACE_CULLING
, true);
53 m_material
.setFlag(video::EMF_BILINEAR_FILTER
, false);
54 m_material
.setFlag(video::EMF_FOG_ENABLE
, true);
55 m_material
.setFlag(video::EMF_ANTI_ALIASING
, true);
56 //m_material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
57 m_material
.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL
;
59 m_params
.height
= 120;
60 m_params
.density
= 0.4f
;
61 m_params
.thickness
= 16.0f
;
62 m_params
.color_bright
= video::SColor(229, 240, 240, 255);
63 m_params
.color_ambient
= video::SColor(255, 0, 0, 0);
64 m_params
.speed
= v2f(0.0f
, -2.0f
);
67 g_settings
->registerChangedCallback("enable_3d_clouds",
68 &cloud_3d_setting_changed
, this);
75 g_settings
->deregisterChangedCallback("enable_3d_clouds",
76 &cloud_3d_setting_changed
, this);
79 void Clouds::OnRegisterSceneNode()
83 SceneManager
->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT
);
84 //SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
87 ISceneNode::OnRegisterSceneNode();
93 if (m_params
.density
<= 0.0f
)
94 return; // no need to do anything
96 video::IVideoDriver
* driver
= SceneManager
->getVideoDriver();
98 if(SceneManager
->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT
)
99 //if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_SOLID)
102 ScopeProfiler
sp(g_profiler
, "Clouds::render()", SPT_AVG
);
104 int num_faces_to_draw
= m_enable_3d
? 6 : 1;
106 m_material
.setFlag(video::EMF_BACK_FACE_CULLING
, m_enable_3d
);
108 driver
->setTransform(video::ETS_WORLD
, AbsoluteTransformation
);
109 driver
->setMaterial(m_material
);
112 Clouds move from Z+ towards Z-
115 const float cloud_full_radius
= cloud_size
* m_cloud_radius_i
;
117 v2f
camera_pos_2d(m_camera_pos
.X
, m_camera_pos
.Z
);
118 // Position of cloud noise origin from the camera
119 v2f cloud_origin_from_camera_f
= m_origin
- camera_pos_2d
;
120 // The center point of drawing in the noise
121 v2f center_of_drawing_in_noise_f
= -cloud_origin_from_camera_f
;
122 // The integer center point of drawing in the noise
123 v2s16
center_of_drawing_in_noise_i(
124 std::floor(center_of_drawing_in_noise_f
.X
/ cloud_size
),
125 std::floor(center_of_drawing_in_noise_f
.Y
/ cloud_size
)
128 // The world position of the integer center point of drawing in the noise
129 v2f world_center_of_drawing_in_noise_f
= v2f(
130 center_of_drawing_in_noise_i
.X
* cloud_size
,
131 center_of_drawing_in_noise_i
.Y
* cloud_size
134 /*video::SColor c_top(128,b*240,b*240,b*255);
135 video::SColor c_side_1(128,b*230,b*230,b*255);
136 video::SColor c_side_2(128,b*220,b*220,b*245);
137 video::SColor c_bottom(128,b*205,b*205,b*230);*/
138 video::SColorf
c_top_f(m_color
);
139 video::SColorf
c_side_1_f(m_color
);
140 video::SColorf
c_side_2_f(m_color
);
141 video::SColorf
c_bottom_f(m_color
);
142 c_side_1_f
.r
*= 0.95;
143 c_side_1_f
.g
*= 0.95;
144 c_side_1_f
.b
*= 0.95;
145 c_side_2_f
.r
*= 0.90;
146 c_side_2_f
.g
*= 0.90;
147 c_side_2_f
.b
*= 0.90;
148 c_bottom_f
.r
*= 0.80;
149 c_bottom_f
.g
*= 0.80;
150 c_bottom_f
.b
*= 0.80;
151 video::SColor c_top
= c_top_f
.toSColor();
152 video::SColor c_side_1
= c_side_1_f
.toSColor();
153 video::SColor c_side_2
= c_side_2_f
.toSColor();
154 video::SColor c_bottom
= c_bottom_f
.toSColor();
156 // Get fog parameters for setting them back later
157 video::SColor
fog_color(0,0,0,0);
158 video::E_FOG_TYPE fog_type
= video::EFT_FOG_LINEAR
;
162 bool fog_pixelfog
= false;
163 bool fog_rangefog
= false;
164 driver
->getFog(fog_color
, fog_type
, fog_start
, fog_end
, fog_density
,
165 fog_pixelfog
, fog_rangefog
);
168 driver
->setFog(fog_color
, fog_type
, cloud_full_radius
* 0.5,
169 cloud_full_radius
*1.2, fog_density
, fog_pixelfog
, fog_rangefog
);
173 std::vector
<char> grid(m_cloud_radius_i
* 2 * m_cloud_radius_i
* 2); // vector<bool> is broken
174 std::vector
<video::S3DVertex
> vertices
;
175 vertices
.reserve(16 * m_cloud_radius_i
* m_cloud_radius_i
);
177 for(s16 zi
= -m_cloud_radius_i
; zi
< m_cloud_radius_i
; zi
++) {
178 u32 si
= (zi
+ m_cloud_radius_i
) * m_cloud_radius_i
* 2 + m_cloud_radius_i
;
180 for (s16 xi
= -m_cloud_radius_i
; xi
< m_cloud_radius_i
; xi
++) {
183 grid
[i
] = gridFilled(
184 xi
+ center_of_drawing_in_noise_i
.X
,
185 zi
+ center_of_drawing_in_noise_i
.Y
190 #define GETINDEX(x, z, radius) (((z)+(radius))*(radius)*2 + (x)+(radius))
191 #define INAREA(x, z, radius) \
192 ((x) >= -(radius) && (x) < (radius) && (z) >= -(radius) && (z) < (radius))
194 for (s16 zi0
= -m_cloud_radius_i
; zi0
< m_cloud_radius_i
; zi0
++)
195 for (s16 xi0
= -m_cloud_radius_i
; xi0
< m_cloud_radius_i
; xi0
++)
199 // Draw from back to front for proper transparency
201 zi
= m_cloud_radius_i
- zi
- 1;
203 xi
= m_cloud_radius_i
- xi
- 1;
205 u32 i
= GETINDEX(xi
, zi
, m_cloud_radius_i
);
210 v2f p0
= v2f(xi
,zi
)*cloud_size
+ world_center_of_drawing_in_noise_f
;
212 video::S3DVertex v
[4] = {
213 video::S3DVertex(0,0,0, 0,0,0, c_top
, 0, 1),
214 video::S3DVertex(0,0,0, 0,0,0, c_top
, 1, 1),
215 video::S3DVertex(0,0,0, 0,0,0, c_top
, 1, 0),
216 video::S3DVertex(0,0,0, 0,0,0, c_top
, 0, 0)
219 const f32 rx
= cloud_size
/ 2.0f
;
220 // if clouds are flat, the top layer should be at the given height
221 const f32 ry
= m_enable_3d
? m_params
.thickness
* BS
: 0.0f
;
222 const f32 rz
= cloud_size
/ 2;
224 for(int i
=0; i
<num_faces_to_draw
; i
++)
229 for (video::S3DVertex
&vertex
: v
) {
230 vertex
.Normal
.set(0,1,0);
232 v
[0].Pos
.set(-rx
, ry
,-rz
);
233 v
[1].Pos
.set(-rx
, ry
, rz
);
234 v
[2].Pos
.set( rx
, ry
, rz
);
235 v
[3].Pos
.set( rx
, ry
,-rz
);
238 if (INAREA(xi
, zi
- 1, m_cloud_radius_i
)) {
239 u32 j
= GETINDEX(xi
, zi
- 1, m_cloud_radius_i
);
243 for (video::S3DVertex
&vertex
: v
) {
244 vertex
.Color
= c_side_1
;
245 vertex
.Normal
.set(0,0,-1);
247 v
[0].Pos
.set(-rx
, ry
,-rz
);
248 v
[1].Pos
.set( rx
, ry
,-rz
);
249 v
[2].Pos
.set( rx
, 0,-rz
);
250 v
[3].Pos
.set(-rx
, 0,-rz
);
253 if (INAREA(xi
+ 1, zi
, m_cloud_radius_i
)) {
254 u32 j
= GETINDEX(xi
+1, zi
, m_cloud_radius_i
);
258 for (video::S3DVertex
&vertex
: v
) {
259 vertex
.Color
= c_side_2
;
260 vertex
.Normal
.set(1,0,0);
262 v
[0].Pos
.set( rx
, ry
,-rz
);
263 v
[1].Pos
.set( rx
, ry
, rz
);
264 v
[2].Pos
.set( rx
, 0, rz
);
265 v
[3].Pos
.set( rx
, 0,-rz
);
268 if (INAREA(xi
, zi
+ 1, m_cloud_radius_i
)) {
269 u32 j
= GETINDEX(xi
, zi
+ 1, m_cloud_radius_i
);
273 for (video::S3DVertex
&vertex
: v
) {
274 vertex
.Color
= c_side_1
;
275 vertex
.Normal
.set(0,0,-1);
277 v
[0].Pos
.set( rx
, ry
, rz
);
278 v
[1].Pos
.set(-rx
, ry
, rz
);
279 v
[2].Pos
.set(-rx
, 0, rz
);
280 v
[3].Pos
.set( rx
, 0, rz
);
283 if (INAREA(xi
-1, zi
, m_cloud_radius_i
)) {
284 u32 j
= GETINDEX(xi
-1, zi
, m_cloud_radius_i
);
288 for (video::S3DVertex
&vertex
: v
) {
289 vertex
.Color
= c_side_2
;
290 vertex
.Normal
.set(-1,0,0);
292 v
[0].Pos
.set(-rx
, ry
, rz
);
293 v
[1].Pos
.set(-rx
, ry
,-rz
);
294 v
[2].Pos
.set(-rx
, 0,-rz
);
295 v
[3].Pos
.set(-rx
, 0, rz
);
298 for (video::S3DVertex
&vertex
: v
) {
299 vertex
.Color
= c_bottom
;
300 vertex
.Normal
.set(0,-1,0);
302 v
[0].Pos
.set( rx
, 0, rz
);
303 v
[1].Pos
.set(-rx
, 0, rz
);
304 v
[2].Pos
.set(-rx
, 0,-rz
);
305 v
[3].Pos
.set( rx
, 0,-rz
);
309 v3f
pos(p0
.X
, m_params
.height
* BS
, p0
.Y
);
310 pos
-= intToFloat(m_camera_offset
, BS
);
312 for (video::S3DVertex
&vertex
: v
) {
314 vertices
.push_back(vertex
);
318 int quad_count
= vertices
.size() / 4;
319 std::vector
<u16
> indices
;
320 indices
.reserve(quad_count
* 6);
321 for (int k
= 0; k
< quad_count
; k
++) {
322 indices
.push_back(4 * k
+ 0);
323 indices
.push_back(4 * k
+ 1);
324 indices
.push_back(4 * k
+ 2);
325 indices
.push_back(4 * k
+ 2);
326 indices
.push_back(4 * k
+ 3);
327 indices
.push_back(4 * k
+ 0);
329 driver
->drawVertexPrimitiveList(vertices
.data(), vertices
.size(), indices
.data(), 2 * quad_count
,
330 video::EVT_STANDARD
, scene::EPT_TRIANGLES
, video::EIT_16BIT
);
332 // Restore fog settings
333 driver
->setFog(fog_color
, fog_type
, fog_start
, fog_end
, fog_density
,
334 fog_pixelfog
, fog_rangefog
);
337 void Clouds::step(float dtime
)
339 m_origin
= m_origin
+ dtime
* BS
* m_params
.speed
;
342 void Clouds::update(const v3f
&camera_p
, const video::SColorf
&color_diffuse
)
344 video::SColorf
ambient(m_params
.color_ambient
);
345 video::SColorf
bright(m_params
.color_bright
);
346 m_camera_pos
= camera_p
;
347 m_color
.r
= core::clamp(color_diffuse
.r
* bright
.r
, ambient
.r
, 1.0f
);
348 m_color
.g
= core::clamp(color_diffuse
.g
* bright
.g
, ambient
.g
, 1.0f
);
349 m_color
.b
= core::clamp(color_diffuse
.b
* bright
.b
, ambient
.b
, 1.0f
);
350 m_color
.a
= bright
.a
;
352 // is the camera inside the cloud mesh?
353 m_camera_inside_cloud
= false; // default
355 float camera_height
= camera_p
.Y
;
356 if (camera_height
>= m_box
.MinEdge
.Y
&&
357 camera_height
<= m_box
.MaxEdge
.Y
) {
359 camera_in_noise
.X
= floor((camera_p
.X
- m_origin
.X
) / cloud_size
+ 0.5);
360 camera_in_noise
.Y
= floor((camera_p
.Z
- m_origin
.Y
) / cloud_size
+ 0.5);
361 bool filled
= gridFilled(camera_in_noise
.X
, camera_in_noise
.Y
);
362 m_camera_inside_cloud
= filled
;
367 void Clouds::readSettings()
369 m_cloud_radius_i
= g_settings
->getU16("cloud_radius");
370 m_enable_3d
= g_settings
->getBool("enable_3d_clouds");
373 bool Clouds::gridFilled(int x
, int y
) const
375 float cloud_size_noise
= cloud_size
/ (BS
* 200.f
);
376 float noise
= noise2d_perlin(
377 (float)x
* cloud_size_noise
,
378 (float)y
* cloud_size_noise
,
380 // normalize to 0..1 (given 3 octaves)
381 static constexpr const float noise_bound
= 1.0f
+ 0.5f
+ 0.25f
;
382 float density
= noise
/ noise_bound
* 0.5f
+ 0.5f
;
383 return (density
< m_params
.density
);