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 : &parameters_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