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.h"
21 
22 #include <map>
23 
TriangleMesh(Progress * progress)24 TriangleMesh::TriangleMesh(Progress* progress)
25   :_triangle_switch_colour(0)
26    ,_emissive(0.0)
27    ,_progress(progress)
28 {}
29 
~TriangleMesh()30 TriangleMesh::~TriangleMesh()
31 {}
32 
progress_start(uint steps,const std::string & info) const33 void TriangleMesh::progress_start(uint steps,const std::string& info) const
34 {
35   if (_progress) _progress->progress_start(steps,info);
36 }
progress_stall(const std::string & reason) const37 void TriangleMesh::progress_stall(const std::string& reason) const
38 {
39   if (_progress) _progress->progress_stall(reason);
40 }
progress_step(uint step) const41 void TriangleMesh::progress_step(uint step) const
42 {
43   if (_progress) _progress->progress_step(step);
44 }
progress_complete(const std::string & info) const45 void TriangleMesh::progress_complete(const std::string& info) const
46 {
47   if (_progress) _progress->progress_complete(info);
48 }
49 
triangle_normal(uint i) const50 const XYZ TriangleMesh::triangle_normal(uint i) const
51 {
52   const Triangle& t=triangle(i);
53   const XYZ& v0=vertex(t.vertex(0)).position();
54   const XYZ& v1=vertex(t.vertex(1)).position();
55   const XYZ& v2=vertex(t.vertex(2)).position();
56 
57   const XYZ n((v1-v0)*(v2-v0));
58 
59   return n.normalised();
60 }
61 
compute_vertex_normals()62 void TriangleMesh::compute_vertex_normals()
63 {
64   const uint steps=triangles()+vertices();
65   uint step=0;
66 
67   progress_start(100,"Compute normals");
68 
69   {
70     std::vector<std::vector<uint> > vertices_to_triangles(vertices());
71     for (uint i=0;i<triangles();i++)
72       {
73     step++;
74     progress_step((100*step)/steps);
75 
76     const Triangle& t=triangle(i);
77     vertices_to_triangles[t.vertex(0)].push_back(i);
78     vertices_to_triangles[t.vertex(1)].push_back(i);
79     vertices_to_triangles[t.vertex(2)].push_back(i);
80       }
81 
82     for (uint i=0;i<vertices();i++)
83       {
84     step++;
85     progress_step((100*step)/steps);
86 
87     XYZ n(0.0,0.0,0.0);
88     for (uint j=0;j<vertices_to_triangles[i].size();j++)
89       {
90         n+=triangle_normal(vertices_to_triangles[i][j]);
91       }
92     n/=vertices_to_triangles[i].size();
93 
94     vertex(i).normal(n);
95       }
96   }
97 
98   progress_complete("Normals computed");
99 }
100 
101 /*! level parameter is just for progress information
102  */
subdivide(const XYZ & variation,uint level,uint levels)103 void TriangleMesh::subdivide(const XYZ& variation,uint level,uint levels)
104 {
105   const uint steps=vertices()+triangles();
106   uint step=0;
107 
108   {
109     std::ostringstream msg;
110     msg
111       << "Subdivision level "
112       << 1+level  // Display 1...levels inclusive
113       << " of "
114       << levels;
115     progress_start(100,msg.str());
116   }
117 
118   {
119     // Efficiently move aside current geometry, get ready for fresh.
120     std::vector<Vertex> old_vertex;
121     old_vertex.swap(_vertex);
122 
123     std::vector<Triangle> old_triangle;
124     old_triangle.swap(_triangle);
125 
126     // Copy back all vertices and perturb
127     for (uint v=0;v<old_vertex.size();v++)
128       {
129     step++;
130     progress_step((100*step)/steps);
131 
132     _vertex.push_back(Vertex(geometry().perturb(old_vertex[v].position(),variation)));
133       }
134 
135     // Build a map from edges to new midpoints
136     typedef std::map<TriangleEdge,uint> EdgeMap;
137     EdgeMap edge_map;
138 
139     // Create new vertices and new triangles
140     for (uint t=0;t<old_triangle.size();t++)
141       {
142     step++;
143     progress_step((100*step)/steps);
144 
145     // These are the existing vertices
146     const uint i0=old_triangle[t].vertex(0);
147     const uint i1=old_triangle[t].vertex(1);
148     const uint i2=old_triangle[t].vertex(2);
149 
150     // These are the edges
151     TriangleEdge e01(i0,i1);
152     TriangleEdge e12(i1,i2);
153     TriangleEdge e20(i2,i0);
154 
155     // Find each edge in the map
156     // If any edges don't exist, create their new mid-point and remember it
157     EdgeMap::const_iterator e01it=edge_map.find(e01);
158     EdgeMap::const_iterator e12it=edge_map.find(e12);
159     EdgeMap::const_iterator e20it=edge_map.find(e20);
160 
161     const bool e01needed=(e01it==edge_map.end());
162     const bool e12needed=(e12it==edge_map.end());
163     const bool e20needed=(e20it==edge_map.end());
164 
165     if (e01needed)
166       {
167         e01it=edge_map.insert(EdgeMap::value_type(e01,_vertex.size())).first;
168         _vertex.push_back(Vertex(geometry().perturb(geometry().midpoint(vertex(i0).position(),vertex(i1).position()),variation)));
169       }
170 
171     if (e12needed)
172       {
173         e12it=edge_map.insert(EdgeMap::value_type(e12,_vertex.size())).first;
174         _vertex.push_back(Vertex(geometry().perturb(geometry().midpoint(vertex(i1).position(),vertex(i2).position()),variation)));
175       }
176 
177     if (e20needed)
178       {
179         e20it=edge_map.insert(EdgeMap::value_type(e20,_vertex.size())).first;
180         _vertex.push_back(Vertex(geometry().perturb(geometry().midpoint(vertex(i2).position(),vertex(i0).position()),variation)));
181       }
182 
183     // Create the subdivided triangles
184 
185     _triangle.push_back(Triangle(i0,(*e01it).second,(*e20it).second));
186     _triangle.push_back(Triangle((*e01it).second,i1,(*e12it).second));
187     _triangle.push_back(Triangle((*e20it).second,(*e12it).second,i2));
188     _triangle.push_back(Triangle((*e01it).second,(*e12it).second,(*e20it).second));
189       }
190   }
191 
192   progress_complete("Subdivision completed");
193 }
194 
subdivide(uint subdivisions,uint flat_subdivisions,const XYZ & variation)195 void TriangleMesh::subdivide(uint subdivisions,uint flat_subdivisions,const XYZ& variation)
196 {
197   for (uint s=0;s<subdivisions;s++)
198     {
199       if (s<flat_subdivisions)
200     subdivide(XYZ(0.0,0.0,0.0),s,subdivisions);
201       else
202     subdivide(variation/(1<<s),s,subdivisions);
203     }
204 }
205 
write_povray(std::ofstream & out,bool exclude_alternate_colour,bool double_illuminate,bool no_shadow) const206 void TriangleMesh::write_povray(std::ofstream& out,bool exclude_alternate_colour,bool double_illuminate,bool no_shadow) const
207 {
208   // \todo: No need to dump all vertices when not outputing all triangles.
209 
210   const uint triangles_to_output=(exclude_alternate_colour ? triangles_of_colour0() : triangles());
211 
212   // The number of steps is:
213   //   vertices() co-ordinates
214   // + vertices()+(exclude_alternate_colour ? 0 : vertices()) textures
215   // + triangles_to_output triangles
216 
217   const uint steps=vertices()+vertices()+(exclude_alternate_colour ? 0 : vertices())+triangles_to_output;
218   uint step=0;
219 
220   progress_start(100,"Writing mesh to POV-Ray file");
221 
222   // Use POV's mesh2 object
223 
224   out << "mesh2 {\n";
225 
226   // Output all the vertex co-ordinates
227   out << "vertex_vectors {" << vertices() << ",\n";
228 
229   for (uint v=0;v<vertices();v++)
230     {
231       step++;
232       progress_step((100*step)/steps);
233 
234       if (v!=0)
235     out << ",";
236       out << vertex(v).position().format_pov() << "\n";
237     }
238   out << "}\n";
239 
240   // Output the vertex colours, and handle emission
241   // If exclude_alternate_colour is true, don't output the alternate colours
242   out << "texture_list {" << vertices()+(exclude_alternate_colour ? 0 : vertices()) << "\n";
243 
244   for (uint c=0;c<(exclude_alternate_colour ? 1 : 2);c++)
245     for (uint v=0;v<vertices();v++)
246       {
247     step++;
248     progress_step((100*step)/steps);
249 
250     out << "texture{pigment{";
251     const FloatRGBA colour(vertex(v).colour(c));
252     if (colour.a==1.0f) out << "rgb " << colour.format_pov_rgb();
253     else out << "rgbf " << colour.format_pov_rgbf();
254     out << "}";
255 
256     if (emissive()!=0.0f && vertex(v).colour(c).a==0)
257       {
258         out << " finish{ambient " << emissive() << " diffuse " << 1.0f-emissive() << "}";
259       }
260     out << "}\n";
261       }
262 
263   out << "}\n";
264 
265   out << "face_indices {" << triangles_to_output << ",\n";
266   bool skip_initial_comma=true;
267   for (uint t=0;t<triangles_to_output;t++)
268     {
269       step++;
270       progress_step((100*step)/steps);
271 
272       if (skip_initial_comma)
273     skip_initial_comma=false;
274       else
275     out << ",";
276 
277       out
278     << "<"
279     << triangle(t).vertex(0)
280     << ","
281     << triangle(t).vertex(1)
282     << ","
283     << triangle(t).vertex(2)
284     << ">";
285 
286       out << "," << triangle(t).vertex(0)+(t<triangles_of_colour0() ? 0 : vertices());
287       out << "," << triangle(t).vertex(1)+(t<triangles_of_colour0() ? 0 : vertices());
288       out << "," << triangle(t).vertex(2)+(t<triangles_of_colour0() ? 0 : vertices());
289       out << "\n";
290     }
291   out << "}\n";
292   if (double_illuminate) out << "double_illuminate\n";
293   if (no_shadow) out << "no_shadow\n";
294   out << "}\n";
295 
296   progress_complete("Wrote mesh to POV-Ray file");
297 }
298 
299 /*! If faux_alpha is null, output per-vertex alpha.
300   If a colour is specified, use the vertex alpha to blend with it.
301  */
write_blender(std::ofstream & out,const std::string & mesh_name,const FloatRGBA * faux_alpha) const302 void TriangleMesh::write_blender(std::ofstream& out,const std::string& mesh_name,const FloatRGBA* faux_alpha) const
303   {
304     std::unique_ptr<ByteRGBA> byte_faux_alpha;
305     if (faux_alpha) byte_faux_alpha=std::unique_ptr<ByteRGBA>(new ByteRGBA(*faux_alpha));
306 
307     const uint steps=vertices()+triangles();
308     uint step=0;
309 
310     {
311       std::ostringstream msg;
312       msg << "Writing mesh " << mesh_name << " to Blender script file";
313       progress_start(100,msg.str());
314     }
315 
316     out <<
317         "mat0 = bpy.data.materials.new(\"fracplanet0\")\n"
318         "mat0.diffuse_color = (0.0, 1.0, 0.0)\n"
319         "mat0.use_vertex_color_paint = True\n"
320         "bpy.ops.object.material_slot_add()\n"
321         "the_mesh_obj.material_slots[-1].material = mat0\n"
322         "mat1 = bpy.data.materials.new(\"fracplanet1\")\n"
323         "mat1.diffuse_color = (0.0, 0.0, 1.0)\n"
324         "mat1.use_vertex_color_paint = True\n"
325         "bpy.ops.object.material_slot_add()\n"
326         "the_mesh_obj.material_slots[-1].material = mat1\n";
327 
328     for (uint v=0;v<vertices();v++)
329       {
330         step++;
331         progress_step((100*step)/steps);
332         out << "v(" << vertex(v).position().format_blender() << ")\n";
333       }
334 
335     out << "\n";
336 
337     for (uint t=0;t<triangles();t++)
338       {
339         step++;
340         progress_step((100*step)/steps);
341         const uint v0=triangle(t).vertex(0);
342         const uint v1=triangle(t).vertex(1);
343         const uint v2=triangle(t).vertex(2);
344         const uint c=(t<triangles_of_colour0() ? 0 : 1);
345         out
346       << "f("
347       << c << ", "
348       << v0 << ", "
349       << v1 << ", "
350       << v2 << ", "
351       << "(" << blender_alpha_workround(byte_faux_alpha.get(),vertex(v0).colour(c)).format_comma() << "), "
352       << "(" << blender_alpha_workround(byte_faux_alpha.get(),vertex(v1).colour(c)).format_comma() << "), "
353       << "(" << blender_alpha_workround(byte_faux_alpha.get(),vertex(v2).colour(c)).format_comma() << ")"
354       << ")\n";
355       }
356 
357     std::ostringstream msg;
358     msg << "Wrote mesh " << mesh_name << " to Blender script file";
359     progress_complete(msg.str());
360   } /*TriangleMesh::write_blender*/
361 
blender_alpha_workround(const ByteRGBA * f,const ByteRGBA & c)362 ByteRGBA TriangleMesh::blender_alpha_workround(const ByteRGBA* f,const ByteRGBA& c)
363 {
364   if (f)
365     {
366       const uint ia=static_cast<uint>(c.a);
367       return ByteRGBA
368     (
369      (ia*c.r+(255-ia)*f->r)/255,
370      (ia*c.g+(255-ia)*f->g)/255,
371      (ia*c.b+(255-ia)*f->b)/255,
372      255
373      );
374     }
375   else
376     return c;
377 }
378 
TriangleMeshFlat(ParametersObject::ObjectType obj,float z,uint seed,Progress * progress)379 TriangleMeshFlat::TriangleMeshFlat(ParametersObject::ObjectType obj,float z,uint seed,Progress* progress)
380 :TriangleMesh(progress)
381  ,_geometry(seed)
382 {
383   switch(obj)
384     {
385     case ParametersObject::ObjectTypeFlatTriangle:
386       for (uint i=0;i<3;i++)
387     {
388       add_vertex(Vertex(XYZ(cos(i*2.0*M_PI/3.0),sin(i*2.0*M_PI/3.0),z)));
389     }
390       add_triangle(Triangle(0,1,2));
391       break;
392 
393     case ParametersObject::ObjectTypeFlatSquare:
394       add_vertex(Vertex(XYZ( 0.0, 0.0,z)));
395       add_vertex(Vertex(XYZ( 1.0, 1.0,z)));
396       add_vertex(Vertex(XYZ(-1.0, 1.0,z)));
397       add_vertex(Vertex(XYZ(-1.0,-1.0,z)));
398       add_vertex(Vertex(XYZ( 1.0,-1.0,z)));
399 
400       for (uint i=0;i<4;i++)
401     {
402       add_triangle(Triangle(0,1+i,1+(i+1)%4));
403     }
404 
405       break;
406 
407     case ParametersObject::ObjectTypeFlatHexagon:
408     default:
409       add_vertex(Vertex(XYZ(0.0,0.0,z)));
410       for (uint i=0;i<6;i++)
411     {
412       add_vertex(Vertex(XYZ(cos(i*M_PI/3.0),sin(i*M_PI/3.0),z)));
413     }
414 
415       for (uint i=0;i<6;i++)
416     {
417       add_triangle(Triangle(0,1+i,1+(i+1)%6));
418     }
419       break;
420     }
421 }
422 
TriangleMeshIcosahedron(float radius,uint seed,Progress * progress)423 TriangleMeshIcosahedron::TriangleMeshIcosahedron(float radius,uint seed,Progress* progress)
424 :TriangleMesh(progress)
425  ,_geometry(seed)
426 {
427   const float x=0.525731112119133606;
428   const float z=0.850650808352039932;
429 
430   const float vdata[12][3]=
431     {
432       { -x,0.0,  z},
433       {  x,0.0,  z},
434       { -x,0.0, -z},
435       {  x,0.0, -z},
436       {0.0,  z,  x},
437       {0.0,  z, -x},
438       {0.0, -z,  x},
439       {0.0, -z, -x},
440       {  z,  x,0.0},
441       { -z,  x,0.0},
442       {  z, -x,0.0},
443       { -z, -x,0.0}
444     };
445 
446   for (uint v=0;v<12;v++)
447     add_vertex(Vertex(radius*XYZ(vdata[v][0],vdata[v][1],vdata[v][2]).normalised()));
448 
449   uint tindices[20][3]=
450     {
451       { 0, 4, 1},
452       { 0, 9, 4},
453       { 9, 5, 4},
454       { 4, 5, 8},
455       { 4, 8, 1},
456       { 8,10, 1},
457       { 8, 3,10},
458       { 5, 3, 8},
459       { 5, 2, 3},
460       { 2, 7, 3},
461       { 7,10, 3},
462       { 7, 6,10},
463       { 7,11, 6},
464       {11, 0, 6},
465       { 0, 1, 6},
466       { 6, 1,10},
467       { 9, 0,11},
468       { 9,11, 2},
469       { 9, 2, 5},
470       { 7, 2,11}
471     };
472 
473   for (uint t=0;t<20;t++)
474     add_triangle(Triangle(tindices[t][2],tindices[t][1],tindices[t][0]));
475 }
476 
TriangleMeshSubdividedIcosahedron(float radius,uint subdivisions,uint flat_subdivisions,uint seed,const XYZ & variation,Progress * progress)477 TriangleMeshSubdividedIcosahedron::TriangleMeshSubdividedIcosahedron(float radius,uint subdivisions,uint flat_subdivisions,uint seed,const XYZ& variation,Progress* progress)
478   :TriangleMesh(progress)
479   ,TriangleMeshIcosahedron(radius,seed,progress)
480 {
481   subdivide(subdivisions,flat_subdivisions,variation);
482 }
483 
484