ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
cc2sm.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <Utils/cc2sm.h>
9 
10 // Local
12 #include <Utils/PCLConv.h>
13 #include <Utils/my_point_types.h>
14 
15 // PCL
16 #include <pcl/common/io.h>
17 #include <pcl/io/vtk_io.h>
18 #include <pcl/io/vtk_lib_io.h>
19 
20 #include <pcl/io/impl/vtk_lib_io.hpp>
21 
22 // VTK
23 #include <vtkCellArray.h>
24 #include <vtkFloatArray.h>
25 #include <vtkMatrix4x4.h>
26 #include <vtkPoints.h>
27 #include <vtkPolyData.h>
28 #include <vtkStringArray.h>
29 #include <vtkUnsignedCharArray.h>
30 
31 // Support for VTK 7.1 upwards
32 #ifdef vtkGenericDataArray_h
33 #define SetTupleValue SetTypedTuple
34 #define InsertNextTupleValue InsertNextTypedTuple
35 #endif
36 
37 // CV_CORE_LIB
38 #include <CVTools.h>
39 
40 // CV_DB_LIB
41 #include <ecvAdvancedTypes.h>
42 #include <ecvHObjectCaster.h>
43 #include <ecvMaterialSet.h>
44 #include <ecvMesh.h>
45 #include <ecvPointCloud.h>
46 #include <ecvPolyline.h>
47 #include <ecvScalarField.h>
48 
49 // system
50 #include <assert.h>
51 
52 using namespace pcl;
53 
54 cc2smReader::cc2smReader(bool showMode /* = false*/)
55  : m_cc_cloud(nullptr),
56  m_showMode(showMode),
57  m_partialVisibility(false),
58  m_visibilityNum(0) {}
59 
61  bool showMode /* = false*/)
62  : m_cc_cloud(cc_cloud),
63  m_showMode(showMode),
64  m_partialVisibility(false),
65  m_visibilityNum(0) {
66  assert(m_cc_cloud);
67  // count the number of visible points
69  m_visibilityNum = 0;
70  assert(m_cc_cloud->getTheVisibilityArray().size() ==
71  m_cc_cloud->size());
72  m_partialVisibility = true;
73  unsigned count = m_cc_cloud->size();
74  for (unsigned i = 0; i < count; ++i) {
77  }
78  }
79  }
80 }
81 
82 PCLCloud::Ptr cc2smReader::getGenericField(std::string field_name) const {
83  PCLCloud::Ptr sm_cloud;
84 
85  if (field_name == "x") {
86  sm_cloud = getOneOf(COORD_X);
87  } else if (field_name == "y") {
88  sm_cloud = getOneOf(COORD_Y);
89  } else if (field_name == "z") {
90  sm_cloud = getOneOf(COORD_Y);
91  } else if (field_name == "normal_x") {
92  sm_cloud = getOneOf(NORM_X);
93  } else if (field_name == "normal_y") {
94  sm_cloud = getOneOf(NORM_Y);
95  } else if (field_name == "normal_z") {
96  sm_cloud = getOneOf(NORM_Z);
97  } else if (field_name == "xyz") {
98  sm_cloud = getXYZ();
99  } else if (field_name == "normal_xyz") {
100  sm_cloud = getNormals();
101  } else if (field_name == "rgb") {
102  sm_cloud = getColors();
103  } else // try to load the field from the scalar fields
104  {
105  sm_cloud = getFloatScalarField(field_name);
106  }
107 
108  return sm_cloud;
109 }
110 
113 }
114 
115 PCLCloud::Ptr cc2smReader::getOneOf(Fields field) const {
116  assert(m_cc_cloud);
117 
118  PCLCloud::Ptr sm_cloud;
119 
120  std::string name;
121  unsigned char dim = 0;
122  switch (field) {
123  case COORD_X:
124  name = "x";
125  dim = 0;
126  break;
127  case COORD_Y:
128  name = "y";
129  dim = 1;
130  break;
131  case COORD_Z:
132  name = "z";
133  dim = 2;
134  break;
135  case NORM_X:
136  if (!m_cc_cloud->hasNormals()) return sm_cloud;
137  name = "normal_x";
138  dim = 0;
139  break;
140  case NORM_Y:
141  if (!m_cc_cloud->hasNormals()) return sm_cloud;
142  name = "normal_y";
143  dim = 1;
144  break;
145  case NORM_Z:
146  if (!m_cc_cloud->hasNormals()) return sm_cloud;
147  name = "normal_z";
148  dim = 2;
149  break;
150  default:
151  // unhandled field?!
152  assert(false);
153  return sm_cloud;
154  };
155 
156  assert(/*dim >= 0 && */ dim <= 2);
157 
158  try {
159  PointCloud<FloatScalar>::Ptr pcl_cloud(new PointCloud<FloatScalar>);
160 
161  unsigned pointCount = m_cc_cloud->size();
162  unsigned realNum =
164  pcl_cloud->resize(realNum);
165  unsigned index = 0;
166  for (unsigned i = 0; i < pointCount; ++i) {
167  switch (field) {
168  case COORD_X:
169  case COORD_Y:
170  case COORD_Z: {
171  if (m_partialVisibility) {
172  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
173  POINT_VISIBLE) {
174  const CCVector3* P = m_cc_cloud->getPoint(i);
175  pcl_cloud->at(index).S5c4laR =
176  static_cast<float>(P->u[dim]);
177  ++index;
178  }
179  } else {
180  const CCVector3* P = m_cc_cloud->getPoint(i);
181  pcl_cloud->at(i).S5c4laR =
182  static_cast<float>(P->u[dim]);
183  }
184  } break;
185  case NORM_X:
186  case NORM_Y:
187  case NORM_Z: {
188  if (m_partialVisibility) {
189  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
190  POINT_VISIBLE) {
191  const CCVector3& N = m_cc_cloud->getPointNormal(i);
192  pcl_cloud->at(i).S5c4laR =
193  static_cast<float>(N.u[dim]);
194  ++index;
195  }
196  } else {
197  const CCVector3& N = m_cc_cloud->getPointNormal(i);
198  pcl_cloud->at(i).S5c4laR = static_cast<float>(N.u[dim]);
199  }
200  } break;
201  default:
202  // unhandled field?!
203  assert(false);
204  break;
205  };
206  }
207 
208  sm_cloud = PCLCloud::Ptr(new PCLCloud);
209  TO_PCL_CLOUD(*pcl_cloud, *sm_cloud);
210  sm_cloud->fields[0].name = name;
211  } catch (...) {
212  // any error (memory, etc.)
213  sm_cloud.reset();
214  }
215  return sm_cloud;
216 }
217 
218 PointCloud<PointXYZ>::Ptr cc2smReader::getXYZ2() const {
219  assert(m_cc_cloud);
220 
221  PointCloud<PointXYZ>::Ptr pcl_cloud(new PointCloud<PointXYZ>);
222  try {
223  unsigned pointCount = m_cc_cloud->size();
224  unsigned realNum =
226  pcl_cloud->resize(realNum);
227  unsigned index = 0;
228 
229  for (unsigned i = 0; i < pointCount; ++i) {
230  if (m_partialVisibility) {
231  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
232  POINT_VISIBLE) {
233  const CCVector3* P = m_cc_cloud->getPoint(i);
234  pcl_cloud->at(index).x = static_cast<float>(P->x);
235  pcl_cloud->at(index).y = static_cast<float>(P->y);
236  pcl_cloud->at(index).z = static_cast<float>(P->z);
237  ++index;
238  }
239  } else {
240  const CCVector3* P = m_cc_cloud->getPoint(i);
241  pcl_cloud->at(i).x = static_cast<float>(P->x);
242  pcl_cloud->at(i).y = static_cast<float>(P->y);
243  pcl_cloud->at(i).z = static_cast<float>(P->z);
244  }
245  }
246  } catch (...) {
247  // any error (memory, etc.)
248  pcl_cloud.reset();
249  }
250 
251  return pcl_cloud;
252 }
253 
254 PCLCloud::Ptr cc2smReader::getXYZ() const {
255  PCLCloud::Ptr sm_cloud;
256 
257  PointCloud<PointXYZ>::Ptr pcl_cloud = getXYZ2();
258  if (pcl_cloud) {
259  sm_cloud = PCLCloud::Ptr(new PCLCloud);
260  TO_PCL_CLOUD(*pcl_cloud, *sm_cloud);
261  }
262 
263  return sm_cloud;
264 }
265 
266 PCLCloud::Ptr cc2smReader::getNormals() const {
267  if (!m_cc_cloud || !m_cc_cloud->hasNormals())
268  return PCLCloud::Ptr(static_cast<PCLCloud*>(nullptr));
269 
270  PCLCloud::Ptr sm_cloud(new PCLCloud);
271  try {
272  PointCloud<OnlyNormals>::Ptr pcl_cloud(new PointCloud<OnlyNormals>);
273 
274  unsigned pointCount = m_cc_cloud->size();
275  unsigned realNum =
277  pcl_cloud->resize(realNum);
278 
279  unsigned index = 0;
280  for (unsigned i = 0; i < pointCount; ++i) {
281  if (m_partialVisibility) {
282  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
283  POINT_VISIBLE) {
284  const CCVector3& N = m_cc_cloud->getPointNormal(i);
285  pcl_cloud->at(index).normal_x = N.x;
286  pcl_cloud->at(index).normal_y = N.y;
287  pcl_cloud->at(index).normal_z = N.z;
288  ++index;
289  }
290  } else {
291  const CCVector3& N = m_cc_cloud->getPointNormal(i);
292  pcl_cloud->at(i).normal_x = N.x;
293  pcl_cloud->at(i).normal_y = N.y;
294  pcl_cloud->at(i).normal_z = N.z;
295  }
296  }
297 
298  TO_PCL_CLOUD(*pcl_cloud, *sm_cloud);
299  } catch (...) {
300  // any error (memory, etc.)
301  sm_cloud.reset();
302  }
303 
304  return sm_cloud;
305 }
306 
307 PCLCloud::Ptr cc2smReader::getPointNormals() const {
308  PCLCloud::Ptr normals = getNormals();
309  if (!normals) {
310  return normals;
311  }
312  PCLCloud::Ptr xyzCloud = getXYZ();
313  PCLCloud::Ptr sm_tmp(new PCLCloud); // temporary cloud
314  if (xyzCloud) {
315  pcl::concatenateFields(*normals, *xyzCloud, *sm_tmp);
316  }
317  return sm_tmp;
318 }
319 
320 PCLCloud::Ptr cc2smReader::getColors() const {
321  if (!m_cc_cloud || !m_cc_cloud->hasColors())
322  return PCLCloud::Ptr(static_cast<PCLCloud*>(0));
323 
324  PCLCloud::Ptr sm_cloud(new PCLCloud);
325  try {
326  PointCloud<OnlyRGB>::Ptr pcl_cloud(new PointCloud<OnlyRGB>);
327 
328  unsigned pointCount = m_cc_cloud->size();
329  unsigned realNum =
331  pcl_cloud->resize(realNum);
332  unsigned index = 0;
333 
334  for (unsigned i = 0; i < pointCount; ++i) {
335  if (m_partialVisibility) {
336  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
337  POINT_VISIBLE) {
339  pcl_cloud->at(index).r = static_cast<uint8_t>(rgb.r);
340  pcl_cloud->at(index).g = static_cast<uint8_t>(rgb.g);
341  pcl_cloud->at(index).b = static_cast<uint8_t>(rgb.b);
342  ++index;
343  }
344  } else {
346  pcl_cloud->at(i).r = static_cast<uint8_t>(rgb.r);
347  pcl_cloud->at(i).g = static_cast<uint8_t>(rgb.g);
348  pcl_cloud->at(i).b = static_cast<uint8_t>(rgb.b);
349  }
350  }
351 
352  TO_PCL_CLOUD(*pcl_cloud, *sm_cloud);
353  } catch (...) {
354  // any error (memory, etc.)
355  sm_cloud.reset();
356  }
357 
358  return sm_cloud;
359 }
360 
367  bool sfColors) const {
368  if (!m_cc_cloud) return false;
369 
370  if (!scalars) scalars = vtkSmartPointer<vtkUnsignedCharArray>::New();
371  scalars->SetNumberOfComponents(3);
372  unsigned nr_points =
374  reinterpret_cast<vtkUnsignedCharArray*>(&(*scalars))
375  ->SetNumberOfTuples(static_cast<vtkIdType>(nr_points));
376  unsigned char* colors =
377  reinterpret_cast<vtkUnsignedCharArray*>(&(*scalars))->GetPointer(0);
378  int j = 0;
379  if (m_cc_cloud->hasScalarFields() && sfColors) {
381  if (sfIdx < 0) return false;
382  cloudViewer::ScalarField* scalar_field =
383  m_cc_cloud->getScalarField(sfIdx);
384  if (!scalar_field) return false;
385  for (unsigned cp = 0; cp < nr_points; ++cp) {
386  const ecvColor::Rgb* rgb =
387  m_cc_cloud->getScalarValueColor(scalar_field->getValue(cp));
388 
389  if (m_partialVisibility) {
391  continue;
392 
393  colors[j] = static_cast<uint8_t>(rgb->r);
394  colors[j + 1] = static_cast<uint8_t>(rgb->g);
395  colors[j + 2] = static_cast<uint8_t>(rgb->b);
396  j += 3;
397  } else {
398  int idx = static_cast<int>(cp) * 3;
399  colors[idx] = static_cast<uint8_t>(rgb->r);
400  colors[idx + 1] = static_cast<uint8_t>(rgb->g);
401  colors[idx + 2] = static_cast<uint8_t>(rgb->b);
402  }
403  }
404 
405  } else if (m_cc_cloud->hasColors()) {
406  for (unsigned cp = 0; cp < nr_points; ++cp) {
407  // Color every point
409  if (m_partialVisibility) {
411  continue;
412 
413  colors[j] = static_cast<uint8_t>(rgb.r);
414  colors[j + 1] = static_cast<uint8_t>(rgb.g);
415  colors[j + 2] = static_cast<uint8_t>(rgb.b);
416  j += 3;
417 
418  } else {
419  int idx = static_cast<int>(cp) * 3;
420  colors[idx] = static_cast<uint8_t>(rgb.r);
421  colors[idx + 1] = static_cast<uint8_t>(rgb.g);
422  colors[idx + 2] = static_cast<uint8_t>(rgb.b);
423  }
424  }
425  } else {
426  return false;
427  }
428 
429  return true;
430 }
431 
432 std::string cc2smReader::GetSimplifiedSFName(const std::string& ccSfName) {
433  QString simplified = QString::fromStdString(ccSfName).simplified();
434  simplified.replace(' ', '_');
435  return simplified.toStdString();
436 }
437 
439  PCLMaterial& outMaterial) {
440  assert(inMaterial);
441  inMaterial->getTexture();
442  std::string texFile =
443  CVTools::FromQString(inMaterial->getTextureFilename());
444  std::string texName = CVTools::FromQString(inMaterial->getName());
445 
446  // Log all available texture maps
447  auto allTextures = inMaterial->getAllTextureFilenames();
448  // FIX special symbols bugs in vtk rendering system!
449  texName = CVTools::ExtractDigitAlpha(texName);
450 
451  // PBR multi-texture encoding into tex_name (for pcl::TexMaterial
452  // compatibility) pcl::TexMaterial only has one tex_file field, so we encode
453  // multiple textures into tex_name to preserve all texture information.
454  // Format: materialName_PBR_MULTITEX|type0:path0|type1:path1|...
455  // NOTE: This encoding is only used for pcl::TexMaterial/pcl::TextureMesh.
456  // For direct ccMaterial usage, use addTextureMeshFromCCMesh() which doesn't
457  // need encoding.
458  if (allTextures.size() > 1) {
459  texName += "_PBR_MULTITEX";
460  for (const auto& tex : allTextures) {
461  // Encoding format: |type:path
462  texName += "|" + std::to_string(static_cast<int>(tex.first)) + ":" +
463  CVTools::FromQString(tex.second);
464  }
466  "[cc2smReader::ConVertToPCLMaterial] Encoded %zu textures "
467  "into material name for pcl::TexMaterial compatibility",
468  allTextures.size());
469  }
470  const ecvColor::Rgbaf& ambientColor = inMaterial->getAmbient();
471  const ecvColor::Rgbaf& diffuseColor = inMaterial->getDiffuseFront();
472  const ecvColor::Rgbaf& specularColor = inMaterial->getSpecular();
473  float shininess = inMaterial->getShininessFront();
474 
475  outMaterial.tex_name = texName;
476  outMaterial.tex_file = texFile;
477  outMaterial.tex_Ka.r = ambientColor.r;
478  outMaterial.tex_Ka.g = ambientColor.g;
479  outMaterial.tex_Ka.b = ambientColor.b;
480  outMaterial.tex_Kd.r = diffuseColor.r;
481  outMaterial.tex_Kd.g = diffuseColor.g;
482  outMaterial.tex_Kd.b = diffuseColor.b;
483  outMaterial.tex_Ks.r = specularColor.r;
484  outMaterial.tex_Ks.g = specularColor.g;
485  outMaterial.tex_Ks.b = specularColor.b;
486  outMaterial.tex_d = ambientColor.a;
487  outMaterial.tex_Ns = shininess;
488  outMaterial.tex_illum = inMaterial->getIllum();
489  if (outMaterial.tex_illum > 2) // only support 0, 1, 2
490  {
491  outMaterial.tex_illum = 0;
492  }
493 }
494 
496  const std::string& field_name) const {
497  assert(m_cc_cloud);
498 
499  int sfIdx = m_cc_cloud->getScalarFieldIndexByName(field_name.c_str());
500  if (sfIdx < 0) {
501  return PCLCloud::Ptr(static_cast<PCLCloud*>(nullptr));
502  }
503  cloudViewer::ScalarField* scalar_field = m_cc_cloud->getScalarField(sfIdx);
504  assert(scalar_field);
505 
506  PCLCloud::Ptr sm_cloud(new PCLCloud);
507  try {
508  if (m_showMode) {
509  // In showMode: Convert scalar field to RGB colors for visualization
510  // Note: Original scalar values will be added separately to VTK for
511  // tooltip
512  if (m_cc_cloud->sfShown() &&
514  PointCloud<OnlyRGB>::Ptr pcl_cloud(new PointCloud<OnlyRGB>);
515 
516  unsigned pointCount = m_cc_cloud->size();
517  unsigned realNum = m_partialVisibility ? m_visibilityNum
518  : m_cc_cloud->size();
519  pcl_cloud->resize(realNum);
520  unsigned index = 0;
521 
522  for (unsigned i = 0; i < pointCount; ++i) {
523  if (m_partialVisibility) {
524  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
525  POINT_VISIBLE) {
526  ScalarType scalar = scalar_field->getValue(i);
527  const ecvColor::Rgb* col =
529  pcl_cloud->at(index).r =
530  static_cast<uint8_t>(col->r);
531  pcl_cloud->at(index).g =
532  static_cast<uint8_t>(col->g);
533  pcl_cloud->at(index).b =
534  static_cast<uint8_t>(col->b);
535  ++index;
536  }
537  } else {
538  ScalarType scalar = scalar_field->getValue(i);
539  const ecvColor::Rgb* col =
541  pcl_cloud->at(i).r = static_cast<uint8_t>(col->r);
542  pcl_cloud->at(i).g = static_cast<uint8_t>(col->g);
543  pcl_cloud->at(i).b = static_cast<uint8_t>(col->b);
544  }
545  }
546 
547  TO_PCL_CLOUD(*pcl_cloud, *sm_cloud);
548  } else {
549  sm_cloud = nullptr;
550  }
551  } else {
552  PointCloud<FloatScalar>::Ptr pcl_cloud(new PointCloud<FloatScalar>);
553  unsigned pointCount = m_cc_cloud->size();
554  unsigned realNum =
556  pcl_cloud->resize(realNum);
557  unsigned index = 0;
558 
559  for (unsigned i = 0; i < pointCount; ++i) {
560  if (m_partialVisibility) {
561  if (m_cc_cloud->getTheVisibilityArray().at(i) ==
562  POINT_VISIBLE) {
563  ScalarType scalar = scalar_field->getValue(i);
564  pcl_cloud->at(index).S5c4laR =
565  static_cast<float>(scalar);
566  ++index;
567  }
568  } else {
569  ScalarType scalar = scalar_field->getValue(i);
570  pcl_cloud->at(i).S5c4laR = static_cast<float>(scalar);
571  }
572  }
573 
574  TO_PCL_CLOUD(*pcl_cloud, *sm_cloud);
575 
576  // Now change the name of the scalar field -> we cannot have any
577  // space into the field name NOTE this is a little trick to put any
578  // number of scalar fields in a message PointCloud2 object We use a
579  // point type with a generic scalar field named scalar. we load the
580  // scalar field and then we change the name to the needed one
581  sm_cloud->fields[0].name = GetSimplifiedSFName(field_name);
582  }
583  } catch (...) {
584  // any error (memory, etc.)
585  sm_cloud.reset();
586  }
587 
588  return sm_cloud;
589 }
590 
591 bool cc2smReader::checkIfFieldExists(const std::string& field_name) const {
592  if ((field_name == "x") || (field_name == "y") || (field_name == "z") ||
593  (field_name == "xyz"))
594  return (m_cc_cloud->size() != 0);
595 
596  else if ((field_name == "normal_x") || (field_name == "normal_y") ||
597  (field_name == "normal_z") || (field_name == "normal_xyz"))
598  return m_cc_cloud->hasNormals();
599 
600  else if (field_name == "rgb")
601  return m_cc_cloud->hasColors();
602 
603  else
604  return (m_cc_cloud->getScalarFieldIndexByName(field_name.c_str()) >= 0);
605 }
606 
607 PCLCloud::Ptr cc2smReader::getAsSM(
608  std::list<std::string>& requested_fields) const {
609  // preliminary check
610  {
611  for (std::list<std::string>::const_iterator it =
612  requested_fields.begin();
613  it != requested_fields.end(); ++it) {
614  bool exists = checkIfFieldExists(*it);
615  if (!exists) { // all check results must be true
616  return PCLCloud::Ptr(static_cast<PCLCloud*>(nullptr));
617  }
618  }
619  }
620 
621  // are we asking for x, y, and z all togheters?
622  bool got_xyz = (std::find(requested_fields.begin(), requested_fields.end(),
623  "xyz") != requested_fields.end());
624  if (got_xyz) {
625  // remove from the requested fields lists x y and z as single
626  // occurrencies
627  requested_fields.erase(
628  std::remove(requested_fields.begin(), requested_fields.end(),
629  std::string("x")),
630  requested_fields.end());
631  requested_fields.erase(
632  std::remove(requested_fields.begin(), requested_fields.end(),
633  std::string("y")),
634  requested_fields.end());
635  requested_fields.erase(
636  std::remove(requested_fields.begin(), requested_fields.end(),
637  std::string("z")),
638  requested_fields.end());
639  }
640 
641  // same for normals
642  bool got_normal_xyz =
643  (std::find(requested_fields.begin(), requested_fields.end(),
644  "normal_xyz") != requested_fields.end());
645  if (got_normal_xyz) {
646  requested_fields.erase(
647  std::remove(requested_fields.begin(), requested_fields.end(),
648  std::string("normal_x")),
649  requested_fields.end());
650  requested_fields.erase(
651  std::remove(requested_fields.begin(), requested_fields.end(),
652  std::string("normal_y")),
653  requested_fields.end());
654  requested_fields.erase(
655  std::remove(requested_fields.begin(), requested_fields.end(),
656  std::string("normal_z")),
657  requested_fields.end());
658  }
659 
660  // a vector for PointCloud2 clouds
661  PCLCloud::Ptr firstCloud;
662 
663  // load and merge fields/clouds one-by-one
664  {
665  for (std::list<std::string>::const_iterator it =
666  requested_fields.begin();
667  it != requested_fields.end(); ++it) {
668  if (!firstCloud) {
669  firstCloud = getGenericField(*it);
670  } else {
671  PCLCloud::Ptr otherCloud = getGenericField(*it);
672  if (otherCloud) {
673  PCLCloud::Ptr sm_tmp(new PCLCloud); // temporary cloud
674  pcl::concatenateFields(*firstCloud, *otherCloud, *sm_tmp);
675  firstCloud = sm_tmp;
676  }
677  }
678  }
679  }
680 
681  return firstCloud;
682 }
683 
684 PCLCloud::Ptr cc2smReader::getAsSM(bool ignoreScalars) const {
685  // does the cloud have some points?
686  if (!m_cc_cloud || m_cc_cloud->size() == 0) {
687  assert(false);
688  return PCLCloud::Ptr(static_cast<PCLCloud*>(nullptr));
689  }
690 
691  // container
692  std::list<std::string> fields;
693  try {
694  fields.push_back("xyz");
695  if (m_cc_cloud->hasNormals()) fields.push_back("normal_xyz");
696 
697  if (m_cc_cloud->hasColors()) fields.push_back("rgb");
698 
699  if (!ignoreScalars) {
700  for (unsigned i = 0; i < m_cc_cloud->getNumberOfScalarFields(); ++i)
701  fields.push_back(m_cc_cloud->getScalarField(static_cast<int>(i))
702  ->getName());
703  }
704  } catch (const std::bad_alloc&) {
705  // not enough memory
706  return PCLCloud::Ptr(static_cast<PCLCloud*>(nullptr));
707  }
708 
709  return getAsSM(fields);
710 }
711 
712 static PCLCloud::Ptr SetOrAdd(PCLCloud::Ptr firstCloud,
713  PCLCloud::Ptr secondCloud) {
714  if (!secondCloud) {
715  assert(false);
716  return {};
717  }
718 
719  if (firstCloud) {
720  try {
721  PCLCloud::Ptr tempCloud(new PCLCloud); // temporary cloud
722  pcl::concatenateFields(*firstCloud, *secondCloud, *tempCloud);
723  return tempCloud;
724  } catch (const std::bad_alloc&) {
725  CVLog::Warning("Not enough memory");
726  return nullptr;
727  }
728  } else {
729  return secondCloud;
730  }
731 }
732 
733 PCLCloud::Ptr cc2smReader::getAsSM(bool xyz,
734  bool normals,
735  bool rgbColors,
736  const QStringList& scalarFields) const {
737  if (!m_cc_cloud) {
738  assert(false);
739  return {};
740  }
741 
742  PCLCloud::Ptr outputCloud;
743 
744  unsigned pointCount = m_cc_cloud->size();
745 
746  try {
747  if (xyz) {
748  PCLCloud::Ptr xyzCloud = getXYZ();
749  if (!xyzCloud) {
750  return {};
751  }
752 
753  outputCloud = SetOrAdd(outputCloud, xyzCloud);
754  }
755 
756  if (normals && m_cc_cloud->hasNormals()) {
757  PCLCloud::Ptr normalsCloud = getNormals();
758  if (!normalsCloud) {
759  return {};
760  }
761  outputCloud = SetOrAdd(outputCloud, normalsCloud);
762  }
763 
764  if (rgbColors && m_cc_cloud->hasColors()) {
765  PCLCloud::Ptr rgbCloud = getColors();
766  if (!rgbCloud) {
767  return {};
768  }
769  outputCloud = SetOrAdd(outputCloud, rgbCloud);
770  }
771 
772  for (const QString& sfName : scalarFields) {
773  PCLCloud::Ptr sfCloud = getFloatScalarField(sfName.toStdString());
774  if (!sfCloud) {
775  return {};
776  }
777  outputCloud = SetOrAdd(outputCloud, sfCloud);
778  }
779  } catch (...) {
780  // any error (memory, etc.)
781  outputCloud.reset();
782  }
783 
784  return outputCloud;
785 }
786 
787 pcl::PointCloud<pcl::PointNormal>::Ptr cc2smReader::getAsPointNormal() const {
788  if (!m_cc_cloud) {
789  assert(false);
790  return {};
791  }
792 
793  PointCloud<pcl::PointNormal>::Ptr pcl_cloud(
794  new PointCloud<pcl::PointNormal>);
795 
796  unsigned pointCount = m_cc_cloud->size();
797 
798  try {
799  pcl_cloud->resize(pointCount);
800  } catch (...) {
801  // any error (memory, etc.)
802  return {};
803  }
804 
805  for (unsigned i = 0; i < pointCount; ++i) {
806  const CCVector3* P = m_cc_cloud->getPoint(i);
807  pcl_cloud->at(i).x = static_cast<float>(P->x);
808  pcl_cloud->at(i).y = static_cast<float>(P->y);
809  pcl_cloud->at(i).z = static_cast<float>(P->z);
810  }
811 
812  if (m_cc_cloud->hasNormals()) {
813  for (unsigned i = 0; i < pointCount; ++i) {
814  const CCVector3* N = m_cc_cloud->getNormal(i);
815  pcl_cloud->at(i).normal_x = static_cast<float>(N->x);
816  pcl_cloud->at(i).normal_y = static_cast<float>(N->y);
817  pcl_cloud->at(i).normal_z = static_cast<float>(N->z);
818  }
819  }
820 
821  return pcl_cloud;
822 }
823 
824 pcl::PointCloud<pcl::PointXYZ>::Ptr cc2smReader::getRawXYZ() const {
825  if (!m_cc_cloud) {
826  assert(false);
827  return {};
828  }
829 
830  PointCloud<PointXYZ>::Ptr xyzCloud(new PointCloud<PointXYZ>);
831  try {
832  unsigned pointCount = m_cc_cloud->size();
833  xyzCloud->resize(pointCount);
834 
835  for (unsigned i = 0; i < pointCount; ++i) {
836  const CCVector3* P = m_cc_cloud->getPoint(i);
837  xyzCloud->at(i).x = static_cast<float>(P->x);
838  xyzCloud->at(i).y = static_cast<float>(P->y);
839  xyzCloud->at(i).z = static_cast<float>(P->z);
840  }
841  } catch (...) {
842  // any error (memory, etc.)
843  return {};
844  }
845 
846  return xyzCloud;
847 }
848 
850  vtkPolyData* const polydata) const {
851  // Set the colors of the pcl::PointCloud (if the pcl::PointCloud supports
852  // colors and the input vtkPolyData has colors)
853  vtkUnsignedCharArray* colors = vtkUnsignedCharArray::SafeDownCast(
854  polydata->GetPointData()->GetScalars());
855 
856  // Set the normals of the pcl::PointCloud (if the pcl::PointCloud supports
857  // normals and the input vtkPolyData has normals)
858  vtkFloatArray* normals =
859  vtkFloatArray::SafeDownCast(polydata->GetPointData()->GetNormals());
860  PCLCloud::Ptr smCloud(new PCLCloud);
861  if (colors && normals) {
862  PointCloudRGBNormal::Ptr cloud(new PointCloudRGBNormal);
863  pcl::io::vtkPolyDataToPointCloud(polydata, *cloud);
864  TO_PCL_CLOUD(*cloud, *smCloud);
865  } else if (colors && !normals) {
866  PointCloudRGB::Ptr cloud(new PointCloudRGB);
867  pcl::io::vtkPolyDataToPointCloud(polydata, *cloud);
868  TO_PCL_CLOUD(*cloud, *smCloud);
869  } else if (!colors && normals) {
870  PointCloudNormal::Ptr cloud(new PointCloudNormal);
871  pcl::io::vtkPolyDataToPointCloud(polydata, *cloud);
872  TO_PCL_CLOUD(*cloud, *smCloud);
873  } else if (!colors && !normals) {
874  PointCloudT::Ptr cloud(new PointCloudT);
875  pcl::io::vtkPolyDataToPointCloud(polydata, *cloud);
876  TO_PCL_CLOUD(*cloud, *smCloud);
877  }
878 
879  return smCloud;
880 }
881 
883  vtkPolyData* const polydata) const {
884  PCLMesh::Ptr plcMesh(new PCLMesh);
885  vtkCellArray* mesh_polygons = polydata->GetPolys();
886  if (!mesh_polygons || mesh_polygons->GetNumberOfCells() == 0) {
887  return nullptr;
888  }
889 
890  pcl::io::vtk2mesh(polydata, *plcMesh);
891  return plcMesh;
892 }
893 
895  if (!mesh) return nullptr;
896 
897  const ccGenericPointCloud::VisibilityTableType& verticesVisibility =
899  bool visFiltering =
900  (verticesVisibility.size() >= mesh->getAssociatedCloud()->size());
901  PCLMesh::Ptr pclMesh(new PCLMesh);
902 
903  if (!getPclCloud2(mesh, pclMesh->cloud)) {
905  "[cc2smReader::getPclMesh] Failed to get pcl::PCLPointCloud2!");
906  return nullptr;
907  }
908 
909  // vertices visibility
910  unsigned triNum = mesh->size();
911 
912  for (unsigned n = 0; n < triNum; ++n) {
913  const cloudViewer::VerticesIndexes* tsi =
914  mesh->getTriangleVertIndexes(n);
915  if (visFiltering) {
916  // we skip the triangle if at least one vertex is hidden
917  if ((verticesVisibility[tsi->i1] != POINT_VISIBLE) ||
918  (verticesVisibility[tsi->i2] != POINT_VISIBLE) ||
919  (verticesVisibility[tsi->i3] != POINT_VISIBLE))
920  continue;
921  }
922 
923  pcl::Vertices tri;
924  tri.vertices.push_back(n * 3 + 0);
925  tri.vertices.push_back(n * 3 + 1);
926  tri.vertices.push_back(n * 3 + 2);
927  pclMesh->polygons.push_back(tri);
928  }
929 
930  return pclMesh;
931 }
932 
934  unsigned int triNum = mesh->size();
935  if (triNum <= 0) {
936  CVLog::Warning("[cc2smReader::getPclCloud2] No triangles found!");
937  return false;
938  }
939 
940  std::size_t dimension = static_cast<std::size_t>(
942 
943  const ccGenericPointCloud::VisibilityTableType& verticesVisibility =
945  bool visFiltering =
946  (verticesVisibility.size() >= mesh->getAssociatedCloud()->size());
947 
948  bool showSF = mesh->hasDisplayedScalarField() && mesh->sfShown();
949  bool showColors = showSF || (mesh->hasColors() && mesh->colorsShown());
950 
951  // per-triangle normals?
952  bool showTriNormals = (mesh->hasTriNormals() && mesh->triNormsShown());
953  // fix 'showNorms'
954  bool showNorms =
955  showTriNormals || (mesh->hasNormals() && mesh->normalsShown());
956 
957  pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_cloud(
958  new pcl::PointCloud<pcl::PointXYZ>());
959  {
960  xyz_cloud->points.resize(static_cast<std::size_t>(triNum) * dimension);
961  xyz_cloud->width = xyz_cloud->size();
962  xyz_cloud->height = 1;
963  xyz_cloud->is_dense = true;
964  }
965 
966  pcl::PointCloud<pcl::RGB>::Ptr rgb_cloud = nullptr;
967  if (showColors) {
968  rgb_cloud.reset(new pcl::PointCloud<pcl::RGB>());
969  rgb_cloud->points.resize(static_cast<std::size_t>(triNum) * dimension);
970  rgb_cloud->width = rgb_cloud->size();
971  rgb_cloud->height = 1;
972  rgb_cloud->is_dense = true;
973  }
974 
975  pcl::PointCloud<pcl::Normal>::Ptr normal_cloud = nullptr;
976  if (showNorms) {
977  normal_cloud.reset(new pcl::PointCloud<pcl::Normal>());
978  normal_cloud->resize(static_cast<std::size_t>(triNum) * dimension);
979  normal_cloud->width = xyz_cloud->size();
980  normal_cloud->height = 1;
981  normal_cloud->is_dense = true;
982  }
983 
984  // per-triangle normals
985  const NormsIndexesTableType* triNormals = mesh->getTriNormsTable();
986  // materials
987  const ccMaterialSet* materials = mesh->getMaterialSet();
988 
989  // in the case we need normals (i.e. lighting)
990  NormsIndexesTableType* normalsIndexesTable = nullptr;
991  ccNormalVectors* compressedNormals = nullptr;
992  if (showNorms) {
993  // assert(m_cc_cloud->isA(CV_TYPES::POINT_CLOUD));
994  normalsIndexesTable = m_cc_cloud->normals();
995  compressedNormals = ccNormalVectors::GetUniqueInstance();
996  }
997 
998  // current vertex normal
999  const PointCoordinateType* N1 = nullptr;
1000  const PointCoordinateType* N2 = nullptr;
1001  const PointCoordinateType* N3 = nullptr;
1002 
1003  // vertices visibility
1004  for (unsigned n = 0; n < triNum; ++n) {
1005  const cloudViewer::VerticesIndexes* tsi =
1006  mesh->getTriangleVertIndexes(n);
1007  if (visFiltering) {
1008  // we skip the triangle if at least one vertex is hidden
1009  if ((verticesVisibility[tsi->i1] != POINT_VISIBLE) ||
1010  (verticesVisibility[tsi->i2] != POINT_VISIBLE) ||
1011  (verticesVisibility[tsi->i3] != POINT_VISIBLE))
1012  continue;
1013  }
1014 
1015  // First get the xyz information
1016  for (std::size_t vertexIndex = 0; vertexIndex < dimension;
1017  vertexIndex++) {
1018  (*xyz_cloud)[n * dimension + vertexIndex].x = static_cast<float>(
1019  m_cc_cloud->getPoint(tsi->i[vertexIndex])->x);
1020  (*xyz_cloud)[n * dimension + vertexIndex].y = static_cast<float>(
1021  m_cc_cloud->getPoint(tsi->i[vertexIndex])->y);
1022  (*xyz_cloud)[n * dimension + vertexIndex].z = static_cast<float>(
1023  m_cc_cloud->getPoint(tsi->i[vertexIndex])->z);
1024  }
1025 
1026  // Then the color information, if any
1027  if (showSF) {
1028  for (std::size_t vertexIndex = 0; vertexIndex < dimension;
1029  vertexIndex++) {
1030  // individual component copy due to different memory layout
1031  const ecvColor::Rgb* rgb =
1033  ->getValueColor(tsi->i[vertexIndex]);
1034  (*rgb_cloud)[n * dimension + vertexIndex].r = rgb->r;
1035  (*rgb_cloud)[n * dimension + vertexIndex].g = rgb->g;
1036  (*rgb_cloud)[n * dimension + vertexIndex].b = rgb->b;
1037  (*rgb_cloud)[n * dimension + vertexIndex].a = 255;
1038  }
1039  } else if (showColors) {
1040  for (std::size_t vertexIndex = 0; vertexIndex < dimension;
1041  vertexIndex++) {
1042  // individual component copy due to different memory layout
1043  const ecvColor::Rgb* rgb =
1044  &m_cc_cloud->rgbColors()->at(tsi->i[vertexIndex]);
1045  (*rgb_cloud)[n * dimension + vertexIndex].r = rgb->r;
1046  (*rgb_cloud)[n * dimension + vertexIndex].g = rgb->g;
1047  (*rgb_cloud)[n * dimension + vertexIndex].b = rgb->b;
1048  (*rgb_cloud)[n * dimension + vertexIndex].a = 255;
1049  }
1050  }
1051 
1052  // Then handle the normals, if any
1053  if (showNorms) {
1054  if (showTriNormals) {
1055  assert(triNormals);
1056  int n1 = 0;
1057  int n2 = 0;
1058  int n3 = 0;
1059  mesh->getTriangleNormalIndexes(n, n1, n2, n3);
1060  N1 = (n1 >= 0 ? ccNormalVectors::GetNormal(triNormals->at(n1)).u
1061  : nullptr);
1062  N2 = (n1 == n2 ? N1
1063  : n1 >= 0
1064  ? ccNormalVectors::GetNormal(triNormals->at(n2)).u
1065  : nullptr);
1066  N3 = (n1 == n3 ? N1
1067  : n3 >= 0
1068  ? ccNormalVectors::GetNormal(triNormals->at(n3)).u
1069  : nullptr);
1070  } else {
1071  N1 = compressedNormals
1072  ->getNormal(normalsIndexesTable->at(tsi->i1))
1073  .u;
1074  N2 = compressedNormals
1075  ->getNormal(normalsIndexesTable->at(tsi->i2))
1076  .u;
1077  N3 = compressedNormals
1078  ->getNormal(normalsIndexesTable->at(tsi->i3))
1079  .u;
1080  }
1081 
1082  (*normal_cloud)[n * dimension + 0].normal_x = N1[0];
1083  (*normal_cloud)[n * dimension + 0].normal_y = N1[1];
1084  (*normal_cloud)[n * dimension + 0].normal_z = N1[2];
1085  (*normal_cloud)[n * dimension + 1].normal_x = N2[0];
1086  (*normal_cloud)[n * dimension + 1].normal_y = N2[1];
1087  (*normal_cloud)[n * dimension + 1].normal_z = N2[2];
1088  (*normal_cloud)[n * dimension + 2].normal_x = N3[0];
1089  (*normal_cloud)[n * dimension + 2].normal_y = N3[1];
1090  (*normal_cloud)[n * dimension + 2].normal_z = N3[2];
1091  }
1092  }
1093 
1094  // And put it in the mesh cloud
1095  {
1096  // points
1097  TO_PCL_CLOUD(*xyz_cloud, cloud);
1098 
1099  // colors
1100  if (showColors) {
1101  PCLCloud rgb_cloud2;
1102  TO_PCL_CLOUD(*rgb_cloud, rgb_cloud2);
1103  PCLCloud aux;
1104  pcl::concatenateFields(rgb_cloud2, cloud, aux);
1105  cloud = aux;
1106  }
1107 
1108  // normals
1109  if (showNorms) {
1110  PCLCloud normal_cloud2;
1111  TO_PCL_CLOUD(*normal_cloud, normal_cloud2);
1112  PCLCloud aux;
1113  pcl::concatenateFields(normal_cloud2, cloud, aux);
1114  cloud = aux;
1115  }
1116  }
1117 
1118  return true;
1119 }
1120 
1122  ccGenericMesh* mesh, vtkSmartPointer<vtkPolyData>& polydata) const {
1123  if (!mesh) {
1124  return false;
1125  }
1126 
1127  // Use the same logic as getPclCloud2 to ensure consistency
1128  // Point indexing: pointIndex = n * dimension + vertexIndex
1129  unsigned int triNum = mesh->size();
1130  if (triNum <= 0) {
1132  "[cc2smReader::getVtkPolyDataFromMeshCloud] No triangles "
1133  "found!");
1134  return false;
1135  }
1136 
1137  std::size_t dimension = static_cast<std::size_t>(
1138  mesh->getTriangleVertIndexes(0)->getDimension());
1139 
1140  const ccGenericPointCloud::VisibilityTableType& verticesVisibility =
1142  bool visFiltering =
1143  (verticesVisibility.size() >= mesh->getAssociatedCloud()->size());
1144 
1145  bool showSF = mesh->hasDisplayedScalarField() && mesh->sfShown();
1146  bool showColors = showSF || (mesh->hasColors() && mesh->colorsShown());
1147 
1148  // per-triangle normals?
1149  bool showTriNormals = (mesh->hasTriNormals() && mesh->triNormsShown());
1150  bool showNorms =
1151  showTriNormals || (mesh->hasNormals() && mesh->normalsShown());
1152 
1153  // IMPORTANT: For Find Data / selection tools, always export normals if
1154  // available regardless of display setting. This ensures tooltip can show
1155  // normal values. The display setting only affects rendering, not data
1156  // availability.
1157  bool hasNormalsForExport = mesh->hasTriNormals() || mesh->hasNormals();
1158 
1160  QString("[cc2smReader::getVtkPolyDataFromMeshCloud] "
1161  "hasTriNormals=%1, hasNormals=%2, hasNormalsForExport=%3")
1162  .arg(mesh->hasTriNormals())
1163  .arg(mesh->hasNormals())
1164  .arg(hasNormalsForExport));
1165 
1166  // Pre-allocate VTK data structures (same size as getPclCloud2)
1167  // IMPORTANT: Use triNum * dimension (not visibleTriNum) to match
1168  // getPclCloud2
1169  std::size_t totalPoints = static_cast<std::size_t>(triNum) * dimension;
1171  poly_points->SetNumberOfPoints(static_cast<vtkIdType>(totalPoints));
1172 
1174  if (showColors) {
1176  colors->SetNumberOfComponents(3);
1177  colors->SetNumberOfTuples(static_cast<vtkIdType>(totalPoints));
1178  colors->SetName("RGB");
1179  }
1180 
1181  // Create normals array if normals exist (for Find Data / selection)
1182  // Use hasNormalsForExport to always export normals data
1184  if (hasNormalsForExport) {
1186  normals->SetNumberOfComponents(3);
1187  normals->SetNumberOfTuples(static_cast<vtkIdType>(totalPoints));
1188  normals->SetName("Normals"); // Explicit name for Find Data detection
1189  }
1190 
1192  polys->AllocateEstimate(static_cast<vtkIdType>(triNum), 3);
1193 
1194  // per-triangle normals
1195  const NormsIndexesTableType* triNormals = mesh->getTriNormsTable();
1196 
1197  // in the case we need normals (i.e. lighting)
1198  // Use hasNormalsForExport instead of showNorms to always populate normal
1199  // data
1200  NormsIndexesTableType* normalsIndexesTable = nullptr;
1201  ccNormalVectors* compressedNormals = nullptr;
1202  if (hasNormalsForExport) {
1203  normalsIndexesTable = m_cc_cloud->normals();
1204  compressedNormals = ccNormalVectors::GetUniqueInstance();
1205  }
1206 
1207  // current vertex normal
1208  const PointCoordinateType* N1 = nullptr;
1209  const PointCoordinateType* N2 = nullptr;
1210  const PointCoordinateType* N3 = nullptr;
1211 
1212  // Process triangles (same logic as getPclCloud2)
1213  for (unsigned n = 0; n < triNum; ++n) {
1214  const cloudViewer::VerticesIndexes* tsi =
1215  mesh->getTriangleVertIndexes(n);
1216  if (visFiltering) {
1217  // we skip the triangle if at least one vertex is hidden
1218  if ((verticesVisibility[tsi->i1] != POINT_VISIBLE) ||
1219  (verticesVisibility[tsi->i2] != POINT_VISIBLE) ||
1220  (verticesVisibility[tsi->i3] != POINT_VISIBLE))
1221  continue;
1222  }
1223 
1224  // Calculate point indices (MUST match getPclCloud2: n * dimension +
1225  // vertexIndex)
1226  vtkIdType basePointIndex =
1227  static_cast<vtkIdType>(n) * static_cast<vtkIdType>(dimension);
1228 
1229  // Process each vertex (same logic as getPclCloud2)
1230  for (std::size_t vertexIndex = 0; vertexIndex < dimension;
1231  vertexIndex++) {
1232  vtkIdType pointIndex =
1233  basePointIndex + static_cast<vtkIdType>(vertexIndex);
1234  unsigned vertexIdx = tsi->i[vertexIndex];
1235 
1236  // Set point (same logic as getPclCloud2)
1237  const CCVector3* p = m_cc_cloud->getPoint(vertexIdx);
1238  poly_points->SetPoint(pointIndex, p->x, p->y, p->z);
1239 
1240  // Set color if needed (same logic as getPclCloud2)
1241  if (showColors) {
1242  const ecvColor::Rgb* rgb = nullptr;
1243  if (showSF) {
1245  ->getValueColor(vertexIdx);
1246  } else {
1247  rgb = &m_cc_cloud->rgbColors()->at(vertexIdx);
1248  }
1249  unsigned char* colorPtr = colors->GetPointer(
1250  static_cast<vtkIdType>(pointIndex) * 3);
1251  colorPtr[0] = rgb->r;
1252  colorPtr[1] = rgb->g;
1253  colorPtr[2] = rgb->b;
1254  }
1255 
1256  // Set normal if available (for Find Data / selection)
1257  // Use hasNormalsForExport to always export normals data regardless
1258  // of display setting
1259  if (hasNormalsForExport) {
1260  // Use tri normals if available, otherwise use vertex normals
1261  bool useTriNormals = mesh->hasTriNormals() && triNormals;
1262  if (useTriNormals) {
1263  int n1 = 0;
1264  int n2 = 0;
1265  int n3 = 0;
1266  mesh->getTriangleNormalIndexes(n, n1, n2, n3);
1267  N1 = (n1 >= 0 ? ccNormalVectors::GetNormal(
1268  triNormals->at(n1))
1269  .u
1270  : nullptr);
1271  N2 = (n1 == n2 ? N1
1272  : n2 >= 0 ? ccNormalVectors::GetNormal(
1273  triNormals->at(n2))
1274  .u
1275  : nullptr);
1276  N3 = (n1 == n3 ? N1
1277  : n3 >= 0 ? ccNormalVectors::GetNormal(
1278  triNormals->at(n3))
1279  .u
1280  : nullptr);
1281  } else if (normalsIndexesTable && compressedNormals) {
1282  N1 = compressedNormals
1283  ->getNormal(normalsIndexesTable->at(tsi->i1))
1284  .u;
1285  N2 = compressedNormals
1286  ->getNormal(normalsIndexesTable->at(tsi->i2))
1287  .u;
1288  N3 = compressedNormals
1289  ->getNormal(normalsIndexesTable->at(tsi->i3))
1290  .u;
1291  }
1292 
1293  const PointCoordinateType* N = nullptr;
1294  if (vertexIndex == 0) {
1295  N = N1;
1296  } else if (vertexIndex == 1) {
1297  N = N2;
1298  } else {
1299  N = N3;
1300  }
1301 
1302  float* normalPtr = normals->GetPointer(
1303  static_cast<vtkIdType>(pointIndex) * 3);
1304  if (N) {
1305  normalPtr[0] = static_cast<float>(N[0]);
1306  normalPtr[1] = static_cast<float>(N[1]);
1307  normalPtr[2] = static_cast<float>(N[2]);
1308  } else {
1309  normalPtr[0] = 0.0f;
1310  normalPtr[1] = 0.0f;
1311  normalPtr[2] = 0.0f;
1312  }
1313  }
1314  }
1315 
1316  // Add polygon (same indexing as getPclTextureMesh: n * 3 + vertexIndex)
1317  polys->InsertNextCell(3);
1318  polys->InsertCellPoint(basePointIndex + 0);
1319  polys->InsertCellPoint(basePointIndex + 1);
1320  polys->InsertCellPoint(basePointIndex + 2);
1321  }
1322 
1323  // Create polydata
1324  polydata = vtkSmartPointer<vtkPolyData>::New();
1325  polydata->SetPoints(poly_points);
1326  polydata->SetPolys(polys);
1327  if (showColors) {
1328  // Set scalar array name for tooltip display
1329  // If showing scalar field, use the scalar field name; otherwise use
1330  // "RGB"
1331  if (showSF && mesh->hasDisplayedScalarField()) {
1332  // Cast to ccPointCloud to access scalar field methods
1334  const_cast<ccGenericPointCloud*>(
1335  mesh->getAssociatedCloud()));
1336  if (cloud) {
1337  int sfIdx = cloud->getCurrentDisplayedScalarFieldIndex();
1338  if (sfIdx >= 0) {
1339  QString sfName = cloud->getScalarFieldName(sfIdx);
1340  colors->SetName(sfName.toStdString().c_str());
1341  CVLog::PrintDebug(QString("[cc2smReader::"
1342  "getVtkPolyDataFromMeshCloud] "
1343  "Set scalar array name: %1")
1344  .arg(sfName));
1345  } else {
1346  colors->SetName("RGB");
1347  }
1348  } else {
1349  colors->SetName("RGB");
1350  }
1351  } else {
1352  colors->SetName("RGB");
1353  }
1354  polydata->GetPointData()->SetScalars(colors);
1355  }
1356  // Always set normals if available (for Find Data / selection tools)
1357  if (hasNormalsForExport && normals) {
1358  polydata->GetPointData()->SetNormals(normals);
1359  }
1360 
1361  // Add dataset name to field data (ParaView style)
1362  QString meshName = mesh->getName();
1363  if (meshName.length() > 0) {
1364  vtkSmartPointer<vtkStringArray> datasetNameArray =
1366  datasetNameArray->SetName("DatasetName");
1367  datasetNameArray->SetNumberOfTuples(1);
1368  datasetNameArray->SetValue(0, meshName.toStdString());
1369  polydata->GetFieldData()->AddArray(datasetNameArray);
1370  }
1371 
1372  return true;
1373 }
1374 
1375 PCLTextureMesh::Ptr cc2smReader::getPclTextureMesh(ccGenericMesh* mesh) {
1376  if (!mesh) return nullptr;
1377 
1378  // materials & textures
1379  bool applyMaterials = (mesh->hasMaterials() && mesh->materialsShown());
1380  bool lodEnabled = false;
1381  bool showTextures =
1382  (mesh->hasTextures() && mesh->materialsShown() && !lodEnabled);
1383 
1384  if (applyMaterials || showTextures) {
1385  unsigned int triNum = mesh->size();
1386  if (triNum <= 0) {
1388  "[cc2smReader::getPclTextureMesh] No triangles found!");
1389  return nullptr;
1390  }
1391 
1392  PCLTextureMesh::Ptr textureMesh(new PCLTextureMesh);
1393  if (!getPclCloud2(mesh, textureMesh->cloud)) {
1395  "[cc2smReader::getPclTextureMesh] Failed to get "
1396  "pcl::PCLPointCloud2!");
1397  return nullptr;
1398  }
1399 
1400  const ccGenericPointCloud::VisibilityTableType& verticesVisibility =
1402  bool visFiltering = (verticesVisibility.size() >=
1403  mesh->getAssociatedCloud()->size());
1404 
1405  // materials
1406  const ccMaterialSet* materials = mesh->getMaterialSet();
1407 
1408  // loop on all triangles
1409  int lasMtlIndex = -1;
1410 
1411  // vertices visibility
1412  for (unsigned n = 0; n < triNum; ++n) {
1413  const cloudViewer::VerticesIndexes* tsi =
1414  mesh->getTriangleVertIndexes(n);
1415  if (visFiltering) {
1416  // we skip the triangle if at least one vertex is hidden
1417  if ((verticesVisibility[tsi->i1] != POINT_VISIBLE) ||
1418  (verticesVisibility[tsi->i2] != POINT_VISIBLE) ||
1419  (verticesVisibility[tsi->i3] != POINT_VISIBLE))
1420  continue;
1421  }
1422 
1423  assert(materials);
1424  int newMatlIndex = mesh->getTriangleMtlIndex(n);
1425  // do we need to change material?
1426  if (lasMtlIndex != newMatlIndex) {
1427  assert(newMatlIndex < static_cast<int>(materials->size()));
1428 
1429  if (newMatlIndex >= 0) {
1430  textureMesh->tex_polygons.push_back(
1431  std::vector<pcl::Vertices>());
1432  textureMesh->tex_materials.push_back(PCLMaterial());
1433  textureMesh->tex_coordinates.push_back(
1434  std::vector<Eigen::Vector2f,
1435  Eigen::aligned_allocator<
1436  Eigen::Vector2f>>());
1437  ConVertToPCLMaterial((*materials)[newMatlIndex],
1438  textureMesh->tex_materials.back());
1439  } else { // if we don't have any current material, we apply
1440  // default one
1441  textureMesh->tex_polygons.push_back(
1442  std::vector<pcl::Vertices>());
1443  textureMesh->tex_materials.push_back(PCLMaterial());
1444  textureMesh->tex_coordinates.push_back(
1445  std::vector<Eigen::Vector2f,
1446  Eigen::aligned_allocator<
1447  Eigen::Vector2f>>());
1448  ccMaterial::Shared defaultMaterial(
1449  new ccMaterial("default"));
1450  ConVertToPCLMaterial(defaultMaterial,
1451  textureMesh->tex_materials.back());
1452  }
1453 
1454  lasMtlIndex = newMatlIndex;
1455  }
1456 
1457  // get the texture coordinates information
1458  if (showTextures && !textureMesh->tex_coordinates.empty()) {
1459  // current vertex texture coordinates
1460  TexCoords2D* Tx1 = nullptr;
1461  TexCoords2D* Tx2 = nullptr;
1462  TexCoords2D* Tx3 = nullptr;
1463  mesh->getTriangleTexCoordinates(n, Tx1, Tx2, Tx3);
1464 
1465  // Check for null pointers before dereferencing
1466  if (Tx1 && Tx2 && Tx3) {
1467  textureMesh->tex_coordinates.back().push_back(
1468  Eigen::Vector2f(Tx1->tx, Tx1->ty));
1469  textureMesh->tex_coordinates.back().push_back(
1470  Eigen::Vector2f(Tx2->tx, Tx2->ty));
1471  textureMesh->tex_coordinates.back().push_back(
1472  Eigen::Vector2f(Tx3->tx, Tx3->ty));
1473  } else {
1475  "[cc2smReader::getPclTextureMesh] Null texture "
1476  "coordinates for triangle %d (Tx1=%p, Tx2=%p, "
1477  "Tx3=%p)",
1478  n, Tx1, Tx2, Tx3);
1479  // Add default UV coordinates to avoid crash
1480  textureMesh->tex_coordinates.back().push_back(
1481  Eigen::Vector2f(0.0f, 0.0f));
1482  textureMesh->tex_coordinates.back().push_back(
1483  Eigen::Vector2f(0.0f, 0.0f));
1484  textureMesh->tex_coordinates.back().push_back(
1485  Eigen::Vector2f(0.0f, 0.0f));
1486  }
1487  }
1488 
1489  pcl::Vertices tri;
1490  tri.vertices.push_back(n * 3 + 0);
1491  tri.vertices.push_back(n * 3 + 1);
1492  tri.vertices.push_back(n * 3 + 2);
1493 
1494  if (!textureMesh->tex_polygons.empty()) {
1495  textureMesh->tex_polygons.back().push_back(tri);
1496  }
1497  }
1498 
1499  // check
1500  {
1501  // no texture materials --> exit
1502  if (textureMesh->tex_materials.size() == 0) {
1504  "[cc2smReader::getPclTextureMesh] No textures found!");
1505  return nullptr;
1506  }
1507  // polygons are mapped to texture materials
1508  if (textureMesh->tex_materials.size() !=
1509  textureMesh->tex_polygons.size()) {
1511  "[cc2smReader::getPclTextureMesh] Materials number %lu "
1512  "differs from polygons number %lu!",
1513  textureMesh->tex_materials.size(),
1514  textureMesh->tex_polygons.size());
1515  return nullptr;
1516  }
1517  // each texture material should have its coordinates set
1518  if (textureMesh->tex_materials.size() !=
1519  textureMesh->tex_coordinates.size()) {
1521  "[cc2smReader::getPclTextureMesh] Coordinates number "
1522  "%lu differs from materials number %lu!",
1523  textureMesh->tex_coordinates.size(),
1524  textureMesh->tex_materials.size());
1525  return nullptr;
1526  }
1527  }
1528  return textureMesh;
1529  } else {
1531  "[cc2smReader::getPclTextureMesh] this mesh has no material "
1532  "and texture and please try polygonMesh other than "
1533  "textureMesh");
1534  return nullptr;
1535  }
1536 }
1537 
1538 PCLPolygon::Ptr cc2smReader::getPclPolygon(ccPolyline* polyline) const {
1539  PCLPolygon::Ptr pclPolygon(new PCLPolygon);
1540  pcl::PointCloud<PointT>::Ptr cloud_xyz(new pcl::PointCloud<PointT>);
1541  if (polyline->size() < 2) {
1542  return nullptr;
1543  }
1544 
1545  cloud_xyz->resize(polyline->size());
1546 
1547  for (unsigned i = 0; i < polyline->size(); ++i) {
1548  const CCVector3* pp = polyline->getPoint(i);
1549  CCVector3d output3D;
1550  if (polyline->is2DMode()) {
1551  PCLDisplayTools::TheInstance()->toWorldPoint(*pp, output3D);
1552  } else {
1553  output3D = CCVector3d::fromArray(pp->u);
1554  }
1555 
1556  cloud_xyz->points[i].x = output3D.x;
1557  cloud_xyz->points[i].y = output3D.y;
1558  cloud_xyz->points[i].z = output3D.z;
1559  }
1560 
1561  pclPolygon->setContour(*cloud_xyz);
1562 
1563  return pclPolygon;
1564 }
1565 
1567  ccGenericMesh* mesh,
1568  vtkSmartPointer<vtkPolyData>& polydata,
1569  vtkSmartPointer<vtkMatrix4x4>& transformation,
1570  std::vector<std::vector<Eigen::Vector2f>>& tex_coordinates) {
1571  if (!mesh) {
1572  return false;
1573  }
1574 
1575  // Use getVtkPolyDataFromMeshCloud to get base polydata (points, colors,
1576  // normals) This avoids creating PCL intermediate format and improves
1577  // efficiency
1578  if (!getVtkPolyDataFromMeshCloud(mesh, polydata)) {
1580  "[cc2smReader::getVtkPolyDataWithTextures] Failed to get "
1581  "vtkPolyData!");
1582  return false;
1583  }
1584 
1585  // Now extract texture coordinates using same logic as getPclTextureMesh
1586  // but without creating PCL intermediate format
1587  bool applyMaterials = (mesh->hasMaterials() && mesh->materialsShown());
1588  bool lodEnabled = false;
1589  bool showTextures =
1590  (mesh->hasTextures() && mesh->materialsShown() && !lodEnabled);
1591 
1592  if (!applyMaterials && !showTextures) {
1594  "[cc2smReader::getVtkPolyDataWithTextures] Mesh has no "
1595  "materials/textures!");
1596  return false;
1597  }
1598 
1599  unsigned int triNum = mesh->size();
1600  if (triNum <= 0) {
1602  "[cc2smReader::getVtkPolyDataWithTextures] No triangles "
1603  "found!");
1604  return false;
1605  }
1606 
1607  std::size_t dimension = static_cast<std::size_t>(
1608  mesh->getTriangleVertIndexes(0)->getDimension());
1609 
1610  const ccGenericPointCloud::VisibilityTableType& verticesVisibility =
1612  bool visFiltering =
1613  (verticesVisibility.size() >= mesh->getAssociatedCloud()->size());
1614 
1615  const ccMaterialSet* materials = mesh->getMaterialSet();
1616  assert(materials);
1617 
1618  // Track material groups and texture coordinates (same logic as
1619  // getPclTextureMesh)
1620  std::vector<std::vector<Eigen::Vector2f>> materialTexCoords;
1621  std::vector<int>
1622  triangleToMaterial; // Map triangle index to material index
1623  triangleToMaterial.reserve(triNum);
1624 
1625  int lasMtlIndex = -1;
1626  int currentMaterialIndex = -1;
1627 
1628  // Process triangles to extract texture coordinates (same logic as
1629  // getPclTextureMesh)
1630  for (unsigned n = 0; n < triNum; ++n) {
1631  const cloudViewer::VerticesIndexes* tsi =
1632  mesh->getTriangleVertIndexes(n);
1633  if (visFiltering) {
1634  if ((verticesVisibility[tsi->i1] != POINT_VISIBLE) ||
1635  (verticesVisibility[tsi->i2] != POINT_VISIBLE) ||
1636  (verticesVisibility[tsi->i3] != POINT_VISIBLE))
1637  continue;
1638  }
1639 
1640  // Check material change (same logic as getPclTextureMesh)
1641  int newMatlIndex = mesh->getTriangleMtlIndex(n);
1642  if (lasMtlIndex != newMatlIndex) {
1643  if (newMatlIndex >= 0) {
1644  assert(newMatlIndex < static_cast<int>(materials->size()));
1645  currentMaterialIndex =
1646  static_cast<int>(materialTexCoords.size());
1647  materialTexCoords.push_back(std::vector<Eigen::Vector2f>());
1648  } else {
1649  // Default material
1650  currentMaterialIndex =
1651  static_cast<int>(materialTexCoords.size());
1652  materialTexCoords.push_back(std::vector<Eigen::Vector2f>());
1653  }
1654  lasMtlIndex = newMatlIndex;
1655  }
1656 
1657  // Store material index for this triangle
1658  if (static_cast<size_t>(n) >= triangleToMaterial.size()) {
1659  triangleToMaterial.resize(n + 1, -1);
1660  }
1661  triangleToMaterial[n] = currentMaterialIndex;
1662 
1663  // Get texture coordinates (same logic as getPclTextureMesh)
1664  if (showTextures && currentMaterialIndex >= 0) {
1665  TexCoords2D* Tx1 = nullptr;
1666  TexCoords2D* Tx2 = nullptr;
1667  TexCoords2D* Tx3 = nullptr;
1668  mesh->getTriangleTexCoordinates(n, Tx1, Tx2, Tx3);
1669 
1670  if (Tx1 && Tx2 && Tx3) {
1671  materialTexCoords[currentMaterialIndex].push_back(
1672  Eigen::Vector2f(Tx1->tx, Tx1->ty));
1673  materialTexCoords[currentMaterialIndex].push_back(
1674  Eigen::Vector2f(Tx2->tx, Tx2->ty));
1675  materialTexCoords[currentMaterialIndex].push_back(
1676  Eigen::Vector2f(Tx3->tx, Tx3->ty));
1677  } else {
1678  // Default UV coordinates
1679  materialTexCoords[currentMaterialIndex].push_back(
1680  Eigen::Vector2f(0.0f, 0.0f));
1681  materialTexCoords[currentMaterialIndex].push_back(
1682  Eigen::Vector2f(0.0f, 0.0f));
1683  materialTexCoords[currentMaterialIndex].push_back(
1684  Eigen::Vector2f(0.0f, 0.0f));
1685  }
1686  } else if (currentMaterialIndex >= 0) {
1687  // No texture coordinates, use defaults
1688  materialTexCoords[currentMaterialIndex].push_back(
1689  Eigen::Vector2f(0.0f, 0.0f));
1690  materialTexCoords[currentMaterialIndex].push_back(
1691  Eigen::Vector2f(0.0f, 0.0f));
1692  materialTexCoords[currentMaterialIndex].push_back(
1693  Eigen::Vector2f(0.0f, 0.0f));
1694  }
1695  }
1696 
1697  // Build texture coordinates arrays: for each material, create a full-size
1698  // array Points that don't belong to a material get invalid coordinates (-1,
1699  // -1)
1700  vtkIdType numPoints = polydata->GetNumberOfPoints();
1701  tex_coordinates.clear();
1702  tex_coordinates.resize(materialTexCoords.size());
1703 
1704  // Track current position in each material's coordinate array
1705  std::vector<size_t> materialCoordIndex(materialTexCoords.size(), 0);
1706 
1707  // Iterate through all points (which are in triangle order: n * dimension +
1708  // vertexIndex)
1709  for (vtkIdType pointIdx = 0; pointIdx < numPoints; ++pointIdx) {
1710  int triIdx = static_cast<int>(pointIdx) / static_cast<int>(dimension);
1711  int matIdx = (triIdx < static_cast<int>(triangleToMaterial.size()))
1712  ? triangleToMaterial[triIdx]
1713  : -1;
1714 
1715  // For each material, add coordinate or invalid marker
1716  for (size_t mat = 0; mat < materialTexCoords.size(); ++mat) {
1717  if (static_cast<int>(mat) == matIdx &&
1718  materialCoordIndex[mat] < materialTexCoords[mat].size()) {
1719  // This point belongs to this material, use its coordinate
1720  tex_coordinates[mat].push_back(
1721  materialTexCoords[mat][materialCoordIndex[mat]]);
1722  materialCoordIndex[mat]++;
1723  } else {
1724  // This point doesn't belong to this material, use invalid
1725  // coordinate
1726  tex_coordinates[mat].push_back(Eigen::Vector2f(-1.0f, -1.0f));
1727  }
1728  }
1729  }
1730 
1731  // Add material library information (ParaView-style)
1732  // MaterialLibraries should contain the MTL filename(s), not individual
1733  // material names
1734  if (materials && materials->size() > 0) {
1735  // Try to get MTL filename from mesh metadata (stored during OBJ
1736  // loading)
1737  QString mtlFilename;
1738  QVariant mtlData = mesh->getMetaData("MTL_FILENAME");
1739  if (mtlData.isValid() && !mtlData.toString().isEmpty()) {
1740  mtlFilename = mtlData.toString();
1741  } else {
1742  // Fallback: use first material's name
1743  mtlFilename = materials->at(0)->getName();
1744  }
1745 
1746  vtkSmartPointer<vtkStringArray> materialLibArray =
1748  materialLibArray->SetName("MaterialLibraries");
1749  materialLibArray->SetNumberOfTuples(1); // ParaView shows one MTL file
1750  materialLibArray->SetValue(0, mtlFilename.toStdString());
1751  polydata->GetFieldData()->AddArray(materialLibArray);
1752 
1753  // Add MaterialNames array (ParaView-style)
1754  // This contains individual material names for texture coordinate naming
1755  vtkSmartPointer<vtkStringArray> materialNamesArray =
1757  materialNamesArray->SetName("MaterialNames");
1758  materialNamesArray->SetNumberOfComponents(1);
1759  materialNamesArray->SetNumberOfTuples(materials->size());
1760  for (size_t i = 0; i < materials->size(); ++i) {
1761  QString matName = materials->at(i)->getName();
1762  materialNamesArray->SetValue(i, matName.toStdString());
1763  }
1764  polydata->GetFieldData()->AddArray(materialNamesArray);
1765  }
1766 
1767  // Note: Texture coordinates are returned via tex_coordinates parameter
1768  // and will be applied later by
1769  // MeshTextureApplier::ApplyTexturesFromMaterialSet
1770 
1771  // Create transformation matrix (identity for now)
1772  transformation = vtkSmartPointer<vtkMatrix4x4>::New();
1773  transformation->Identity();
1774  return true;
1775 }
constexpr unsigned char POINT_VISIBLE
Definition: CVConst.h:92
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::vector< PCLPointField > fields
std::string name
int count
pcl::PointCloud< PointRGBNormal > PointCloudRGBNormal
Definition: PCLCloud.h:29
pcl::TexMaterial PCLMaterial
Definition: PCLCloud.h:32
pcl::PointCloud< PointNT > PointCloudNormal
Definition: PCLCloud.h:25
pcl::PointCloud< PointT > PointCloudT
Definition: PCLCloud.h:17
pcl::PCLPointCloud2 PCLCloud
Definition: PCLCloud.h:34
pcl::PlanarPolygon< PointT > PCLPolygon
Definition: PCLCloud.h:36
pcl::PointCloud< PointRGB > PointCloudRGB
Definition: PCLCloud.h:21
pcl::TextureMesh PCLTextureMesh
Definition: PCLCloud.h:33
pcl::PolygonMesh PCLMesh
Definition: PCLCloud.h:31
#define TO_PCL_CLOUD
Definition: PCLConv.h:12
static PCLCloud::Ptr SetOrAdd(PCLCloud::Ptr firstCloud, PCLCloud::Ptr secondCloud)
Definition: cc2sm.cpp:712
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
Definition: CVLog.cpp:153
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static std::string ExtractDigitAlpha(const std::string &str)
Definition: CVTools.cpp:80
static std::string FromQString(const QString &qs)
Definition: CVTools.cpp:100
Array of compressed 3D normals (single index)
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
static void ConVertToPCLMaterial(ccMaterial::CShared inMaterial, PCLMaterial &outMaterial)
Definition: cc2sm.cpp:438
static std::string GetSimplifiedSFName(const std::string &ccSfName)
Definition: cc2sm.cpp:432
bool m_partialVisibility
Definition: cc2sm.h:140
PCLCloud::Ptr getFloatScalarField(const std::string &field_name) const
Definition: cc2sm.cpp:495
PCLCloud::Ptr getPointNormals() const
Definition: cc2sm.cpp:307
PCLCloud::Ptr getOneOf(Fields field) const
Definition: cc2sm.cpp:115
pcl::PointCloud< pcl::PointNormal >::Ptr getAsPointNormal() const
Converts the ccPointCloud to a 'pcl::PointNormal' cloud.
Definition: cc2sm.cpp:787
unsigned m_visibilityNum
Definition: cc2sm.h:141
PCLCloud::Ptr getVtkPolyDataAsSM(vtkPolyData *const polydata) const
Definition: cc2sm.cpp:849
PCLCloud::Ptr getColors() const
Definition: cc2sm.cpp:320
PCLCloud::Ptr getAsSM(std::list< std::string > &requested_fields) const
Definition: cc2sm.cpp:607
PCLPolygon::Ptr getPclPolygon(ccPolyline *polyline) const
Definition: cc2sm.cpp:1538
const ccPointCloud * m_cc_cloud
Associated cloud.
Definition: cc2sm.h:138
unsigned getvisibilityNum() const
Definition: cc2sm.cpp:111
bool getVtkPolyDataWithTextures(ccGenericMesh *mesh, vtkSmartPointer< vtkPolyData > &polydata, vtkSmartPointer< vtkMatrix4x4 > &transformation, std::vector< std::vector< Eigen::Vector2f >> &tex_coordinates)
Convert ccGenericMesh to vtkPolyData with texture coordinates Reuses getPclTextureMesh logic to ensur...
Definition: cc2sm.cpp:1566
PCLMesh::Ptr getVtkPolyDataAsPclMesh(vtkPolyData *const polydata) const
Definition: cc2sm.cpp:882
bool checkIfFieldExists(const std::string &field_name) const
Definition: cc2sm.cpp:591
PCLCloud::Ptr getXYZ() const
Definition: cc2sm.cpp:254
cc2smReader(bool showMode=false)
Definition: cc2sm.cpp:54
PCLMesh::Ptr getPclMesh(ccGenericMesh *mesh)
Definition: cc2sm.cpp:894
bool getPclCloud2(ccGenericMesh *mesh, PCLCloud &cloud) const
Definition: cc2sm.cpp:933
bool m_showMode
Definition: cc2sm.h:139
PCLCloud::Ptr getNormals() const
Definition: cc2sm.cpp:266
PCLTextureMesh::Ptr getPclTextureMesh(ccGenericMesh *mesh)
Definition: cc2sm.cpp:1375
PCLCloud::Ptr getGenericField(std::string field_name) const
Definition: cc2sm.cpp:82
bool getvtkScalars(vtkSmartPointer< vtkDataArray > &scalars, bool sfColors) const
Obtain the actual color for the input dataset as vtk scalars.
Definition: cc2sm.cpp:366
@ NORM_Y
Definition: cc2sm.h:63
@ COORD_Y
Definition: cc2sm.h:63
@ COORD_Z
Definition: cc2sm.h:63
@ COORD_X
Definition: cc2sm.h:63
@ NORM_Z
Definition: cc2sm.h:63
@ NORM_X
Definition: cc2sm.h:63
bool getVtkPolyDataFromMeshCloud(ccGenericMesh *mesh, vtkSmartPointer< vtkPolyData > &polydata) const
Convert ccGenericMesh to vtkPolyData using same logic as getPclCloud2 Direct conversion without PCL i...
Definition: cc2sm.cpp:1121
pcl::PointCloud< pcl::PointXYZ >::Ptr getXYZ2() const
Definition: cc2sm.cpp:218
pcl::PointCloud< pcl::PointXYZ >::Ptr getRawXYZ() const
Converts the ccPointCloud to a 'pcl::PointXYZ' cloud.
Definition: cc2sm.cpp:824
virtual bool colorsShown() const
Returns whether colors are shown or not.
virtual bool hasDisplayedScalarField() const
Returns whether an active scalar field is available or not.
virtual bool hasColors() const
Returns whether colors are enabled or not.
virtual bool sfShown() const
Returns whether active scalar field is visible.
virtual bool normalsShown() const
Returns whether normals are shown or not.
virtual bool hasNormals() const
Returns whether normals are enabled or not.
Generic mesh interface.
virtual bool materialsShown() const
Sets whether textures/material should be displayed or not.
virtual bool hasTextures() const =0
Returns whether textures are available for this mesh.
virtual bool triNormsShown() const
Returns whether per-triangle normals are shown or not.
virtual void getTriangleNormalIndexes(unsigned triangleIndex, int &i1, int &i2, int &i3) const =0
Returns a triplet of normal indexes for a given triangle (if any)
virtual const ccMaterialSet * getMaterialSet() const =0
virtual bool hasTriNormals() const =0
Returns whether the mesh has per-triangle normals.
virtual int getTriangleMtlIndex(unsigned triangleIndex) const =0
Returns a given triangle material indexes.
virtual ccGenericPointCloud * getAssociatedCloud() const =0
Returns the vertices cloud.
virtual void getTriangleTexCoordinates(unsigned triIndex, TexCoords2D *&tx1, TexCoords2D *&tx2, TexCoords2D *&tx3) const =0
Returns per-triangle texture coordinates (pointer to)
virtual NormsIndexesTableType * getTriNormsTable() const =0
Returns per-triangle normals shared array.
virtual bool hasMaterials() const =0
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual bool isVisibilityTableInstantiated() const
Returns whether the visibility array is allocated or not.
virtual VisibilityTableType & getTheVisibilityArray()
Returns associated visibility array.
std::vector< unsigned char > VisibilityTableType
Array of "visibility" information for each point.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Mesh (triangle) material.
Mesh (triangle) material.
Definition: ecvMaterial.h:28
QSharedPointer< const ccMaterial > CShared
Const + Shared type.
Definition: ecvMaterial.h:31
QSharedPointer< ccMaterial > Shared
Shared type.
Definition: ecvMaterial.h:33
Compressed normal vectors handler.
const CCVector3 & getNormal(unsigned normIndex) const
Returns the precomputed normal corresponding to a given compressed index.
static const CCVector3 & GetNormal(unsigned normIndex)
Static access to ccNormalVectors::getNormal.
static ccNormalVectors * GetUniqueInstance()
Returns unique instance.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
Definition: ecvObject.cpp:208
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
ColorsTableType * rgbColors() const
Returns pointer on RGB colors table.
const ecvColor::Rgb * getScalarValueColor(ScalarType d) const override
Returns color corresponding to a given scalar value.
NormsIndexesTableType * normals() const
Returns pointer on compressed normals indexes table.
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
bool hasNormals() const override
Returns whether normals are enabled or not.
int getCurrentDisplayedScalarFieldIndex() const
Returns the currently displayed scalar field index (or -1 if none)
bool hasColors() const override
Returns whether colors are enabled or not.
const CCVector3 * getNormal(unsigned pointIndex) const override
If per-point normals are available, returns the one at a specific index.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
bool hasScalarFields() const override
Returns whether one or more scalar fields are instantiated.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
Colored polyline.
Definition: ecvPolyline.h:24
bool is2DMode() const
Returns whether the polyline is considered as 2D or 3D.
Definition: ecvPolyline.h:63
const ecvColor::Rgb * getValueColor(unsigned index) const
Shortcut to getColor.
virtual unsigned size() const =0
Returns the number of points.
virtual VerticesIndexes * getTriangleVertIndexes(unsigned triangleIndex)=0
Returns the indexes of the vertices of a given triangle.
virtual unsigned size() const =0
Returns the number of triangles.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
unsigned size() const override
Definition: PointCloudTpl.h:38
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
const CCVector3 * getPoint(unsigned index) const override
unsigned size() const override
Returns the number of points.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
const char * getName() const
Returns scalar field name.
Definition: ScalarField.h:43
RGB color structure.
Definition: ecvColorTypes.h:49
RGBA color structure.
static ecvDisplayTools * TheInstance()
virtual void toWorldPoint(const CCVector3d &input2D, CCVector3d &output3D)
double colors[3]
double normals[3]
normal_z rgb
normal_z scalar
std::string to_string(const T &n)
Definition: Common.h:20
2D texture coordinates
Triangle described by the indexes of its 3 vertices.
unsigned int getDimension() const