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