1 /**************************************************************************/
2 /* Copyright 2009 Tim Day */
3 /* */
4 /* This file is part of Fracplanet */
5 /* */
6 /* Fracplanet is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* Fracplanet is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public License */
17 /* along with Fracplanet. If not, see <http://www.gnu.org/licenses/>. */
18 /**************************************************************************/
19
20 #include "triangle_mesh_cloud.h"
21
22 #include "noise.h"
23 #include "matrix34.h"
24 #include "parameters_render.h"
25
TriangleMeshCloud(Progress * progress)26 TriangleMeshCloud::TriangleMeshCloud(Progress* progress)
27 :TriangleMesh(progress)
28 {}
29
~TriangleMeshCloud()30 TriangleMeshCloud::~TriangleMeshCloud()
31 {}
32
write_povray(std::ofstream & out,const ParametersSave &,const ParametersCloud &) const33 void TriangleMeshCloud::write_povray(std::ofstream& out,const ParametersSave&,const ParametersCloud&) const
34 {
35 // Double illuminate so underside of clouds is white.
36 // No-shadow so clouds don't cast crazy dark shadows.
37 TriangleMesh::write_povray(out,false,true,true);
38 }
39
write_blender(std::ofstream & out,const ParametersSave & parameters_save,const ParametersCloud &,const std::string & mesh_name) const40 void TriangleMeshCloud::write_blender(std::ofstream& out,const ParametersSave& parameters_save,const ParametersCloud&,const std::string& mesh_name) const
41 {
42 TriangleMesh::write_blender
43 (
44 out,
45 mesh_name+".cloud",
46 (parameters_save.blender_per_vertex_alpha ? 0 : ¶meters_save.parameters_render->background_colour_low)
47 );
48 }
49
50 namespace
51 {
52 class ScanConvertHelper : public ScanConvertBackend
53 {
54 public:
ScanConvertHelper(Raster<uchar> & image,const boost::array<float,3> & vertex_colours)55 ScanConvertHelper(Raster<uchar>& image,const boost::array<float,3>& vertex_colours)
56 :ScanConvertBackend(image.width(),image.height())
57 ,_image(image)
58 ,_vertex_colours(vertex_colours)
59 {}
~ScanConvertHelper()60 virtual ~ScanConvertHelper()
61 {}
scan_convert_backend(uint,const ScanEdge &,const ScanEdge &) const62 virtual void scan_convert_backend(uint /*y*/,const ScanEdge& /*edge0*/,const ScanEdge& /*edge1*/) const
63 {}
subdivide(const boost::array<XYZ,3> &,const XYZ &,const ScanConverter &) const64 virtual void subdivide(const boost::array<XYZ,3>& /*v*/,const XYZ& /*m*/,const ScanConverter& /*scan_converter*/) const
65 {}
66 private:
67 Raster<uchar>& _image;
68 const boost::array<float,3>& _vertex_colours;
69 };
70 }
71
render_texture(Raster<uchar> & image) const72 void TriangleMeshCloud::render_texture(Raster<uchar>& image) const
73 {
74 assert(false);
75 image.fill(0);
76 for (uint i=0;i<triangles();i++)
77 {
78 const Triangle& t=triangle(i);
79 const boost::array<XYZ,3> vertex_positions
80 ={{
81 vertex(t.vertex(0)).position(),
82 vertex(t.vertex(1)).position(),
83 vertex(t.vertex(2)).position()
84 }};
85 const boost::array<float,3> vertex_colours
86 ={{
87 FloatRGBA(vertex(t.vertex(0)).colour(0)).a,
88 FloatRGBA(vertex(t.vertex(1)).colour(0)).a,
89 FloatRGBA(vertex(t.vertex(2)).colour(0)).a
90 }};
91
92 ScanConvertHelper scan_convert_backend(image,vertex_colours);
93 geometry().scan_convert
94 (
95 vertex_positions,
96 scan_convert_backend
97 );
98 }
99 }
100
do_cloud(const ParametersCloud & parameters)101 void TriangleMeshCloud::do_cloud(const ParametersCloud& parameters)
102 {
103 compute_vertex_normals();
104
105 progress_start(100,"Cloud colouring");
106
107 const ByteRGBA c(parameters.colour);
108
109 //! \todo Wire up terms, decay and base fequency and thresholds
110 MultiscaleNoise noise(parameters.seed,6,0.5);
111 for (uint i=0;i<vertices();i++)
112 {
113 progress_step((100*i)/vertices());
114
115 const float v=0.5+0.5*noise(4.0f*vertex(i).position());
116 const float v_min=0.5f;
117 const float v_max=0.6f;
118 const float v_k=1.0f/(v_max-v_min);
119 const float vs=std::min(1.0f,std::max(0.0f,(v-v_min)*v_k));
120
121 vertex(i).colour(0,ByteRGBA(c.r,c.g,c.b,static_cast<uint>(255.0*vs)));
122
123 // Set other colour (unused) to red for debug
124 vertex(i).colour(1,ByteRGBA(255,0,0,255));
125 }
126 progress_complete("Cloud colouring completed");
127
128 // TODO: Eliminate all-transparent triangles & unused vertices.
129 // Leave if nothing left
130
131 // TODO: Bias weather into temperate bands (maybe not)
132
133 progress_start(100,"Weather systems");
134
135 Random01 r01(parameters.seed);
136 const uint steps=100*vertices();
137 uint step=0;
138 for (uint i=0;i<parameters.weather_systems;i++)
139 {
140 const uint random_vertex=static_cast<uint>(r01()*vertices());
141 const XYZ position(vertex(random_vertex).position());
142 const XYZ axis(geometry().up(position));
143
144 // Rotate opposite direction in other hemisphere
145 const float strength=r01()*(position.z<0.0 ? -M_PI : M_PI);
146
147 for (uint j=0;j<vertices();j++)
148 {
149 progress_step((100*step)/steps);
150 step++;
151
152 const XYZ p(vertex(j).position());
153 const XYZ pn=geometry().up(p);
154 const float pna=pn%axis;
155
156 if (pna>0.0f) // Don't create same feature on other side of planet (actually the distance would be big so could drop this)
157 {
158 const float distance=(p-position).magnitude();
159 const float rotation_angle=strength*exp(-10.0*distance);
160
161 // Now rotate p about axis through position by the rotation angle
162 // TODO: Optimise! axis and position is the same for all points; we're constantly recomputing the basis change matrices.
163 // Create a stateful version of Matrix34RotateAboutAxisThrough.
164 vertex(j).position
165 (
166 Matrix34RotateAboutAxisThrough(axis,rotation_angle,position)*p
167 );
168 }
169 }
170 }
171
172 progress_complete("Weather systems completed");
173
174 _triangle_switch_colour=triangles();
175 }
176
TriangleMeshCloudPlanet(const ParametersCloud & parameters,Progress * progress)177 TriangleMeshCloudPlanet::TriangleMeshCloudPlanet(const ParametersCloud& parameters,Progress* progress)
178 :TriangleMesh(progress)
179 ,TriangleMeshCloud(progress)
180 ,TriangleMeshSubdividedIcosahedron(1.0+parameters.cloudbase,parameters.subdivisions,parameters.subdivisions,parameters.seed,XYZ(0.0,0.0,0.0),progress)
181 {
182 do_cloud(parameters);
183 }
184
TriangleMeshCloudFlat(const ParametersCloud & parameters,Progress * progress)185 TriangleMeshCloudFlat::TriangleMeshCloudFlat(const ParametersCloud& parameters,Progress* progress)
186 :TriangleMesh(progress)
187 ,TriangleMeshCloud(progress)
188 ,TriangleMeshFlat(parameters.object_type,parameters.cloudbase,parameters.seed,progress)
189 {
190 subdivide(parameters.subdivisions,parameters.subdivisions,XYZ(0.0,0.0,0.0));
191 do_cloud(parameters);
192 }
193
194