cgv
Loading...
Searching...
No Matches
simple_mesh.cxx
1#include "simple_mesh.h"
2#include "stl_reader.h"
3#include "obj_loader.h"
4#include <cgv/utils/scan.h>
6#include <cgv/media/mesh/obj_reader.h>
7#include <cgv/math/bucket_sort.h>
8#include <fstream>
9
10namespace cgv {
11 namespace media {
12 namespace mesh {
13
14std::string simple_mesh_base::get_attribute_name(attribute_type attr)
15{
16 const char* attribute_names[] = { "position", "texcoords", "normal", "tangent", "color" };
17 return attribute_names[int(attr)];
18}
19simple_mesh_base::AttributeFlags simple_mesh_base::get_attribute_flag(attribute_type attr)
20{
21 AttributeFlags attribute_flags[] = { AF_position, AF_texcoords, AF_normal, AF_tangent, AF_color };
22 return attribute_flags[int(attr)];
23}
28 colored_model(smb),
29 position_indices(smb.position_indices),
30 normal_indices(smb.normal_indices),
31 tex_coord_indices(smb.tex_coord_indices),
32 faces(smb.faces),
33 group_indices(smb.group_indices),
34 group_names(smb.group_names),
35 material_indices(smb.material_indices),
36 materials(smb.materials)
37{
38}
40 colored_model(std::move(smb)),
41 position_indices(std::move(smb.position_indices)),
42 normal_indices(std::move(smb.normal_indices)),
43 tex_coord_indices(std::move(smb.tex_coord_indices)),
44 faces(std::move(smb.faces)),
45 group_indices(std::move(smb.group_indices)),
46 group_names(std::move(smb.group_names)),
47 material_indices(std::move(smb.material_indices)),
48 materials(std::move(smb.materials))
49{
50}
52{
54 position_indices=smb.position_indices;
55 normal_indices=smb.normal_indices;
56 tex_coord_indices=smb.tex_coord_indices;
57 faces=smb.faces;
58 group_indices=smb.group_indices;
59 group_names=smb.group_names;
60 material_indices=smb.material_indices;
61 materials = smb.materials;
62 return *this;
63}
65{
66 colored_model::operator=(std::move(smb));
67 position_indices=std::move(smb.position_indices);
68 normal_indices=std::move(smb.normal_indices);
69 tex_coord_indices=std::move(smb.tex_coord_indices);
70 faces=std::move(smb.faces);
71 group_indices=std::move(smb.group_indices);
72 group_names=std::move(smb.group_names);
73 material_indices=std::move(smb.material_indices);
74 materials = std::move(smb.materials);
75 return *this;
76}
78{
79 faces.push_back((cgv::type::uint32_type)position_indices.size());
80 if (!materials.empty())
81 material_indices.push_back(idx_type(materials.size()) - 1);
82 if (!group_names.empty())
83 group_indices.push_back(idx_type(group_names.size()) - 1);
84 return idx_type(faces.size() - 1);
85}
87 idx_type tex_coord_index)
88{
89 position_indices.push_back(position_index);
90 if (normal_index != -1) //FIXME: -1 underflows unsigned int!
91 normal_indices.push_back(normal_index);
92 if (tex_coord_index != -1) //FIXME: -1 underflows unsigned int!
93 tex_coord_indices.push_back(tex_coord_index);
94 return idx_type(position_indices.size());
95}
97{
98 bool nmls = position_indices.size() == normal_indices.size();
99 bool tcs = position_indices.size() == tex_coord_indices.size();
100 for (idx_type fi = 0; fi < get_nr_faces(); ++fi) {
101 idx_type ci = begin_corner(fi);
102 idx_type cj = end_corner(fi);
103 while (ci + 1 < cj) {
104 --cj;
105 std::swap(position_indices[ci], position_indices[cj]);
106 if (nmls)
107 std::swap(normal_indices[ci], normal_indices[cj]);
108 if (tcs)
109 std::swap(tex_coord_indices[ci], tex_coord_indices[cj]);
110 ++ci;
111 }
112 }
113}
114void simple_mesh_base::sort_faces(std::vector<idx_type>& perm, bool by_group, bool by_material) const
115{
116 if (by_group && by_material) {
117 std::vector<idx_type> perm0;
118 cgv::math::bucket_sort(group_indices, get_nr_groups(), perm0);
119 cgv::math::bucket_sort(material_indices, get_nr_materials(), perm, &perm0);
120 }
121 else if (by_group)
122 cgv::math::bucket_sort(group_indices, get_nr_groups(), perm);
123 else
124 cgv::math::bucket_sort(material_indices, get_nr_materials(), perm);
125}
126void simple_mesh_base::merge_indices(std::vector<idx_type>& indices, std::vector<idx4_type>& unique_quadruples, bool* include_tex_coords_ptr, bool* include_normals_ptr, bool* include_tangents_ptr) const
127{
128 bool include_tex_coords = false;
129 if (include_tex_coords_ptr)
130 *include_tex_coords_ptr = include_tex_coords = (tex_coord_indices.size() > 0) && *include_tex_coords_ptr;
131
132 bool include_normals = false;
133 if (include_normals_ptr)
134 *include_normals_ptr = include_normals = (normal_indices.size() > 0) && *include_normals_ptr;
135
136 bool include_tangents = false;
137 if(include_tangents_ptr)
138 *include_tangents_ptr = include_tangents = (tangent_indices.size() > 0) && *include_tangents_ptr;
139
140 std::map<std::tuple<idx_type, idx_type, idx_type, idx_type>, idx_type> corner_to_index;
141 for (idx_type ci = 0; ci < position_indices.size(); ++ci) {
142 // construct corner
143 idx4_type c(position_indices[ci],
144 (include_tex_coords && ci < tex_coord_indices.size()) ? tex_coord_indices[ci] : 0,
145 (include_normals && ci < normal_indices.size()) ? normal_indices[ci] : 0,
146 (include_tangents && ci < tangent_indices.size()) ? tangent_indices[ci] : 0);
147 std::tuple<idx_type, idx_type, idx_type, idx_type> quadruple(c(0),c(1),c(2),c(3));
148 // look corner up in map
149 auto iter = corner_to_index.find(quadruple);
150 // determine vertex index
151 idx_type vi;
152 if (iter == corner_to_index.end()) {
153 vi = idx_type(unique_quadruples.size());
154 corner_to_index[quadruple] = vi;
155 unique_quadruples.push_back(c);
156 }
157 else
158 vi = iter->second;
159
160 indices.push_back(vi);
161 }
162}
164 const std::vector<idx_type>& vertex_indices, std::vector<idx_type>& triangle_element_buffer,
165 const std::vector<idx_type>* face_permutation_ptr, std::vector<idx3_type>* material_group_start_ptr) const
166{
167 idx_type mi = idx_type(-1);
168 idx_type gi = idx_type(-1);
169 // construct triangle element buffer
170 for (idx_type fi = 0; fi < faces.size(); ++fi) {
171 idx_type fj = face_permutation_ptr ? face_permutation_ptr->at(fi) : fi;
172 if (material_group_start_ptr) {
173 if (mi != material_indices[fj] || gi != group_indices[fj]) {
174 mi = material_indices[fj];
175 gi = group_indices[fj];
176 material_group_start_ptr->push_back(idx3_type(mi, gi, idx_type(triangle_element_buffer.size())));
177 }
178 }
179 if (face_degree(fj) == 3) {
180 for (idx_type ci = begin_corner(fj); ci < end_corner(fj); ++ci)
181 triangle_element_buffer.push_back(vertex_indices.at(ci));
182 }
183 else {
184 // in case of non triangular faces do simplest triangulation approach that assumes convexity of faces
185 for (idx_type ci = begin_corner(fj) + 2; ci < end_corner(fj); ++ci) {
186 triangle_element_buffer.push_back(vertex_indices.at(begin_corner(fj)));
187 triangle_element_buffer.push_back(vertex_indices.at(ci - 1));
188 triangle_element_buffer.push_back(vertex_indices.at(ci));
189 }
190 }
191 }
192}
193void simple_mesh_base::extract_wireframe_element_buffer(const std::vector<idx_type>& vertex_indices, std::vector<idx_type>& edge_element_buffer) const
194{
195 // map stores for each halfedge the number of times it has been seen before
196 std::map<std::tuple<idx_type, idx_type>, idx_type> halfedge_to_count;
197 for (idx_type fi = 0; fi < faces.size(); ++fi) {
198 idx_type last_vi = vertex_indices.at(end_corner(fi) - 1);
199 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
200 // construct halfedge with sorted vertex indices
201 idx_type vi = vertex_indices.at(ci);
202 std::tuple<idx_type, idx_type> halfedge(last_vi, vi);
203 if (vi < last_vi)
204 std::swap(std::get<0>(halfedge), std::get<1>(halfedge));
205
206 // lookup corner in map
207 auto iter = halfedge_to_count.find(halfedge);
208
209 // determine vertex index
210 if (iter == halfedge_to_count.end()) {
211 halfedge_to_count[halfedge] = 1;
212 edge_element_buffer.push_back(last_vi);
213 edge_element_buffer.push_back(vi);
214 }
215 else
216 ++halfedge_to_count[halfedge];
217 last_vi = vi;
218 }
219 }
220}
221simple_mesh_base::idx_type simple_mesh_base::extract_vertex_attribute_buffer_base(const std::vector<idx4_type>& unique_quadruples, AttributeFlags& flags, std::vector<uint8_t>& attrib_buffer) const
222{
223 // update flags of to be used attributes
224 if (position_indices.empty() && (flags & AF_position))
225 flags = AttributeFlags(flags & ~AF_position);
226 if (tex_coord_indices.empty() && (flags & AF_texcoords))
227 flags = AttributeFlags(flags & ~AF_texcoords);
228 if (normal_indices.empty() && (flags & AF_normal))
229 flags = AttributeFlags(flags & ~AF_normal);
230 if (tangent_indices.empty() && (flags & AF_tangent))
231 flags = AttributeFlags(flags & ~AF_tangent);
232 if (!has_colors() && (flags & AF_color))
233 flags = AttributeFlags(flags & ~AF_color);
234 bool include_attribute[5] = { bool(flags&AF_position),bool(flags&AF_texcoords),
235 bool(flags&AF_normal),bool(flags&AF_tangent),bool(flags&AF_color) };
236 // determine vertex size in bytes and allocate attribute buffer
237 uint32_t cs = get_coord_size();
238 uint32_t attribute_size[5] = { 3 * cs,2 * cs,3 * cs,3 * cs,uint32_t(get_color_size()) };
239 uint32_t attribute_offset[5] = { 3 * cs,2 * cs,3 * cs,3 * cs,uint32_t(get_color_size() == 3 ? 4 : get_color_size()) };
240 uint32_t vs = 0;
241 for (int ai = 0; ai < 5; ++ai)
242 if (include_attribute[ai])
243 vs += attribute_offset[ai];
244 attrib_buffer.resize(vs * unique_quadruples.size());
245 // fill attribute buffer
246 const uint8_t* attrib_ptrs[5] = {
247 include_attribute[0] ? get_attribute_ptr(attribute_type::position) : nullptr,
248 include_attribute[1] ? get_attribute_ptr(attribute_type::texcoords) : nullptr,
249 include_attribute[2] ? get_attribute_ptr(attribute_type::normal) : nullptr,
250 include_attribute[3] ? get_attribute_ptr(attribute_type::tangent) : nullptr,
251 include_attribute[4] ? get_attribute_ptr(attribute_type::color) : nullptr
252 };
253 size_t loc = 0;
254 for (auto t : unique_quadruples) {
255 for (int ai = 0; ai < 5; ++ai)
256 if (include_attribute[ai]) {
257 const uint8_t* src_ptr = attrib_ptrs[ai] + attribute_size[ai] * t[ai & 3];
258 std::copy(src_ptr, src_ptr + attribute_size[ai], &attrib_buffer[loc]);
259 loc += attribute_offset[ai];
260 }
261 }
262 return vs;
263}
265 std::vector<idx_type>& inv,
266 bool link_non_manifold_edges,
267 std::vector<idx_type>* p2c_ptr,
268 std::vector<idx_type>* next_ptr,
269 std::vector<idx_type>* prev_ptr,
270 std::vector<idx_type>* unmatched,
271 std::vector<idx_type>* non_manifold,
272 std::vector<idx_type>* unmatched_elements,
273 std::vector<idx_type>* non_manifold_elements) const
274{
275 uint32_t fi, e = 0;
276 if (p2c_ptr)
277 p2c_ptr->resize(get_nr_positions());
278 if (next_ptr)
279 next_ptr->resize(get_nr_corners());
280 if (prev_ptr)
281 prev_ptr->resize(get_nr_corners());
282 inv.resize(get_nr_corners(), uint32_t(-1));
283 std::vector<idx_type> pis(get_nr_corners()), perm0, perm;
284 // extract min position indices per corner
285 //idx_type a = 0;
286 //std::cout << "before sort" << std::endl;
287 for (fi = 0; fi < get_nr_faces(); ++fi) {
288 idx_type cb = begin_corner(fi), ce = end_corner(fi), cp = ce - 1, pp = c2p(cp);
289 for (idx_type ci = cb; ci < ce; ++ci) {
290 idx_type pi = c2p(ci);
291 if (p2c_ptr)
292 p2c_ptr->at(pi) = ci;
293 if (next_ptr)
294 next_ptr->at(cp) = ci;
295 if (prev_ptr)
296 prev_ptr->at(ci) = cp;
297 pis[cp] = std::min(pp, pi);
298 //if (++a < 20)
299 // std::cout << " " << a - 1 << " : " << pp << "," << pi << std::endl;
300 cp = ci;
301 pp = pi;
302 }
303 }
304 // sort corners by min position indices
305 cgv::math::bucket_sort(pis, get_nr_positions(), perm0);
306 //std::cout << "after min sort" << std::endl;
307 //for (a=0; a < 20; ++a)
308 // std::cout << " " << a << " : " << c2p(perm0[a]) << "," << c2p(next_ptr->at(perm0[a])) << " (" << pis[perm0[a]] << ")" << std::endl;
309 // extract max position indices per corner and fill p2c, next and prev vectors
310 for (fi = 0; fi < get_nr_faces(); ++fi) {
311 idx_type cb = begin_corner(fi), ce = end_corner(fi), cp = ce - 1;
312 for (idx_type ci = cb; ci < ce; ++ci) {
313 idx_type pi = c2p(ci);
314 pis[cp] = std::max(c2p(cp), pi);
315 cp = ci;
317 }
318 // sort corners by max position indices
319 cgv::math::bucket_sort(pis, get_nr_positions(), perm, &perm0);
320 //std::cout << "after max sort" << std::endl;
321 //for (a = 0; a < 20; ++a)
322 // std::cout << " " << a << " : " << c2p(perm[a]) << "," << c2p(next_ptr->at(perm[a])) << " (" << pis[perm[a]] << ")" << std::endl;
323 // store target in pis
324 for (fi = 0; fi < get_nr_faces(); ++fi) {
325 idx_type cb = begin_corner(fi), ce = end_corner(fi), cp = ce - 1;
326 for (idx_type ci = cb; ci < ce; ++ci) {
327 pis[cp] = c2p(ci);
328 cp = ci;
329 }
330 }
331 perm0.clear();
332 // finally perform matching
333 idx_type i = 0;
334 while (i < perm.size()) {
335 idx_type ci = perm[i], pi0 = c2p(ci), pi1 = pis[ci];
336 idx_type cnt = 1;
337 while (i + cnt < perm.size()) {
338 idx_type cj = perm[i + cnt], pj0 = c2p(cj), pj1 = pis[cj];
339 if (std::min(pi0, pi1) == std::min(pj0, pj1) && std::max(pi0, pi1) == std::max(pj0, pj1)) {
340 ++cnt;
341 }
342 else
343 break;
344 }
345 if (cnt == 1) {
346 if (unmatched)
347 unmatched->push_back(ci);
348 if (unmatched_elements) {
349 unmatched_elements->push_back(pi0);
350 unmatched_elements->push_back(pi1);
351 }
352 }
353 else if (cnt == 2) {
354 inv[perm[i]] = perm[i + 1];
355 inv[perm[i + 1]] = perm[i];
356 ++e;
357 }
358 else {
359 if (link_non_manifold_edges) {
360 idx_type cl = perm[i + cnt - 1];
361 for (idx_type k = 0; k < cnt; ++k) {
362 idx_type ck = perm[i + k];
363 inv[cl] = ck;
364 cl = ck;
366 }
367 if (non_manifold)
368 non_manifold->push_back(ci);
369 if (non_manifold_elements) {
370 non_manifold_elements->push_back(pi0);
371 non_manifold_elements->push_back(pi1);
372 }
374 i += cnt;
376 return e;
377 /*
378 std::map<std::pair<uint32_t, uint32_t>, uint32_t> pipj2ci;
379 for (fi = 0; fi < get_nr_faces(); ++fi) {
380 uint32_t prev_ci = end_corner(fi) - 1;
381 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
382 uint32_t pi = c2p(ci);
383 if (p2c_ptr)
384 p2c_ptr->at(pi) = ci;
385 uint32_t next_ci = ci + 1 == end_corner(fi) ? begin_corner(fi) : ci + 1;
386 if (next_ptr)
387 next_ptr->at(ci) = next_ci;
388 if (prev_ptr)
389 prev_ptr->at(ci) = prev_ci;
390 prev_ci = ci;
391 uint32_t pj = c2p(next_ci);
392 std::pair<uint32_t, uint32_t> pipj(std::min(pi, pj), std::max(pi, pj));
393 if (pipj2ci.find(pipj) == pipj2ci.end())
394 pipj2ci[pipj] = ci;
395 else {
396 uint32_t inv_ci = pipj2ci[pipj];
397 inv[ci] = inv_ci;
398 inv[inv_ci] = ci;
399 pipj2ci.erase(pipj2ci.find(pipj));
400 }
401 }
402 }
403 */
404}
405simple_mesh_base::idx_type simple_mesh_base::compute_c2e(const std::vector<uint32_t>& inv, std::vector<uint32_t>& c2e, std::vector<uint32_t>* e2c_ptr) const
406{
407 uint32_t e = 0;
408 c2e.resize(get_nr_corners(), -1);
409 if (e2c_ptr)
410 e2c_ptr->resize(get_nr_corners() / 2);
411 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
412 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
413 if (c2e[ci] == -1) {
414 c2e[ci] = c2e[inv[ci]] = e;
415 if (e2c_ptr)
416 e2c_ptr->at(e) = ci;
417 ++e;
418 }
419 }
420 }
421 return e;
422}
423void simple_mesh_base::compute_c2f(std::vector<uint32_t>& c2f) const
424{
425 c2f.resize(get_nr_corners(), -1);
426 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
427 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
428 c2f[ci] = fi;
429 }
430}
431template <typename T>
432void simple_mesh<T>::construct(const obj_loader_generic<T>& loader, bool copy_grp_info, bool copy_material_info)
433{
434 for (unsigned vi = 0; vi < loader.vertices.size(); ++vi)
435 new_position(loader.vertices[vi]);
436 for (unsigned ni = 0; ni < loader.normals.size(); ++ni)
437 new_normal(loader.normals[ni]);
438 for (unsigned ti = 0; ti < loader.texcoords.size(); ++ti)
439 new_tex_coord(loader.texcoords[ti]);
440 if (copy_grp_info)
441 for (unsigned gi = 0; gi < loader.groups.size(); ++gi)
442 new_group(loader.groups[gi].name);
443 if (copy_material_info)
444 for (unsigned mi = 0; mi < loader.materials.size(); ++mi)
445 ref_material(new_material()) = loader.materials[mi];
446 for (unsigned fi = 0; fi < loader.faces.size(); ++fi) {
447 start_face();
448 const auto& F = loader.faces[fi];
449 if (copy_grp_info && !loader.groups.empty())
450 group_index(fi) = F.group_index;
451 if (copy_material_info && !loader.materials.empty())
452 material_index(fi) = F.material_index;
453 for (unsigned i = 0; i < F.degree; ++i) {
454 new_corner(
455 loader.vertex_indices[F.first_vertex_index + i],
456 F.first_normal_index != -1 ? loader.normal_indices[F.first_normal_index + i] : -1,
457 F.first_texcoord_index != -1 ? loader.texcoord_indices[F.first_texcoord_index + i] : -1
458 );
459 }
460 }
461}
462
463template <typename T>
465{
466public:
468 typedef T coord_type;
475
476 typedef typename simple_mesh<T>::idx_type idx_type;
477protected:
478 simple_mesh<T> &mesh;
479public:
480 simple_mesh_obj_reader(simple_mesh<T>& _mesh) : mesh(_mesh) {}
482 void process_vertex(const vec3_type& p) { mesh.positions.push_back(p); }
484 void process_texcoord(const vec2_type& t) { mesh.tex_coords.push_back(vec2_type(t(0),t(1))); }
486 void process_color(const color_type& c) { mesh.resize_colors(mesh.get_nr_colors() + 1); mesh.set_color(mesh.get_nr_colors()-1, c); }
488 void process_normal(const vec3_type& n) { mesh.normals.push_back(n); }
490 void process_face(unsigned vcount, int *vertices, int *texcoords, int *normals)
491 {
492 obj_reader_base::convert_to_positive(vcount, vertices, texcoords, normals, unsigned(mesh.positions.size()), unsigned(mesh.normals.size()), unsigned(mesh.tex_coords.size()));
493 mesh.faces.push_back(idx_type(mesh.position_indices.size()));
495 mesh.group_indices.push_back(obj_reader_base::get_current_group());
497 mesh.material_indices.push_back(obj_reader_base::get_current_material());
498 if (texcoords) {
499 if (mesh.tex_coord_indices.size() < mesh.position_indices.size())
500 mesh.tex_coord_indices.resize(mesh.position_indices.size(), 0);
501 }
502 if (normals) {
503 if (mesh.normal_indices.size() < mesh.position_indices.size())
504 mesh.normal_indices.resize(mesh.position_indices.size(), 0);
505 }
506 for (idx_type i = 0; i < vcount; ++i) {
507 mesh.position_indices.push_back(idx_type(vertices[i]));
508 if (texcoords)
509 mesh.tex_coord_indices.push_back(idx_type(texcoords[i]));
510 if (normals)
511 mesh.normal_indices.push_back(idx_type(normals[i]));
512 }
513 }
515 void process_group(const std::string& name, const std::string& parameters)
516 {
517 mesh.group_names.push_back(name);
518 }
521 {
522 if (idx >= mesh.materials.size())
523 mesh.materials.resize(idx+1);
524 mesh.materials[idx] = mtl;
525 }
526};
527
528
530template <typename T>
532 : simple_mesh_base(sm), positions(sm.positions), normals(sm.normals), tex_coords(sm.tex_coords)
533{
534}
535
537template <typename T>
539 : simple_mesh_base(std::move(sm)), positions(std::move(sm.positions)), normals(std::move(sm.normals)),
540 tex_coords(std::move(sm.tex_coords))
541{
542}
543
545template <typename T>
547{
549 positions = sm.positions;
550 normals = sm.normals;
551 tangents = sm.tangents;
552 tex_coords = sm.tex_coords;
553 return *this;
554}
555
557template <typename T>
559{
560 simple_mesh_base::operator = (std::move(sm));
561 positions = std::move(sm.positions);
562 normals = std::move(sm.normals);
563 tangents = std::move(sm.tangents);
564 tex_coords = std::move(sm.tex_coords);
565 return *this;
566}
567
569template <typename T>
571{
572 positions.clear();
573 normals.clear();
574 tangents.clear();
575 tex_coords.clear();
576 position_indices.clear();
577 tex_coord_indices.clear();
578 normal_indices.clear();
579 faces.clear();
580 group_indices.clear();
581 group_names.clear();
582 material_indices.clear();
583 materials.clear();
584 destruct_colors();
585}
586
587template <typename T>
588bool read_off(const std::string& file_name,
589 std::vector<cgv::math::fvec<T,3>>& positions, std::vector<cgv::rgba>& vertex_colors,
590 std::vector<std::vector<uint32_t>>& faces, std::vector<cgv::rgba>& face_colors)
591{
592 std::string content;
593 if (!cgv::utils::file::read(file_name, content, true))
594 return false;
595 std::vector<cgv::utils::line> lines;
596 cgv::utils::split_to_lines(content, lines);
597 if (!(lines[0] == "OFF")) {
598 std::cerr << "WARNING: first line in OFF file " << file_name << " does not contain 'OFF'" << std::endl;
599 return false;
600 }
601 unsigned real_li = 1;
602 int v, f, e;
603 for (unsigned li = 1; li < lines.size(); ++li) {
604 if (lines[li].empty())
605 continue;
606 if (lines[li].begin[0] == '#')
607 continue;
608 ++real_li;
609 std::vector<cgv::utils::token> toks;
610 cgv::utils::split_to_tokens(lines[li], toks, "");
611 if (real_li == 2) {
612 if (toks.size() != 3) {
613 std::cerr << "WARNING: second line in OFF file " << file_name << " does provide 3 tokens" << std::endl;
614 return false;
615 }
616 int I[3];
617 for (int i = 0; i < 3; ++i) {
618 if (!cgv::utils::is_integer(toks[i].begin, toks[i].end, I[i])) {
619 std::cerr << "WARNING: token " << i << " on second line in OFF file " << file_name << " is not an integer value" << std::endl;
620 return false;
621 }
622 }
623 v = I[0]; f = I[1]; e = I[2];
624 std::cout << "OFF file " << file_name << " found " << v << " vertices, " << f << " faces, and " << e << " edges." << std::endl;
625 continue;
626 }
627 if (int(real_li) < v+3) {
628 if (!(toks.size() == 3 || toks.size() == 6 || toks.size() == 7)) {
629 std::cerr << "WARNING: line of vertex " << real_li - 3 << " contains " << toks.size() << " tokens instead of 3 or 7." << std::endl;
630 return false;
631 }
632 double x[3];
633 for (unsigned i = 0; i < 3; ++i) {
634 if (!cgv::utils::is_double(toks[i].begin, toks[i].end, x[i])) {
635 std::cerr << "WARNING: line of vertex " << real_li - 3 << " no double in XYZ component " << i << " but <" << toks[i] << ">." << std::endl;
636 return false;
637 }
638 }
639 positions.push_back(cgv::math::fvec<T, 3>(T(x[0]), T(x[1]), T(x[2])));
640 if (toks.size() >= 6) {
641 double c[4] = { 0,0,0,1 };
642 for (unsigned i = 0; i+3 < toks.size(); ++i) {
643 if (!cgv::utils::is_double(toks[i+3].begin, toks[i+3].end, c[i])) {
644 std::cerr << "WARNING: line of vertex " << real_li - 3 << " no double in RGB[A] component " << i << " but <" << toks[i+3] << ">." << std::endl;
645 return false;
646 }
647 }
648 while (vertex_colors.size() + 1 < positions.size())
649 vertex_colors.push_back(vertex_colors.empty() ? cgv::rgba(0.5, 0.5, 0.5, 1.0f) : vertex_colors.back());
650 vertex_colors.push_back(cgv::rgba(float(c[0]), float(c[1]), float(c[2]), float(1.0-c[3])));
651 }
652 }
653 else {
654 int n;
655 if (!cgv::utils::is_integer(toks[0].begin, toks[0].end, n)) {
656 std::cerr << "WARNING: first token on face " << faces.size() << " is not of type integer " << std::endl;
657 return false;
658 }
659 if (!(toks.size() == n + 1 || toks.size() == n + 4 || toks.size() == n + 5)) {
660 std::cerr << "WARNING: line of face " << faces.size() << " contains " << toks.size() << " tokens instead of " << n+1 << " or " << n+5 << std::endl;
661 return false;
662 }
663 faces.push_back({});
664 auto& face = faces.back();
665 for (int i = 0; i<n; ++i) {
666 int pi;
667 if (!cgv::utils::is_integer(toks[i+1].begin, toks[i + 1].end, pi)) {
668 std::cerr << "WARNING: token " << i+1 << " on face " << faces.size()-1 << " is not of type integer " << std::endl;
669 return false;
670 }
671 face.push_back(uint32_t(pi));
672 }
673 if (toks.size() >= n + 4) {
674 double c[4] = { 0,0,0,1 };
675 for (unsigned i = 0; i < toks.size()-n-1; ++i) {
676 if (!cgv::utils::is_double(toks[i + n + 1].begin, toks[i + n + 1].end, c[i])) {
677 std::cerr << "WARNING: line of vertex " << real_li - 3 << " no double in RGBA component " << i << " but <" << toks[i + n + 1] << ">." << std::endl;
678 return false;
679 }
680 }
681 while (face_colors.size()+1 < faces.size())
682 face_colors.push_back(face_colors.empty() ? cgv::rgba(0.5, 0.5, 0.5, 1.0f) : face_colors.back());
683 face_colors.push_back(cgv::rgba(float(c[0]), float(c[1]), float(c[2]), float(1.0 - c[3])));
684 }
685 }
686 }
687 return true;
688}
689
690
692template <typename T>
693bool simple_mesh<T>::read(const std::string& file_name)
694{
695 std::string ext = cgv::utils::to_lower(cgv::utils::file::get_extension(file_name));
696 if (ext == "obj") {
697 simple_mesh_obj_reader<T> reader(*this);
698 return reader.read_obj(file_name);
699 }
700 if (ext == "stl") {
701 try {
702 stl_reader::StlMesh <T, unsigned> mesh(file_name);
703
704 // copy vertices
705 for (size_t vi = 0; vi < mesh.num_vrts(); ++vi)
706 new_position(cgv::math::fvec<T, 3>(3, mesh.vrt_coords(vi)));
707
708 // copy triangles and normals
709 bool has_normals = mesh.raw_normals();
710 for (size_t ti = 0; ti < mesh.num_tris(); ++ti) {
711 if (has_normals)
712 new_normal(cgv::math::fvec<T, 3>(3, mesh.tri_normal(ti)));
713 start_face();
714 for (size_t ci = 0; ci < 3; ++ci)
715 new_corner(mesh.tri_corner_ind(ti, ci), has_normals ? (unsigned)ti : -1);
716 }
717 return true;
718 }
719 catch (std::exception& e) {
720 std::cout << e.what() << std::endl;
721 return false;
722 }
723 }
724 if (ext == "off") {
725 ensure_colors(cgv::media::ColorType::CT_RGBA);
726 auto& vertex_colors = *reinterpret_cast<std::vector<cgv::rgba>*>(ref_color_data_vector_ptr());
727 std::vector<cgv::rgba> face_colors;
728 std::vector<std::vector<idx_type>> faces;
729 if (!read_off(file_name, ref_positions(), vertex_colors, faces, face_colors))
730 return false;
731 for (const auto& f : faces) {
732 start_face();
733 for (auto pi : f)
734 new_corner(pi);
735 }
736 return true;
737 }
738 std::cerr << "unknown mesh file extension '*." << ext << "'" << std::endl;
739 return false;
740}
741
743template <typename T>
744bool simple_mesh<T>::write(const std::string& file_name) const
745{
746 std::ofstream os(file_name);
747 if (os.fail())
748 return false;
749 for (const auto& p : positions)
750 os << "v " << p << std::endl;
751 for (const auto& t : tex_coords)
752 os << "vt " << t << std::endl;
753 for (const auto& n : normals)
754 os << "vn " << n << std::endl;
755
756 bool nmls = position_indices.size() == normal_indices.size();
757 bool tcs = position_indices.size() == tex_coord_indices.size();
758
759 for (idx_type fi = 0; fi < faces.size(); ++fi) {
760 os << "f";
761 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
762 os << " " << position_indices[ci] + 1;
763 if (!nmls && !tcs)
764 continue;
765 os << "/";
766 if (tcs)
767 os << tex_coord_indices[ci] + 1;
768 if (nmls)
769 os << "/" << normal_indices[ci] + 1;
770 }
771 os << "\n";
772 }
773 return true;
774}
775
777template <typename T>
779{
780 box_type box;
781 for (const auto& p : positions)
782 box.add_point(p);
783 return box;
784}
786template <typename T>
787void simple_mesh<T>::compute_vertex_normals(bool use_parallel_implementation)
788{
789 // clear previous normal info
790 if (has_normals())
791 normals.clear();
792 // copy position indices to normals
793 normal_indices = position_indices;
794 // initialize normals to null vectors
795 normals.resize(positions.size(), vec3_type(T(0)));
796 if (use_parallel_implementation) {
797#pragma omp parallel for
798 for (int fi = 0; fi < int(get_nr_faces()); ++fi) {
799 vec3_type nml;
800 if (compute_face_normal(fi, nml))
801 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci)
802 normal(normal_indices[ci]) += nml;
803 }
804#pragma omp parallel for
805 for (int ni = 0; ni < int(normals.size()); ++ni)
806 normals[ni].normalize();
807 }
808 else {
809 vec3_type nml;
810 for (idx_type fi = 0; fi < get_nr_faces(); ++fi)
811 if (compute_face_normal(fi, nml))
812 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci)
813 normal(normal_indices[ci]) += nml;
814 for (auto& n : normals)
815 n.normalize();
816 }
817}
818
819template <typename T>
820unsigned simple_mesh<T>::extract_vertex_attribute_buffer(const std::vector<idx4_type>& unique_quadruples,
821 bool include_tex_coords, bool include_normals,
822 bool include_tangents, std::vector<T>& attrib_buffer,
823 bool* include_colors_ptr, int* num_floats_in_vertex) const
824{
825 // correct inquiry in case data is missing
826 include_tex_coords = include_tex_coords && !tex_coord_indices.empty() && !tex_coords.empty();
827 include_normals = include_normals && !normal_indices.empty() && !normals.empty();
828 include_tangents = include_tangents && !tangent_indices.empty() && !tangents.empty();
829 bool include_colors = false;
830 if (include_colors_ptr)
831 *include_colors_ptr = include_colors = has_colors() && get_nr_colors() > 0 && *include_colors_ptr;
832
833 // determine number floats per vertex
834 unsigned nr_floats = 3;
835 nr_floats += include_tex_coords ? 2 : 0;
836 nr_floats += include_normals ? 3 : 0;
837 nr_floats += include_tangents ? 3 : 0;
838 unsigned color_increment = 0;
839 if (include_colors) {
840 color_increment = (int)ceil((float)get_color_size() / sizeof(T));
841 nr_floats += color_increment;
842 }
843
844 if (num_floats_in_vertex)
845 *num_floats_in_vertex = nr_floats;
846
847 attrib_buffer.resize(nr_floats * unique_quadruples.size());
848 T* data_ptr = &attrib_buffer.front();
849 for (auto t : unique_quadruples) {
850 *reinterpret_cast<vec3_type*>(data_ptr) = positions[t[0]];
851 data_ptr += 3;
852 if (include_tex_coords) {
853 *reinterpret_cast<vec2_type*>(data_ptr) = tex_coords[t[1]];
854 data_ptr += 2;
855 }
856 if (include_normals) {
857 *reinterpret_cast<vec3_type*>(data_ptr) = normals[t[2]];
858 data_ptr += 3;
859 }
860 if (include_tangents) {
861 *reinterpret_cast<vec3_type*>(data_ptr) = tangents[t[3]];
862 data_ptr += 3;
863 }
864 if (include_colors) {
865 put_color(t[0], data_ptr);
866 data_ptr += color_increment;
867 }
868 }
869 return color_increment;
870}
871
872template <typename T> void simple_mesh<T>::transform(const mat3_type& linear_transformation, const vec3_type& translation)
873{
874 mat3_type inverse_linear_transform = cgv::math::inverse(linear_transformation);
875 transform(linear_transformation, translation, inverse_linear_transform);
876}
877template <typename T> void simple_mesh<T>::transform(const mat3_type& linear_transform, const vec3_type& translation, const mat3_type& inverse_linear_transform)
878{
879 for (auto& p : positions)
880 p = linear_transform * p + translation;
881 for (auto& n : normals)
882 n = n * inverse_linear_transform;
883 for(auto& t : tangents)
884 t = t * inverse_linear_transform;
885}
886
888{
889 vec3_type ctr = vec3_type(0.0f);
890 uint32_t nr = 0;
891 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
892 ctr += position(c2p(ci));
893 ++nr;
894 }
895 ctr /= float(nr);
896 return ctr;
897}
898template <typename T> bool simple_mesh<T>::compute_face_normal(idx_type fi, vec3_type& nml_out, bool normalize) const
899{
900 idx_type c0 = begin_corner(fi);
901 idx_type ce = end_corner(fi);
902 vec3_type p0 = position(position_indices[c0]);
903 vec3_type dj = position(position_indices[c0 + 1]) - p0;
904 vec3_type nml(0.0f);
905 for (idx_type ci = c0 + 2; ci < ce; ++ci) {
906 vec3_type di = position(position_indices[ci]) - p0;
907 nml += cross(dj, di);
908 dj = di;
909 }
910 if (!normalize) {
911 nml_out = nml;
912 return true;
913 }
914 T nl = nml.length();
915 if (nl > T(1e-8f)) {
916 nml *= T(1) / nl;
917 nml_out = nml;
918 return true;
919 }
920 return false;
921}
922template <typename T> void simple_mesh<T>::compute_face_normals(bool construct_normal_indices)
923{
924 if (construct_normal_indices)
925 normal_indices.clear();
926 normals.clear();
927 // compute per face normals
928 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
929 vec3_type nml = vec3_type(T(1),0,0);
930 compute_face_normal(fi, nml);
931 uint32_t ni = new_normal(nml);
932 if (construct_normal_indices) {
933 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
934 normal_indices.push_back(ni);
935 }
936 }
937 }
938}
939template <typename T> typename simple_mesh<T>::vec3_type simple_mesh<T>::compute_normal(const vec3_type& p0, const vec3_type& p1, const vec3_type& p2)
940{
941 return normalize(cross(p1 - p0, p2 - p0));
942}
943template <typename T> void simple_mesh<T>::compute_face_tangents(bool construct_tangent_indices) {
944 // compute per face tangents
945 if(!has_tex_coords())
946 return;
947
948 for(uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
949 std::vector<vec3_type> _P;
950 std::vector<vec2_type> _T;
951 vec3_type ctr(0.0f);
952 uint32_t ci, nr = 0;
953 for(ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
954 _P.push_back(position(c2p(ci)));
955 _T.push_back(tex_coord(c2t(ci)));
956 ctr += _P.back();
957 ++nr;
958 }
959 vec3_type tng(1.0f, 0.0f, 0.0f);
960 // calculate tangents for faces with at least three corners
961 // for more than 3 corners only use the first two edges and assume the face to be planar
962 if(_P.size() > 2) {
963 vec3_type edge0 = _P[1] - _P[0];
964 vec3_type edge1 = _P[2] - _P[0];
965 vec2_type delta_uv0 = _T[1] - _T[0];
966 vec2_type delta_uv1 = _T[2] - _T[0];
967
968 float dir_correction = (delta_uv1.x() * delta_uv0.y() - delta_uv1.y() * delta_uv0.x()) < 0.0f ? -1.0f : 1.0f;
969
970 // when t1, t2, t3 in same position in UV space, just use default UV direction.
971 if(delta_uv0.x() * delta_uv1.y() == delta_uv0.y() * delta_uv1.x()) {
972 delta_uv0.x() = 0.0f;
973 delta_uv0.y() = 1.0f;
974 delta_uv1.x() = 1.0f;
975 delta_uv1.y() = 0.0f;
976 }
977
978 tng.x() = delta_uv0.y() * edge1.x() - delta_uv1.y() * edge0.x();
979 tng.y() = delta_uv0.y() * edge1.y() - delta_uv1.y() * edge0.y();
980 tng.z() = delta_uv0.y() * edge1.z() - delta_uv1.y() * edge0.z();
981 tng *= dir_correction;
982 } else {
983 std::cout << "could not compute tangent for non-triangular face" << std::endl;
984 }
985
986 tng.normalize();
987 uint32_t ti = new_tangent(tng);
988 if(construct_tangent_indices) {
989 for(ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
990 tangent_indices.push_back(ti);
991 }
992 }
993 }
994}
995
996template <typename T> simple_mesh<T>::simple_mesh(const std::string& conway_notation)
997{
998 if (!conway_notation.empty())
999 construct_conway_polyhedron(conway_notation);
1000}
1001template <typename T> void simple_mesh<T>::ambo()
1002{
1003 std::vector<uint32_t> c2e;
1004 std::vector<uint32_t> e2c;
1005 std::vector<uint32_t> inv;
1006 std::vector<uint32_t> prev;
1007 std::vector<uint32_t> p2c;
1008 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1009 uint32_t e = compute_c2e(inv, c2e, &e2c);
1010 mesh_type new_M;
1011 // create one vertex per edge
1012 for (uint32_t ei = 0; ei < e; ++ei) {
1013 uint32_t pi = c2p(e2c[ei]);
1014 uint32_t pj = c2p(inv[e2c[ei]]);
1015 new_M.new_position(normalize(position(pi) + position(pj)));
1016 }
1017 // create one face for original faces
1018 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
1019 uint32_t new_fi = new_M.start_face();
1020 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1021 new_M.new_corner(c2e[ci], new_fi);
1022 }
1023 // create one face for original vertices
1024 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1025 uint32_t c0 = p2c[pi];
1026 uint32_t ci = c0;
1027 uint32_t new_fi = new_M.start_face();
1028 do {
1029 new_M.new_corner(c2e[ci], new_fi);
1030 ci = prev[ci];
1031 ci = inv[ci];
1032 } while (ci != c0);
1033 }
1034 new_M.compute_face_normals();
1035 *this = new_M;
1036}
1037template <typename T> void simple_mesh<T>::truncate(T lambda)
1038{
1039 std::vector<uint32_t> inv;
1040 std::vector<uint32_t> prev;
1041 std::vector<uint32_t> p2c;
1042 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1043 uint32_t c = get_nr_corners();
1044 mesh_type new_M;
1045 // create one vertex per corner
1046 for (uint32_t ci = 0; ci < c; ++ci) {
1047 uint32_t pi = c2p(ci);
1048 uint32_t pj = c2p(inv[ci]);
1049 new_M.new_position(normalize((1 - lambda) * position(pi) + lambda * position(pj)));
1050 }
1051 // create one face for original faces
1052 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
1053 uint32_t new_fi = new_M.start_face();
1054 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
1055 new_M.new_corner(ci, new_fi);
1056 new_M.new_corner(inv[ci], new_fi);
1057 }
1058 }
1059 // create one face for original vertices
1060 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1061 uint32_t c0 = p2c[pi];
1062 uint32_t ci = c0;
1063 uint32_t new_fi = new_M.start_face();
1064 do {
1065 new_M.new_corner(ci, new_fi);
1066 ci = prev[ci];
1067 ci = inv[ci];
1068 } while (ci != c0);
1069 }
1070 new_M.compute_face_normals();
1071 *this = new_M;
1072}
1073template <typename T> void simple_mesh<T>::snub(T lambda)
1074{
1075 std::vector<uint32_t> inv;
1076 std::vector<uint32_t> prev;
1077 std::vector<uint32_t> p2c;
1078 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1079 uint32_t c = get_nr_corners();
1080 mesh_type new_M;
1081 // create one vertex per corner
1082 for (uint32_t ci = 0; ci < c; ++ci) {
1083 uint32_t pi = c2p(ci);
1084 uint32_t pj = c2p(inv[ci]);
1085 new_M.new_position(normalize((1 - lambda) * position(pi) + lambda * position(pj)));
1086 }
1087 // create central face for original faces
1088 uint32_t fi;
1089 for (fi = 0; fi < get_nr_faces(); ++fi) {
1090 uint32_t new_fi = new_M.start_face();
1091 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1092 new_M.new_corner(ci, new_fi);
1093 }
1094 // create one per corner inside original faces
1095 for (fi = 0; fi < get_nr_faces(); ++fi) {
1096 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
1097 uint32_t new_fi = new_M.start_face();
1098 uint32_t prev_ci = prev[ci];
1099 new_M.new_corner(prev_ci, new_fi);
1100 new_M.new_corner(inv[prev_ci], new_fi);
1101 new_M.new_corner(ci, new_fi);
1102 }
1103 }
1104 // create one face for original vertices
1105 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1106 uint32_t c0 = p2c[pi];
1107 uint32_t ci = c0;
1108 uint32_t new_fi = new_M.start_face();
1109 do {
1110 new_M.new_corner(ci, new_fi);
1111 ci = prev[ci];
1112 ci = inv[ci];
1113 } while (ci != c0);
1114 }
1115 new_M.compute_face_normals();
1116 *this = new_M;
1117}
1118template <typename T> void simple_mesh<T>::dual()
1119{
1120 std::vector<uint32_t> c2f;
1121 std::vector<uint32_t> p2c;
1122 std::vector<uint32_t> inv;
1123 std::vector<uint32_t> prev;
1124 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1125 compute_c2f(c2f);
1126 uint32_t f = get_nr_faces();
1127 mesh_type new_M;
1128 // create one vertex per face
1129 for (uint32_t fi = 0; fi < f; ++fi) {
1130 vec3_type ctr = vec3_type(0.0f);
1131 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1132 ctr += position(c2p(ci));
1133 ctr.normalize();
1134 new_M.new_position(ctr);
1135 }
1136 // create one face for original vertices
1137 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1138 uint32_t c0 = p2c[pi];
1139 uint32_t ci = c0;
1140 uint32_t new_fi = new_M.start_face();
1141 do {
1142 new_M.new_corner(c2f[ci], new_fi);
1143 ci = prev[ci];
1144 ci = inv[ci];
1145 } while (ci != c0);
1146 }
1147 new_M.compute_face_normals();
1148 *this = new_M;
1149}
1150template <typename T> void simple_mesh<T>::gyro(T lambda)
1151{
1152 std::vector<uint32_t> c2f;
1153 std::vector<uint32_t> p2c;
1154 std::vector<uint32_t> inv;
1155 std::vector<uint32_t> prev;
1156 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1157 compute_c2f(c2f);
1158 uint32_t v = get_nr_positions();
1159 uint32_t f = get_nr_faces();
1160 uint32_t c = get_nr_corners();
1161 mesh_type new_M;
1162 // copy vertices
1163 for (uint32_t pi = 0; pi < v; ++pi)
1164 new_M.new_position(position(pi));
1165 // create one vertex per face
1166 for (uint32_t fi = 0; fi < f; ++fi) {
1167 vec3_type ctr = vec3_type(0.0f);
1168 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1169 ctr += position(c2p(ci));
1170 ctr.normalize();
1171 new_M.new_position(ctr);
1172 }
1173 // create one vertex per corner
1174 uint32_t ci;
1175 for (ci = 0; ci < c; ++ci) {
1176 uint32_t pi = c2p(ci);
1177 uint32_t pj = c2p(inv[ci]);
1178 new_M.new_position(normalize((1 - lambda) * position(pi) + lambda * position(pj)));
1179 }
1180 // create one face for per original corner
1181 for (ci = 0; ci < c; ++ci) {
1182 uint32_t inv_ci = inv[ci];
1183 uint32_t prev_inv_ci = inv[prev[ci]];
1184 uint32_t fi = c2f[ci];
1185 uint32_t pi = c2p(ci);
1186 uint32_t new_fi = new_M.start_face();
1187 new_M.new_corner(pi, new_fi);
1188 new_M.new_corner(ci + v + f, new_fi);
1189 new_M.new_corner(inv_ci + v + f, new_fi);
1190 new_M.new_corner(v + fi, new_fi);
1191 new_M.new_corner(prev_inv_ci + v + f, new_fi);
1192 }
1193 new_M.compute_face_normals();
1194 *this = new_M;
1195}
1196template <typename T> void simple_mesh<T>::join()
1197{
1198 std::vector<uint32_t> c2e;
1199 std::vector<uint32_t> e2c;
1200 std::vector<uint32_t> inv;
1201 std::vector<uint32_t> prev;
1202 std::vector<uint32_t> p2c;
1203 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1204 uint32_t e = compute_c2e(inv, c2e, &e2c);
1205 std::vector<uint32_t> c2f;
1206 compute_c2f(c2f);
1207
1208 uint32_t f = get_nr_faces();
1209 uint32_t v = get_nr_positions();
1210 mesh_type new_M;
1211 // copy vertices
1212 for (uint32_t pi = 0; pi < v; ++pi)
1213 new_M.new_position(position(pi));
1214
1215 // append one vertex per face
1216 for (uint32_t fi = 0; fi < f; ++fi) {
1217 vec3_type ctr = vec3_type(0.0f);
1218 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1219 ctr += position(c2p(ci));
1220 ctr.normalize();
1221 new_M.new_position(ctr);
1222 }
1223
1224 // create one face per edge
1225 for (uint32_t ei = 0; ei < e; ++ei) {
1226 uint32_t ci = e2c[ei];
1227 uint32_t fi = c2f[ci];
1228 uint32_t pi = c2p(ci);
1229 uint32_t cj = inv[ci];
1230 uint32_t fj = c2f[cj];
1231 uint32_t pj = c2p(cj);
1232 new_M.start_face();
1233 new_M.new_corner(fi + v, ei);
1234 new_M.new_corner(pi, ei);
1235 new_M.new_corner(fj + v, ei);
1236 new_M.new_corner(pj, ei);
1237 }
1238 new_M.compute_face_normals();
1239 *this = new_M;
1240}
1241template <typename T> void simple_mesh<T>::ortho()
1242{
1243 join();
1244 join();
1245}
1246template <typename T> void simple_mesh<T>::construct_conway_polyhedron(const std::string& conway_notation)
1247{
1248 if (conway_notation.back() == 'C' || conway_notation.back() == 'O') {
1249 // cube
1250 static float V[8 * 3] = { -1,-1,+1, +1,-1,+1, -1,+1,+1, +1,+1,+1, -1,-1,-1, +1,-1,-1, -1,+1,-1, +1,+1,-1 };
1251 static float N[6 * 3] = { -1,0,0, +1,0,0, 0,-1,0, 0,+1,0, 0,0,-1, 0,0,+1 };
1252 static int F[6 * 4] = { 0,2,6,4, 1,5,7,3, 0,4,5,1, 2,3,7,6, 4,6,7,5, 0,1,3,2 };
1253 for (int vi = 0; vi < 8; ++vi)
1254 new_position(normalize(vec3_type(3, &V[3 * vi])));
1255 for (int ni = 0; ni < 6; ++ni)
1256 new_normal(vec3_type(3, &N[3 * ni]));
1257 for (int fi = 0; fi < 6; ++fi) {
1258 start_face();
1259 for (int ci = 0; ci < 4; ++ci)
1260 new_corner(F[4 * fi + ci], fi);
1261 }
1262 if (conway_notation.back() == 'O')
1263 dual();
1264 }
1265 if (conway_notation.back() == 'T' || conway_notation.back() == 'I' || conway_notation.back() == 'D') {
1266 static const float a = float(1.0 / (2 * sqrt(3.0)));
1267 static const float b = float(1.0 / (3 * sqrt(3.0 / 2)));
1268 static const float V[4 * 3] = { -0.5f, -a, -b, 0.5f, -a, -b, 0,2 * a, -b, 0, 0,2 * b };
1269 static const int F[4 * 3] = { 0,2,1,3,2,0,3,0,1,3,1,2 };
1270 for (int vi = 0; vi < 4; ++vi)
1271 new_position(normalize(vec3_type(3, &V[3 * vi])));
1272 for (int fi = 0; fi < 4; ++fi) {
1273 start_face();
1274 new_normal(compute_normal(position(F[3 * fi]), position(F[3 * fi + 1]), position(F[3 * fi + 2])));
1275 for (int ci = 0; ci < 3; ++ci)
1276 new_corner(F[3 * fi + ci], fi);
1277 }
1278 if (conway_notation.back() != 'T') {
1279 snub();
1280 dual();
1281 if (conway_notation.back() == 'I')
1282 dual();
1283 }
1284 }
1285 for (int ci = (int)conway_notation.size() - 1; ci >= 0; --ci) {
1286 switch (conway_notation[ci]) {
1287 case 't': truncate(); break;
1288 case 'a': ambo(); break;
1289 case 'd': dual(); break;
1290 case 'j': join(); break;
1291 case 'o': ortho(); break;
1292 case 's': snub(); break;
1293 case 'g': gyro(); break;
1294 }
1295 }
1296}
1297
1298template class simple_mesh<float>;
1299template class simple_mesh<double>;
1300
1301 }
1302 }
1303}
More advanced text processing for splitting text into lines or tokens.
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
matrix of fixed size dimensions
Definition fmat.h:23
A vector with zero based index.
Definition fvec.h:29
T & z()
return third component
Definition fvec.h:144
T & y()
return second component
Definition fvec.h:140
T normalize()
normalize the vector using the L2-Norm and return the length
Definition fvec.h:293
T length() const
length of the vector L2-Norm
Definition fvec.h:257
T & x()
return first component
Definition fvec.h:136
An axis aligned box, defined by to points: min and max.
void add_point(const fpnt_type &p)
extent box to include given point
coordinate type independent base class of simple mesh data structure that handles indices and colors.
colored_model & operator=(const colored_model &cm)
assignment operator
void set_color(size_t i, const void *col_ptr)
set i-th color to color of type stored in storage
size_t get_color_size() const
return the size of one allocated color in byte
bool has_colors() const
check whether colors have been allocated
size_t get_nr_colors() const
return number of allocated colors
void resize_colors(size_t nr_colors)
resize the color storage to given number of colors
implements the virtual interface of the obj_reader and stores all read information.
Definition obj_loader.h:65
virtual bool read_obj(const std::string &file_name)
read an obj file
unsigned get_current_group() const
return the index of the currently selected group or -1 if no group is defined
unsigned get_current_material() const
return the index of the currently selected material or -1 if no material is defined
void convert_to_positive(unsigned vcount, int *vertices, int *texcoords, int *normals, unsigned v, unsigned n, unsigned t)
convert negative indices to positive ones by adding the number of elements
implements the pure reading of an obj file and calls virtual callback functions to allow a derived cl...
Definition obj_reader.h:98
coordinate type independent base class of simple mesh data structure that handles indices and colors.
Definition simple_mesh.h:27
void compute_c2f(std::vector< idx_type > &c2f) const
compute index vector with per corner its face index
cgv::type::uint32_type idx_type
define index type
Definition simple_mesh.h:30
idx_type end_corner(idx_type fi) const
Retrieve index of the vertex which follows the end corner of a face.
idx_type extract_vertex_attribute_buffer_base(const std::vector< idx4_type > &unique_quadruples, AttributeFlags &flags, std::vector< uint8_t > &attrib_buffer) const
extract vertex attribute buffer for the given flags and return size of vertex in bytes
simple_mesh_base()
default constructor
simple_mesh_base & operator=(const simple_mesh_base &smb)
assignment operator
@ AF_normal
texture coordinates
Definition simple_mesh.h:47
@ AF_color
tangent vectors for u coordinate
Definition simple_mesh.h:49
size_t get_nr_groups() const
return number of face groups
idx_type compute_inv(std::vector< idx_type > &inv, bool link_non_manifold_edges=false, std::vector< idx_type > *p2c_ptr=0, std::vector< idx_type > *next_ptr=0, std::vector< idx_type > *prev_ptr=0, std::vector< idx_type > *unmatched=0, std::vector< idx_type > *non_manifold=0, std::vector< idx_type > *unmatched_elements=0, std::vector< idx_type > *non_manifold_elements=0) const
Do inverse matching of half-edges.
idx_type get_nr_corners() const
return the number of corners
idx_type start_face()
Create a new empty face to which new corners are added.
idx_type compute_c2e(const std::vector< idx_type > &inv, std::vector< idx_type > &c2e, std::vector< idx_type > *e2c_ptr=0) const
given the inv corners compute vector storing per corner the edge index and optionally per edge one co...
void merge_indices(std::vector< idx_type > &vertex_indices, std::vector< idx4_type > &unique_tuples, bool *include_tex_coords_ptr=0, bool *include_normals_ptr=0, bool *include_tangents_ptr=0) const
Transforms n individual vertex attribute indices into one list of unique index n-tuples.
void extract_wireframe_element_buffer(const std::vector< idx_type > &vertex_indices, std::vector< idx_type > &edge_element_buffer) const
Extract element array buffers for edges in wireframe.
idx_type c2p(idx_type ci) const
return position index of corner
Definition simple_mesh.h:98
size_t get_nr_materials() const
return number of materials in mesh
virtual idx_type get_nr_positions() const =0
position count
virtual uint32_t get_coord_size() const =0
return the size of one coordinate in bytes
void sort_faces(std::vector< idx_type > &perm, bool by_group=true, bool by_material=true) const
Calculate a permutation of face indices which sorts them by group and/or material.
idx_type new_corner(idx_type position_index, idx_type normal_index=-1, idx_type tex_coord_index=-1)
Create a new corner with the given attributes.
idx_type get_nr_faces() const
return the number of faces
idx_type begin_corner(idx_type fi) const
Retrieve the vertex index of the first corner of a face.
void revert_face_orientation()
revert face orientation
void extract_triangle_element_buffer(const std::vector< idx_type > &vertex_indices, std::vector< idx_type > &triangle_element_buffer, const std::vector< idx_type > *face_permutation_ptr=0, std::vector< idx3_type > *material_group_start_ptr=0) const
Extract element array buffers for triangulation.
idx_type face_degree(idx_type fi) const
return number of edges/corners of face with index fi
cgv::math::fvec< idx_type, 3 > idx3_type
define index triple type
Definition simple_mesh.h:34
void process_group(const std::string &name, const std::string &parameters)
overide this function to process a group given by name and parameter string
cgv::math::fvec< T, 3 > vec3_type
type used to store positions and normal vectors
cgv::math::fvec< T, 2 > vec2_type
type used to store texture coordinates
void process_face(unsigned vcount, int *vertices, int *texcoords, int *normals)
overide this function to process a face, the indices start with 0
void process_vertex(const vec3_type &p)
overide this function to process a vertex
void process_color(const color_type &c)
overide this function to process a color (this called for vc prefixes which is is not in the standard...
void process_texcoord(const vec2_type &t)
overide this function to process a texcoord
void process_material(const cgv::media::illum::obj_material &mtl, unsigned idx)
process a material definition. If a material with a certain name is overwritten, it will receive the ...
void process_normal(const vec3_type &n)
overide this function to process a normal
illum::obj_material::color_type color_type
type used for rgba colors
the simple_mesh class is templated over the coordinate type that defaults to float
void compute_face_normals(bool construct_normal_indices=true)
compute per face normals (ensure that per corner normal indices are set correspondingly)
unsigned extract_vertex_attribute_buffer(const std::vector< idx4_type > &unique_quadruples, bool include_tex_coords, bool include_normals, bool include_tangents, std::vector< T > &attrib_buffer, bool *include_colors_ptr=0, int *num_floats_in_vertex=nullptr) const
Extract vertex attribute array and element array buffers for triangulation and edges in wireframe.
void compute_vertex_normals(bool use_parallel_implementation=true)
compute vertex normals by averaging triangle normals
void transform(const mat3_type &linear_transformation, const vec3_type &translation)
apply transformation to mesh
void construct(const obj_loader_generic< T > &loader, bool copy_grp_info, bool copy_material_info)
construct from obj loader
void ambo()
Conway ambo operator.
void ortho()
Conway ortho operator.
void truncate(T lambda=0.33333f)
Conway truncate operator.
void compute_face_tangents(bool construct_tangent_indices=true)
compute per face tangents (ensure that per corner tangent indices are set correspondingly)
box_type compute_box() const
compute the axis aligned bounding box
vec3_type compute_face_center(idx_type fi) const
compute face center
bool read(const std::string &file_name)
read simple mesh from file (currently only obj and stl are supported)
void construct_conway_polyhedron(const std::string &conway_notation)
construct new mesh according to Conway polyhedron notation: [a|t|s|d|g|j|o]*[T|C|O|D|I] which is eval...
void gyro(T lambda=0.3333f)
Conway gyro operator.
void clear()
clear simple mesh
void snub(T lambda=0.33333f)
Conway snub operator.
void join()
Conway join operator.
bool write(const std::string &file_name) const
write simple mesh to file (currently only obj is supported)
idx_type new_position(const vec3_type &p)
add a new position and return position index
simple_mesh< T > & operator=(const simple_mesh< T > &sm)
assignment operator
bool compute_face_normal(idx_type fi, vec3_type &nml, bool normalize=true) const
compute the normal nml of a face and return whether this was possible
simple_mesh(const simple_mesh< T > &sm)
copy constructor
void dual()
Conway dual operator.
const TNumber * vrt_coords(const size_t vi) const
returns an array of 3 floating point values, one for each coordinate of the vertex
Definition stl_reader.h:297
size_t num_tris() const
returns the number of triangles in the mesh
Definition stl_reader.h:303
const TNumber * raw_normals() const
returns a pointer to the normal array, containing num_tris()*3 entries.
Definition stl_reader.h:376
const TIndex tri_corner_ind(const size_t ti, const size_t ci) const
returns the index of the corner with index 0<=ci<3 of triangle ti
Definition stl_reader.h:315
size_t num_vrts() const
returns the number of vertices in the mesh
Definition stl_reader.h:291
const TNumber * tri_normal(const size_t ti) const
returns an array of 3 floating point values defining the normal of a tri
Definition stl_reader.h:333
unsigned int uint32_type
this type provides an 32 bit unsigned integer type
void split_to_tokens(const char *begin, const char *end, std::vector< token > &tokens, const std::string &separators, bool merge_separators, const std::string &open_parenthesis, const std::string &close_parenthesis, const std::string &whitespaces, unsigned int max_nr_tokens)
this function splits a text range into tokens.
bool is_integer(const char *begin, const char *end, int &value)
check if the text range (begin,end( defines an integer value. If yes, store the value in the passed r...
Definition scan.cxx:367
void split_to_lines(const char *global_begin, const char *global_end, std::vector< line > &lines, bool truncate_trailing_spaces)
this function splits a text range at the newline characters into single lines.
char to_lower(char c)
convert char to lower case
Definition scan.cxx:39
bool is_double(const char *begin, const char *end, double &value)
check if the text range (begin,end( defines a double value. If yes, store the value in the passed ref...
Definition scan.cxx:426
the cgv namespace
Definition print.h:11
Helper functions to process strings.
Provides functions to read stl files into user provided arrays.
Extension of a phong material with support for texture-mapped color channels.