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}
264void simple_mesh_base::extract_triangle_indices(std::vector<idx3_type>& position_indices, std::vector<idx3_type>* tex_coord_indices, std::vector<idx3_type>* normal_indices, std::vector<idx3_type>* tangent_indices) const {
265 std::vector<idx_type> vertex_indices;
266 std::vector<idx4_type> unique_quadruples;
267
268 bool include_tex_coords = tex_coord_indices != nullptr;
269 bool include_normals = normal_indices != nullptr;
270 bool include_tangents = tangent_indices != nullptr;
271
272 merge_indices(vertex_indices, unique_quadruples, &include_tex_coords, &include_normals, &include_tangents);
273
274 std::vector<idx_type> triangle_element_buffer;
275 extract_triangle_element_buffer(vertex_indices, triangle_element_buffer);
276
277 constexpr size_t position_index = 0;
278 constexpr size_t tex_coord_index = 1;
279 constexpr size_t normal_index = 2;
280 constexpr size_t tangent_index = 3;
281
282 for(size_t i = 0; i < triangle_element_buffer.size(); i += 3) {
283 idx4_type i0 = unique_quadruples[triangle_element_buffer[i + 0]];
284 idx4_type i1 = unique_quadruples[triangle_element_buffer[i + 1]];
285 idx4_type i2 = unique_quadruples[triangle_element_buffer[i + 2]];
286
287 position_indices.emplace_back(i0[position_index], i1[position_index], i2[position_index]);
288
289 if(include_tex_coords)
290 tex_coord_indices->emplace_back(i0[tex_coord_index], i1[tex_coord_index], i2[tex_coord_index]);
291
292 if(include_normals)
293 normal_indices->emplace_back(i0[normal_index], i1[normal_index], i2[normal_index]);
294
295 if(include_tangents)
296 tangent_indices->emplace_back(i0[tangent_index], i1[tangent_index], i2[tangent_index]);
297 }
298}
300 std::vector<idx_type>& inv,
301 bool link_non_manifold_edges,
302 std::vector<idx_type>* p2c_ptr,
303 std::vector<idx_type>* next_ptr,
304 std::vector<idx_type>* prev_ptr,
305 std::vector<idx_type>* unmatched,
306 std::vector<idx_type>* non_manifold,
307 std::vector<idx_type>* unmatched_elements,
308 std::vector<idx_type>* non_manifold_elements) const
309{
310 uint32_t fi, e = 0;
311 if (p2c_ptr)
312 p2c_ptr->resize(get_nr_positions());
313 if (next_ptr)
314 next_ptr->resize(get_nr_corners());
315 if (prev_ptr)
316 prev_ptr->resize(get_nr_corners());
317 inv.resize(get_nr_corners(), uint32_t(-1));
318 std::vector<idx_type> pis(get_nr_corners()), perm0, perm;
319 // extract min position indices per corner
320 //idx_type a = 0;
321 //std::cout << "before sort" << std::endl;
322 for (fi = 0; fi < get_nr_faces(); ++fi) {
323 idx_type cb = begin_corner(fi), ce = end_corner(fi), cp = ce - 1, pp = c2p(cp);
324 for (idx_type ci = cb; ci < ce; ++ci) {
325 idx_type pi = c2p(ci);
326 if (p2c_ptr)
327 p2c_ptr->at(pi) = ci;
328 if (next_ptr)
329 next_ptr->at(cp) = ci;
330 if (prev_ptr)
331 prev_ptr->at(ci) = cp;
332 pis[cp] = std::min(pp, pi);
333 //if (++a < 20)
334 // std::cout << " " << a - 1 << " : " << pp << "," << pi << std::endl;
335 cp = ci;
336 pp = pi;
337 }
338 }
339 // sort corners by min position indices
340 cgv::math::bucket_sort(pis, get_nr_positions(), perm0);
341 //std::cout << "after min sort" << std::endl;
342 //for (a=0; a < 20; ++a)
343 // std::cout << " " << a << " : " << c2p(perm0[a]) << "," << c2p(next_ptr->at(perm0[a])) << " (" << pis[perm0[a]] << ")" << std::endl;
344 // extract max position indices per corner and fill p2c, next and prev vectors
345 for (fi = 0; fi < get_nr_faces(); ++fi) {
346 idx_type cb = begin_corner(fi), ce = end_corner(fi), cp = ce - 1;
347 for (idx_type ci = cb; ci < ce; ++ci) {
348 idx_type pi = c2p(ci);
349 pis[cp] = std::max(c2p(cp), pi);
350 cp = ci;
351 }
352 }
353 // sort corners by max position indices
354 cgv::math::bucket_sort(pis, get_nr_positions(), perm, &perm0);
355 //std::cout << "after max sort" << std::endl;
356 //for (a = 0; a < 20; ++a)
357 // std::cout << " " << a << " : " << c2p(perm[a]) << "," << c2p(next_ptr->at(perm[a])) << " (" << pis[perm[a]] << ")" << std::endl;
358 // store target in pis
359 for (fi = 0; fi < get_nr_faces(); ++fi) {
360 idx_type cb = begin_corner(fi), ce = end_corner(fi), cp = ce - 1;
361 for (idx_type ci = cb; ci < ce; ++ci) {
362 pis[cp] = c2p(ci);
363 cp = ci;
364 }
365 }
366 perm0.clear();
367 // finally perform matching
368 idx_type i = 0;
369 while (i < perm.size()) {
370 idx_type ci = perm[i], pi0 = c2p(ci), pi1 = pis[ci];
371 idx_type cnt = 1;
372 while (i + cnt < perm.size()) {
373 idx_type cj = perm[i + cnt], pj0 = c2p(cj), pj1 = pis[cj];
374 if (std::min(pi0, pi1) == std::min(pj0, pj1) && std::max(pi0, pi1) == std::max(pj0, pj1)) {
375 ++cnt;
376 }
377 else
378 break;
380 if (cnt == 1) {
381 if (unmatched)
382 unmatched->push_back(ci);
383 if (unmatched_elements) {
384 unmatched_elements->push_back(pi0);
385 unmatched_elements->push_back(pi1);
386 }
388 else if (cnt == 2) {
389 inv[perm[i]] = perm[i + 1];
390 inv[perm[i + 1]] = perm[i];
391 ++e;
392 }
393 else {
394 if (link_non_manifold_edges) {
395 idx_type cl = perm[i + cnt - 1];
396 for (idx_type k = 0; k < cnt; ++k) {
397 idx_type ck = perm[i + k];
398 inv[cl] = ck;
399 cl = ck;
401 }
402 if (non_manifold)
403 non_manifold->push_back(ci);
404 if (non_manifold_elements) {
405 non_manifold_elements->push_back(pi0);
406 non_manifold_elements->push_back(pi1);
407 }
408 }
409 i += cnt;
410 }
411 return e;
412 /*
413 std::map<std::pair<uint32_t, uint32_t>, uint32_t> pipj2ci;
414 for (fi = 0; fi < get_nr_faces(); ++fi) {
415 uint32_t prev_ci = end_corner(fi) - 1;
416 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
417 uint32_t pi = c2p(ci);
418 if (p2c_ptr)
419 p2c_ptr->at(pi) = ci;
420 uint32_t next_ci = ci + 1 == end_corner(fi) ? begin_corner(fi) : ci + 1;
421 if (next_ptr)
422 next_ptr->at(ci) = next_ci;
423 if (prev_ptr)
424 prev_ptr->at(ci) = prev_ci;
425 prev_ci = ci;
426 uint32_t pj = c2p(next_ci);
427 std::pair<uint32_t, uint32_t> pipj(std::min(pi, pj), std::max(pi, pj));
428 if (pipj2ci.find(pipj) == pipj2ci.end())
429 pipj2ci[pipj] = ci;
430 else {
431 uint32_t inv_ci = pipj2ci[pipj];
432 inv[ci] = inv_ci;
433 inv[inv_ci] = ci;
434 pipj2ci.erase(pipj2ci.find(pipj));
435 }
436 }
437 }
438 */
439}
440simple_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
441{
442 uint32_t e = 0;
443 c2e.resize(get_nr_corners(), -1);
444 if (e2c_ptr)
445 e2c_ptr->resize(get_nr_corners() / 2);
446 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
447 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
448 if (c2e[ci] == -1) {
449 c2e[ci] = c2e[inv[ci]] = e;
450 if (e2c_ptr)
451 e2c_ptr->at(e) = ci;
452 ++e;
453 }
454 }
455 }
456 return e;
457}
458void simple_mesh_base::compute_c2f(std::vector<uint32_t>& c2f) const
459{
460 c2f.resize(get_nr_corners(), -1);
461 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
462 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
463 c2f[ci] = fi;
464 }
465}
466template <typename T>
467void simple_mesh<T>::construct(const obj_loader_generic<T>& loader, bool copy_grp_info, bool copy_material_info)
468{
469 for (unsigned vi = 0; vi < loader.vertices.size(); ++vi)
470 new_position(loader.vertices[vi]);
471 for (unsigned ni = 0; ni < loader.normals.size(); ++ni)
472 new_normal(loader.normals[ni]);
473 for (unsigned ti = 0; ti < loader.texcoords.size(); ++ti)
474 new_tex_coord(loader.texcoords[ti]);
475 if (copy_grp_info)
476 for (unsigned gi = 0; gi < loader.groups.size(); ++gi)
477 new_group(loader.groups[gi].name);
478 if (copy_material_info)
479 for (unsigned mi = 0; mi < loader.materials.size(); ++mi)
480 ref_material(new_material()) = loader.materials[mi];
481 for (unsigned fi = 0; fi < loader.faces.size(); ++fi) {
482 start_face();
483 const auto& F = loader.faces[fi];
484 if (copy_grp_info && !loader.groups.empty())
485 group_index(fi) = F.group_index;
486 if (copy_material_info && !loader.materials.empty())
487 material_index(fi) = F.material_index;
488 for (unsigned i = 0; i < F.degree; ++i) {
489 new_corner(
490 loader.vertex_indices[F.first_vertex_index + i],
491 F.first_normal_index != -1 ? loader.normal_indices[F.first_normal_index + i] : -1,
492 F.first_texcoord_index != -1 ? loader.texcoord_indices[F.first_texcoord_index + i] : -1
493 );
494 }
495 }
496}
497
498template <typename T>
500{
501public:
503 typedef T coord_type;
510
511 typedef typename simple_mesh<T>::idx_type idx_type;
512protected:
513 simple_mesh<T> &mesh;
514public:
515 simple_mesh_obj_reader(simple_mesh<T>& _mesh) : mesh(_mesh) {}
517 void process_vertex(const vec3_type& p) { mesh.positions.push_back(p); }
519 void process_texcoord(const vec2_type& t) { mesh.tex_coords.push_back(vec2_type(t(0),t(1))); }
521 void process_color(const color_type& c) { mesh.resize_colors(mesh.get_nr_colors() + 1); mesh.set_color(mesh.get_nr_colors()-1, c); }
523 void process_normal(const vec3_type& n) { mesh.normals.push_back(n); }
525 void process_face(unsigned vcount, int *vertices, int *texcoords, int *normals)
526 {
527 obj_reader_base::convert_to_positive(vcount, vertices, texcoords, normals, unsigned(mesh.positions.size()), unsigned(mesh.normals.size()), unsigned(mesh.tex_coords.size()));
528 mesh.faces.push_back(idx_type(mesh.position_indices.size()));
530 mesh.group_indices.push_back(obj_reader_base::get_current_group());
532 mesh.material_indices.push_back(obj_reader_base::get_current_material());
533 if (texcoords) {
534 if (mesh.tex_coord_indices.size() < mesh.position_indices.size())
535 mesh.tex_coord_indices.resize(mesh.position_indices.size(), 0);
536 }
537 if (normals) {
538 if (mesh.normal_indices.size() < mesh.position_indices.size())
539 mesh.normal_indices.resize(mesh.position_indices.size(), 0);
540 }
541 for (idx_type i = 0; i < vcount; ++i) {
542 mesh.position_indices.push_back(idx_type(vertices[i]));
543 if (texcoords)
544 mesh.tex_coord_indices.push_back(idx_type(texcoords[i]));
545 if (normals)
546 mesh.normal_indices.push_back(idx_type(normals[i]));
547 }
548 }
550 void process_group(const std::string& name, const std::string& parameters)
551 {
552 mesh.group_names.push_back(name);
553 }
556 {
557 if (idx >= mesh.materials.size())
558 mesh.materials.resize(idx+1);
559 mesh.materials[idx] = mtl;
560 }
561};
562
563
565template <typename T>
567 : simple_mesh_base(sm), positions(sm.positions), normals(sm.normals), tex_coords(sm.tex_coords)
568{
569}
570
572template <typename T>
574 : simple_mesh_base(std::move(sm)), positions(std::move(sm.positions)), normals(std::move(sm.normals)),
575 tex_coords(std::move(sm.tex_coords))
576{
577}
578
580template <typename T>
582{
584 positions = sm.positions;
585 normals = sm.normals;
586 tangents = sm.tangents;
587 tex_coords = sm.tex_coords;
588 return *this;
589}
590
592template <typename T>
594{
595 simple_mesh_base::operator = (std::move(sm));
596 positions = std::move(sm.positions);
597 normals = std::move(sm.normals);
598 tangents = std::move(sm.tangents);
599 tex_coords = std::move(sm.tex_coords);
600 return *this;
601}
602
604template <typename T>
606{
607 positions.clear();
608 normals.clear();
609 tangents.clear();
610 tex_coords.clear();
611 position_indices.clear();
612 tex_coord_indices.clear();
613 normal_indices.clear();
614 faces.clear();
615 group_indices.clear();
616 group_names.clear();
617 material_indices.clear();
618 materials.clear();
619 destruct_colors();
620}
621
622template <typename T>
623bool read_off(const std::string& file_name,
624 std::vector<cgv::math::fvec<T,3>>& positions, std::vector<cgv::rgba>& vertex_colors,
625 std::vector<std::vector<uint32_t>>& faces, std::vector<cgv::rgba>& face_colors)
626{
627 std::string content;
628 if (!cgv::utils::file::read(file_name, content, true))
629 return false;
630 std::vector<cgv::utils::line> lines;
631 cgv::utils::split_to_lines(content, lines);
632 if (!(lines[0] == "OFF")) {
633 std::cerr << "WARNING: first line in OFF file " << file_name << " does not contain 'OFF'" << std::endl;
634 return false;
635 }
636 unsigned real_li = 1;
637 int v, f, e;
638 for (unsigned li = 1; li < lines.size(); ++li) {
639 if (lines[li].empty())
640 continue;
641 if (lines[li].begin[0] == '#')
642 continue;
643 ++real_li;
644 std::vector<cgv::utils::token> toks;
645 cgv::utils::split_to_tokens(lines[li], toks, "");
646 if (real_li == 2) {
647 if (toks.size() != 3) {
648 std::cerr << "WARNING: second line in OFF file " << file_name << " does provide 3 tokens" << std::endl;
649 return false;
650 }
651 int I[3];
652 for (int i = 0; i < 3; ++i) {
653 if (!cgv::utils::is_integer(toks[i].begin, toks[i].end, I[i])) {
654 std::cerr << "WARNING: token " << i << " on second line in OFF file " << file_name << " is not an integer value" << std::endl;
655 return false;
656 }
657 }
658 v = I[0]; f = I[1]; e = I[2];
659 std::cout << "OFF file " << file_name << " found " << v << " vertices, " << f << " faces, and " << e << " edges." << std::endl;
660 continue;
661 }
662 if (int(real_li) < v+3) {
663 if (!(toks.size() == 3 || toks.size() == 6 || toks.size() == 7)) {
664 std::cerr << "WARNING: line of vertex " << real_li - 3 << " contains " << toks.size() << " tokens instead of 3 or 7." << std::endl;
665 return false;
666 }
667 double x[3];
668 for (unsigned i = 0; i < 3; ++i) {
669 if (!cgv::utils::is_double(toks[i].begin, toks[i].end, x[i])) {
670 std::cerr << "WARNING: line of vertex " << real_li - 3 << " no double in XYZ component " << i << " but <" << toks[i] << ">." << std::endl;
671 return false;
672 }
673 }
674 positions.push_back(cgv::math::fvec<T, 3>(T(x[0]), T(x[1]), T(x[2])));
675 if (toks.size() >= 6) {
676 double c[4] = { 0,0,0,1 };
677 for (unsigned i = 0; i+3 < toks.size(); ++i) {
678 if (!cgv::utils::is_double(toks[i+3].begin, toks[i+3].end, c[i])) {
679 std::cerr << "WARNING: line of vertex " << real_li - 3 << " no double in RGB[A] component " << i << " but <" << toks[i+3] << ">." << std::endl;
680 return false;
681 }
682 }
683 while (vertex_colors.size() + 1 < positions.size())
684 vertex_colors.push_back(vertex_colors.empty() ? cgv::rgba(0.5, 0.5, 0.5, 1.0f) : vertex_colors.back());
685 vertex_colors.push_back(cgv::rgba(float(c[0]), float(c[1]), float(c[2]), float(1.0-c[3])));
686 }
687 }
688 else {
689 int n;
690 if (!cgv::utils::is_integer(toks[0].begin, toks[0].end, n)) {
691 std::cerr << "WARNING: first token on face " << faces.size() << " is not of type integer " << std::endl;
692 return false;
693 }
694 if (!(toks.size() == n + 1 || toks.size() == n + 4 || toks.size() == n + 5)) {
695 std::cerr << "WARNING: line of face " << faces.size() << " contains " << toks.size() << " tokens instead of " << n+1 << " or " << n+5 << std::endl;
696 return false;
697 }
698 faces.push_back({});
699 auto& face = faces.back();
700 for (int i = 0; i<n; ++i) {
701 int pi;
702 if (!cgv::utils::is_integer(toks[i+1].begin, toks[i + 1].end, pi)) {
703 std::cerr << "WARNING: token " << i+1 << " on face " << faces.size()-1 << " is not of type integer " << std::endl;
704 return false;
705 }
706 face.push_back(uint32_t(pi));
707 }
708 if (toks.size() >= n + 4) {
709 double c[4] = { 0,0,0,1 };
710 for (unsigned i = 0; i < toks.size()-n-1; ++i) {
711 if (!cgv::utils::is_double(toks[i + n + 1].begin, toks[i + n + 1].end, c[i])) {
712 std::cerr << "WARNING: line of vertex " << real_li - 3 << " no double in RGBA component " << i << " but <" << toks[i + n + 1] << ">." << std::endl;
713 return false;
714 }
715 }
716 while (face_colors.size()+1 < faces.size())
717 face_colors.push_back(face_colors.empty() ? cgv::rgba(0.5, 0.5, 0.5, 1.0f) : face_colors.back());
718 face_colors.push_back(cgv::rgba(float(c[0]), float(c[1]), float(c[2]), float(1.0 - c[3])));
719 }
720 }
721 }
722 return true;
723}
724
725
727template <typename T>
728bool simple_mesh<T>::read(const std::string& file_name)
729{
730 std::string ext = cgv::utils::to_lower(cgv::utils::file::get_extension(file_name));
731 if (ext == "obj") {
732 simple_mesh_obj_reader<T> reader(*this);
733 return reader.read_obj(file_name);
734 }
735 if (ext == "stl") {
736 try {
737 stl_reader::StlMesh <T, unsigned> mesh(file_name);
738
739 // copy vertices
740 for (size_t vi = 0; vi < mesh.num_vrts(); ++vi)
741 new_position(cgv::math::fvec<T, 3>(3, mesh.vrt_coords(vi)));
742
743 // copy triangles and normals
744 bool has_normals = mesh.raw_normals();
745 for (size_t ti = 0; ti < mesh.num_tris(); ++ti) {
746 if (has_normals)
747 new_normal(cgv::math::fvec<T, 3>(3, mesh.tri_normal(ti)));
748 start_face();
749 for (size_t ci = 0; ci < 3; ++ci)
750 new_corner(mesh.tri_corner_ind(ti, ci), has_normals ? (unsigned)ti : -1);
751 }
752 return true;
753 }
754 catch (std::exception& e) {
755 std::cout << e.what() << std::endl;
756 return false;
757 }
758 }
759 if (ext == "off") {
760 ensure_colors(cgv::media::ColorType::CT_RGBA);
761 auto& vertex_colors = *reinterpret_cast<std::vector<cgv::rgba>*>(ref_color_data_vector_ptr());
762 std::vector<cgv::rgba> face_colors;
763 std::vector<std::vector<idx_type>> faces;
764 if (!read_off(file_name, ref_positions(), vertex_colors, faces, face_colors))
765 return false;
766 for (const auto& f : faces) {
767 start_face();
768 for (auto pi : f)
769 new_corner(pi);
770 }
771 return true;
772 }
773 std::cerr << "unknown mesh file extension '*." << ext << "'" << std::endl;
774 return false;
775}
776
778template <typename T>
779bool simple_mesh<T>::write(const std::string& file_name) const
780{
781 std::ofstream os(file_name);
782 if (os.fail())
783 return false;
784 for (const auto& p : positions)
785 os << "v " << p << std::endl;
786 for (const auto& t : tex_coords)
787 os << "vt " << t << std::endl;
788 for (const auto& n : normals)
789 os << "vn " << n << std::endl;
790
791 bool nmls = position_indices.size() == normal_indices.size();
792 bool tcs = position_indices.size() == tex_coord_indices.size();
793
794 for (idx_type fi = 0; fi < faces.size(); ++fi) {
795 os << "f";
796 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
797 os << " " << position_indices[ci] + 1;
798 if (!nmls && !tcs)
799 continue;
800 os << "/";
801 if (tcs)
802 os << tex_coord_indices[ci] + 1;
803 if (nmls)
804 os << "/" << normal_indices[ci] + 1;
805 }
806 os << "\n";
807 }
808 return true;
809}
810
812template <typename T>
814{
815 box_type box;
816 for (const auto& p : positions)
817 box.add_point(p);
818 return box;
819}
821template <typename T>
822void simple_mesh<T>::compute_vertex_normals(bool use_parallel_implementation)
823{
824 // clear previous normal info
825 if (has_normals())
826 normals.clear();
827 // copy position indices to normals
828 normal_indices = position_indices;
829 // initialize normals to null vectors
830 normals.resize(positions.size(), vec3_type(T(0)));
831 if (use_parallel_implementation) {
832#pragma omp parallel for
833 for (int fi = 0; fi < int(get_nr_faces()); ++fi) {
834 vec3_type nml;
835 if (compute_face_normal(fi, nml))
836 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci)
837 normal(normal_indices[ci]) += nml;
838 }
839#pragma omp parallel for
840 for (int ni = 0; ni < int(normals.size()); ++ni)
841 normals[ni].normalize();
842 }
843 else {
844 vec3_type nml;
845 for (idx_type fi = 0; fi < get_nr_faces(); ++fi)
846 if (compute_face_normal(fi, nml))
847 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci)
848 normal(normal_indices[ci]) += nml;
849 for (auto& n : normals)
850 n.normalize();
851 }
852}
853
854template <typename T>
855unsigned simple_mesh<T>::extract_vertex_attribute_buffer(const std::vector<idx4_type>& unique_quadruples,
856 bool include_tex_coords, bool include_normals,
857 bool include_tangents, std::vector<T>& attrib_buffer,
858 bool* include_colors_ptr, int* num_floats_in_vertex) const
859{
860 // correct inquiry in case data is missing
861 include_tex_coords = include_tex_coords && !tex_coord_indices.empty() && !tex_coords.empty();
862 include_normals = include_normals && !normal_indices.empty() && !normals.empty();
863 include_tangents = include_tangents && !tangent_indices.empty() && !tangents.empty();
864 bool include_colors = false;
865 if (include_colors_ptr)
866 *include_colors_ptr = include_colors = has_colors() && get_nr_colors() > 0 && *include_colors_ptr;
867
868 // determine number floats per vertex
869 unsigned nr_floats = 3;
870 nr_floats += include_tex_coords ? 2 : 0;
871 nr_floats += include_normals ? 3 : 0;
872 nr_floats += include_tangents ? 3 : 0;
873 unsigned color_increment = 0;
874 if (include_colors) {
875 color_increment = (int)ceil((float)get_color_size() / sizeof(T));
876 nr_floats += color_increment;
877 }
878
879 if (num_floats_in_vertex)
880 *num_floats_in_vertex = nr_floats;
881
882 attrib_buffer.resize(nr_floats * unique_quadruples.size());
883 T* data_ptr = &attrib_buffer.front();
884 for (auto t : unique_quadruples) {
885 *reinterpret_cast<vec3_type*>(data_ptr) = positions[t[0]];
886 data_ptr += 3;
887 if (include_tex_coords) {
888 *reinterpret_cast<vec2_type*>(data_ptr) = tex_coords[t[1]];
889 data_ptr += 2;
890 }
891 if (include_normals) {
892 *reinterpret_cast<vec3_type*>(data_ptr) = normals[t[2]];
893 data_ptr += 3;
894 }
895 if (include_tangents) {
896 *reinterpret_cast<vec3_type*>(data_ptr) = tangents[t[3]];
897 data_ptr += 3;
898 }
899 if (include_colors) {
900 put_color(t[0], data_ptr);
901 data_ptr += color_increment;
902 }
903 }
904 return color_increment;
905}
906
907template <typename T> void simple_mesh<T>::transform(const mat3_type& linear_transformation, const vec3_type& translation)
908{
909 mat3_type inverse_linear_transform = cgv::math::inverse(linear_transformation);
910 transform(linear_transformation, translation, inverse_linear_transform);
911}
912template <typename T> void simple_mesh<T>::transform(const mat3_type& linear_transform, const vec3_type& translation, const mat3_type& inverse_linear_transform)
913{
914 for (auto& p : positions)
915 p = linear_transform * p + translation;
916 for (auto& n : normals)
917 n = n * inverse_linear_transform;
918 for(auto& t : tangents)
919 t = t * inverse_linear_transform;
920}
921
923{
924 vec3_type ctr = vec3_type(0.0f);
925 uint32_t nr = 0;
926 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
927 ctr += position(c2p(ci));
928 ++nr;
929 }
930 ctr /= float(nr);
931 return ctr;
932}
933template <typename T> bool simple_mesh<T>::compute_face_normal(idx_type fi, vec3_type& nml_out, bool normalize) const
934{
935 idx_type c0 = begin_corner(fi);
936 idx_type ce = end_corner(fi);
937 vec3_type p0 = position(position_indices[c0]);
938 vec3_type dj = position(position_indices[c0 + 1]) - p0;
939 vec3_type nml(0.0f);
940 for (idx_type ci = c0 + 2; ci < ce; ++ci) {
941 vec3_type di = position(position_indices[ci]) - p0;
942 nml += cross(dj, di);
943 dj = di;
944 }
945 if (!normalize) {
946 nml_out = nml;
947 return true;
948 }
949 T nl = nml.length();
950 if (nl > T(1e-8f)) {
951 nml *= T(1) / nl;
952 nml_out = nml;
953 return true;
954 }
955 return false;
956}
957template <typename T> void simple_mesh<T>::compute_face_normals(bool construct_normal_indices)
958{
959 if (construct_normal_indices)
960 normal_indices.clear();
961 normals.clear();
962 // compute per face normals
963 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
964 vec3_type nml = vec3_type(T(1),0,0);
965 compute_face_normal(fi, nml);
966 uint32_t ni = new_normal(nml);
967 if (construct_normal_indices) {
968 for (idx_type ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
969 normal_indices.push_back(ni);
970 }
971 }
972 }
973}
974template <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)
975{
976 return normalize(cross(p1 - p0, p2 - p0));
977}
978template <typename T> void simple_mesh<T>::compute_face_tangents(bool construct_tangent_indices) {
979 // compute per face tangents
980 if(!has_tex_coords())
981 return;
982
983 for(uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
984 std::vector<vec3_type> _P;
985 std::vector<vec2_type> _T;
986 vec3_type ctr(0.0f);
987 uint32_t ci, nr = 0;
988 for(ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
989 _P.push_back(position(c2p(ci)));
990 _T.push_back(tex_coord(c2t(ci)));
991 ctr += _P.back();
992 ++nr;
993 }
994 vec3_type tng(1.0f, 0.0f, 0.0f);
995 // calculate tangents for faces with at least three corners
996 // for more than 3 corners only use the first two edges and assume the face to be planar
997 if(_P.size() > 2) {
998 vec3_type edge0 = _P[1] - _P[0];
999 vec3_type edge1 = _P[2] - _P[0];
1000 vec2_type delta_uv0 = _T[1] - _T[0];
1001 vec2_type delta_uv1 = _T[2] - _T[0];
1002
1003 float dir_correction = (delta_uv1.x() * delta_uv0.y() - delta_uv1.y() * delta_uv0.x()) < 0.0f ? -1.0f : 1.0f;
1004
1005 // when t1, t2, t3 in same position in UV space, just use default UV direction.
1006 if(delta_uv0.x() * delta_uv1.y() == delta_uv0.y() * delta_uv1.x()) {
1007 delta_uv0.x() = 0.0f;
1008 delta_uv0.y() = 1.0f;
1009 delta_uv1.x() = 1.0f;
1010 delta_uv1.y() = 0.0f;
1011 }
1012
1013 tng.x() = delta_uv0.y() * edge1.x() - delta_uv1.y() * edge0.x();
1014 tng.y() = delta_uv0.y() * edge1.y() - delta_uv1.y() * edge0.y();
1015 tng.z() = delta_uv0.y() * edge1.z() - delta_uv1.y() * edge0.z();
1016 tng *= dir_correction;
1017 } else {
1018 std::cout << "could not compute tangent for non-triangular face" << std::endl;
1019 }
1020
1021 tng.normalize();
1022 uint32_t ti = new_tangent(tng);
1023 if(construct_tangent_indices) {
1024 for(ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
1025 tangent_indices.push_back(ti);
1026 }
1027 }
1028 }
1029}
1030
1031template <typename T> simple_mesh<T>::simple_mesh(const std::string& conway_notation)
1032{
1033 if (!conway_notation.empty())
1034 construct_conway_polyhedron(conway_notation);
1035}
1036template <typename T> void simple_mesh<T>::ambo()
1037{
1038 std::vector<uint32_t> c2e;
1039 std::vector<uint32_t> e2c;
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 e = compute_c2e(inv, c2e, &e2c);
1045 mesh_type new_M;
1046 // create one vertex per edge
1047 for (uint32_t ei = 0; ei < e; ++ei) {
1048 uint32_t pi = c2p(e2c[ei]);
1049 uint32_t pj = c2p(inv[e2c[ei]]);
1050 new_M.new_position(normalize(position(pi) + 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(c2e[ci], new_fi);
1057 }
1058 // create one face for original vertices
1059 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1060 uint32_t c0 = p2c[pi];
1061 uint32_t ci = c0;
1062 uint32_t new_fi = new_M.start_face();
1063 do {
1064 new_M.new_corner(c2e[ci], new_fi);
1065 ci = prev[ci];
1066 ci = inv[ci];
1067 } while (ci != c0);
1068 }
1069 new_M.compute_face_normals();
1070 *this = new_M;
1071}
1072template <typename T> void simple_mesh<T>::truncate(T lambda)
1073{
1074 std::vector<uint32_t> inv;
1075 std::vector<uint32_t> prev;
1076 std::vector<uint32_t> p2c;
1077 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1078 uint32_t c = get_nr_corners();
1079 mesh_type new_M;
1080 // create one vertex per corner
1081 for (uint32_t ci = 0; ci < c; ++ci) {
1082 uint32_t pi = c2p(ci);
1083 uint32_t pj = c2p(inv[ci]);
1084 new_M.new_position(normalize((1 - lambda) * position(pi) + lambda * position(pj)));
1085 }
1086 // create one face for original faces
1087 for (uint32_t fi = 0; fi < get_nr_faces(); ++fi) {
1088 uint32_t new_fi = new_M.start_face();
1089 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
1090 new_M.new_corner(ci, new_fi);
1091 new_M.new_corner(inv[ci], new_fi);
1092 }
1093 }
1094 // create one face for original vertices
1095 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1096 uint32_t c0 = p2c[pi];
1097 uint32_t ci = c0;
1098 uint32_t new_fi = new_M.start_face();
1099 do {
1100 new_M.new_corner(ci, new_fi);
1101 ci = prev[ci];
1102 ci = inv[ci];
1103 } while (ci != c0);
1104 }
1105 new_M.compute_face_normals();
1106 *this = new_M;
1107}
1108template <typename T> void simple_mesh<T>::snub(T lambda)
1109{
1110 std::vector<uint32_t> inv;
1111 std::vector<uint32_t> prev;
1112 std::vector<uint32_t> p2c;
1113 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1114 uint32_t c = get_nr_corners();
1115 mesh_type new_M;
1116 // create one vertex per corner
1117 for (uint32_t ci = 0; ci < c; ++ci) {
1118 uint32_t pi = c2p(ci);
1119 uint32_t pj = c2p(inv[ci]);
1120 new_M.new_position(normalize((1 - lambda) * position(pi) + lambda * position(pj)));
1121 }
1122 // create central face for original faces
1123 uint32_t fi;
1124 for (fi = 0; fi < get_nr_faces(); ++fi) {
1125 uint32_t new_fi = new_M.start_face();
1126 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1127 new_M.new_corner(ci, new_fi);
1128 }
1129 // create one per corner inside original faces
1130 for (fi = 0; fi < get_nr_faces(); ++fi) {
1131 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci) {
1132 uint32_t new_fi = new_M.start_face();
1133 uint32_t prev_ci = prev[ci];
1134 new_M.new_corner(prev_ci, new_fi);
1135 new_M.new_corner(inv[prev_ci], new_fi);
1136 new_M.new_corner(ci, new_fi);
1137 }
1138 }
1139 // create one face for original vertices
1140 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1141 uint32_t c0 = p2c[pi];
1142 uint32_t ci = c0;
1143 uint32_t new_fi = new_M.start_face();
1144 do {
1145 new_M.new_corner(ci, new_fi);
1146 ci = prev[ci];
1147 ci = inv[ci];
1148 } while (ci != c0);
1149 }
1150 new_M.compute_face_normals();
1151 *this = new_M;
1152}
1153template <typename T> void simple_mesh<T>::dual()
1154{
1155 std::vector<uint32_t> c2f;
1156 std::vector<uint32_t> p2c;
1157 std::vector<uint32_t> inv;
1158 std::vector<uint32_t> prev;
1159 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1160 compute_c2f(c2f);
1161 uint32_t f = get_nr_faces();
1162 mesh_type new_M;
1163 // create one vertex per face
1164 for (uint32_t fi = 0; fi < f; ++fi) {
1165 vec3_type ctr = vec3_type(0.0f);
1166 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1167 ctr += position(c2p(ci));
1168 ctr.normalize();
1169 new_M.new_position(ctr);
1170 }
1171 // create one face for original vertices
1172 for (uint32_t pi = 0; pi < get_nr_positions(); ++pi) {
1173 uint32_t c0 = p2c[pi];
1174 uint32_t ci = c0;
1175 uint32_t new_fi = new_M.start_face();
1176 do {
1177 new_M.new_corner(c2f[ci], new_fi);
1178 ci = prev[ci];
1179 ci = inv[ci];
1180 } while (ci != c0);
1181 }
1182 new_M.compute_face_normals();
1183 *this = new_M;
1184}
1185template <typename T> void simple_mesh<T>::gyro(T lambda)
1186{
1187 std::vector<uint32_t> c2f;
1188 std::vector<uint32_t> p2c;
1189 std::vector<uint32_t> inv;
1190 std::vector<uint32_t> prev;
1191 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1192 compute_c2f(c2f);
1193 uint32_t v = get_nr_positions();
1194 uint32_t f = get_nr_faces();
1195 uint32_t c = get_nr_corners();
1196 mesh_type new_M;
1197 // copy vertices
1198 for (uint32_t pi = 0; pi < v; ++pi)
1199 new_M.new_position(position(pi));
1200 // create one vertex per face
1201 for (uint32_t fi = 0; fi < f; ++fi) {
1202 vec3_type ctr = vec3_type(0.0f);
1203 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1204 ctr += position(c2p(ci));
1205 ctr.normalize();
1206 new_M.new_position(ctr);
1207 }
1208 // create one vertex per corner
1209 uint32_t ci;
1210 for (ci = 0; ci < c; ++ci) {
1211 uint32_t pi = c2p(ci);
1212 uint32_t pj = c2p(inv[ci]);
1213 new_M.new_position(normalize((1 - lambda) * position(pi) + lambda * position(pj)));
1214 }
1215 // create one face for per original corner
1216 for (ci = 0; ci < c; ++ci) {
1217 uint32_t inv_ci = inv[ci];
1218 uint32_t prev_inv_ci = inv[prev[ci]];
1219 uint32_t fi = c2f[ci];
1220 uint32_t pi = c2p(ci);
1221 uint32_t new_fi = new_M.start_face();
1222 new_M.new_corner(pi, new_fi);
1223 new_M.new_corner(ci + v + f, new_fi);
1224 new_M.new_corner(inv_ci + v + f, new_fi);
1225 new_M.new_corner(v + fi, new_fi);
1226 new_M.new_corner(prev_inv_ci + v + f, new_fi);
1227 }
1228 new_M.compute_face_normals();
1229 *this = new_M;
1230}
1231template <typename T> void simple_mesh<T>::join()
1232{
1233 std::vector<uint32_t> c2e;
1234 std::vector<uint32_t> e2c;
1235 std::vector<uint32_t> inv;
1236 std::vector<uint32_t> prev;
1237 std::vector<uint32_t> p2c;
1238 compute_inv(inv, /*link_non_manifold_edges*/false, &p2c, 0, &prev);
1239 uint32_t e = compute_c2e(inv, c2e, &e2c);
1240 std::vector<uint32_t> c2f;
1241 compute_c2f(c2f);
1242
1243 uint32_t f = get_nr_faces();
1244 uint32_t v = get_nr_positions();
1245 mesh_type new_M;
1246 // copy vertices
1247 for (uint32_t pi = 0; pi < v; ++pi)
1248 new_M.new_position(position(pi));
1249
1250 // append one vertex per face
1251 for (uint32_t fi = 0; fi < f; ++fi) {
1252 vec3_type ctr = vec3_type(0.0f);
1253 for (uint32_t ci = begin_corner(fi); ci < end_corner(fi); ++ci)
1254 ctr += position(c2p(ci));
1255 ctr.normalize();
1256 new_M.new_position(ctr);
1257 }
1258
1259 // create one face per edge
1260 for (uint32_t ei = 0; ei < e; ++ei) {
1261 uint32_t ci = e2c[ei];
1262 uint32_t fi = c2f[ci];
1263 uint32_t pi = c2p(ci);
1264 uint32_t cj = inv[ci];
1265 uint32_t fj = c2f[cj];
1266 uint32_t pj = c2p(cj);
1267 new_M.start_face();
1268 new_M.new_corner(fi + v, ei);
1269 new_M.new_corner(pi, ei);
1270 new_M.new_corner(fj + v, ei);
1271 new_M.new_corner(pj, ei);
1272 }
1273 new_M.compute_face_normals();
1274 *this = new_M;
1275}
1276template <typename T> void simple_mesh<T>::ortho()
1277{
1278 join();
1279 join();
1280}
1281template <typename T> void simple_mesh<T>::construct_conway_polyhedron(const std::string& conway_notation)
1282{
1283 if (conway_notation.back() == 'C' || conway_notation.back() == 'O') {
1284 // cube
1285 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 };
1286 static float N[6 * 3] = { -1,0,0, +1,0,0, 0,-1,0, 0,+1,0, 0,0,-1, 0,0,+1 };
1287 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 };
1288 for (int vi = 0; vi < 8; ++vi)
1289 new_position(normalize(vec3_type(3, &V[3 * vi])));
1290 for (int ni = 0; ni < 6; ++ni)
1291 new_normal(vec3_type(3, &N[3 * ni]));
1292 for (int fi = 0; fi < 6; ++fi) {
1293 start_face();
1294 for (int ci = 0; ci < 4; ++ci)
1295 new_corner(F[4 * fi + ci], fi);
1296 }
1297 if (conway_notation.back() == 'O')
1298 dual();
1299 }
1300 if (conway_notation.back() == 'T' || conway_notation.back() == 'I' || conway_notation.back() == 'D') {
1301 static const float a = float(1.0 / (2 * sqrt(3.0)));
1302 static const float b = float(1.0 / (3 * sqrt(3.0 / 2)));
1303 static const float V[4 * 3] = { -0.5f, -a, -b, 0.5f, -a, -b, 0,2 * a, -b, 0, 0,2 * b };
1304 static const int F[4 * 3] = { 0,2,1,3,2,0,3,0,1,3,1,2 };
1305 for (int vi = 0; vi < 4; ++vi)
1306 new_position(normalize(vec3_type(3, &V[3 * vi])));
1307 for (int fi = 0; fi < 4; ++fi) {
1308 start_face();
1309 new_normal(compute_normal(position(F[3 * fi]), position(F[3 * fi + 1]), position(F[3 * fi + 2])));
1310 for (int ci = 0; ci < 3; ++ci)
1311 new_corner(F[3 * fi + ci], fi);
1312 }
1313 if (conway_notation.back() != 'T') {
1314 snub();
1315 dual();
1316 if (conway_notation.back() == 'I')
1317 dual();
1318 }
1319 }
1320 for (int ci = (int)conway_notation.size() - 1; ci >= 0; --ci) {
1321 switch (conway_notation[ci]) {
1322 case 't': truncate(); break;
1323 case 'a': ambo(); break;
1324 case 'd': dual(); break;
1325 case 'j': join(); break;
1326 case 'o': ortho(); break;
1327 case 's': snub(); break;
1328 case 'g': gyro(); break;
1329 }
1330 }
1331}
1332
1333template class simple_mesh<float>;
1334template class simple_mesh<double>;
1335
1336 }
1337 }
1338}
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:303
T length() const
length of the vector L2-Norm
Definition fvec.h:267
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_triangle_indices(std::vector< idx3_type > &position_indices, std::vector< idx3_type > *tex_coord_indices=nullptr, std::vector< idx3_type > *normal_indices=nullptr, std::vector< idx3_type > *tangent_indices=nullptr) const
Extract index lists of triangle positions and optional attributes.
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:449
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:508
this header is dependency free
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.