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