ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FilamentGeometryBuffersBuilder.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 
9 
10 #include <LineSet.h>
11 #include <Octree.h>
12 #include <VoxelGrid.h>
13 #include <ecvBBox.h>
14 #include <ecvMesh.h>
15 #include <ecvOrientedBBox.h>
16 #include <ecvPointCloud.h>
17 
21 
22 namespace cloudViewer {
23 namespace visualization {
24 namespace rendering {
25 
26 namespace {
27 static const Eigen::Vector3d kDefaultVoxelColor(0.5, 0.5, 0.5);
28 
29 // Coordinates of 8 vertices in a cuboid (assume origin (0,0,0), size 1)
30 const static std::vector<Eigen::Vector3i> kCuboidVertexOffsets{
31  Eigen::Vector3i(0, 0, 0), Eigen::Vector3i(1, 0, 0),
32  Eigen::Vector3i(0, 1, 0), Eigen::Vector3i(1, 1, 0),
33  Eigen::Vector3i(0, 0, 1), Eigen::Vector3i(1, 0, 1),
34  Eigen::Vector3i(0, 1, 1), Eigen::Vector3i(1, 1, 1),
35 };
36 
37 // Vertex indices of 12 triangles in a cuboid, for right-handed manifold mesh
38 const static std::vector<Eigen::Vector3i> kCuboidTrianglesVertexIndices{
39  Eigen::Vector3i(0, 2, 1), Eigen::Vector3i(0, 1, 4),
40  Eigen::Vector3i(0, 4, 2), Eigen::Vector3i(5, 1, 7),
41  Eigen::Vector3i(5, 7, 4), Eigen::Vector3i(5, 4, 1),
42  Eigen::Vector3i(3, 7, 1), Eigen::Vector3i(3, 1, 2),
43  Eigen::Vector3i(3, 2, 7), Eigen::Vector3i(6, 4, 7),
44  Eigen::Vector3i(6, 7, 2), Eigen::Vector3i(6, 2, 4),
45 };
46 
47 // Vertex indices of 12 lines in a cuboid
48 const static std::vector<Eigen::Vector2i> kCuboidLinesVertexIndices{
53 };
54 
55 static void AddVoxelFaces(ccMesh& mesh,
56  const std::vector<Eigen::Vector3d>& vertices,
57  const Eigen::Vector3d& color) {
58  for (const Eigen::Vector3i& triangle_vertex_indices :
59  kCuboidTrianglesVertexIndices) {
60  int n = int(mesh.getVerticeSize());
61  mesh.addTriangle(Eigen::Vector3i(n, n + 1, n + 2));
62  mesh.addVertice(vertices[triangle_vertex_indices(0)]);
63  mesh.addVertice(vertices[triangle_vertex_indices(1)]);
64  mesh.addVertice(vertices[triangle_vertex_indices(2)]);
65  mesh.addVertexColor(color);
66  mesh.addVertexColor(color);
67  mesh.addVertexColor(color);
68  }
69 }
70 
71 static void AddLineFace(ccMesh& mesh,
72  const Eigen::Vector3d& start,
73  const Eigen::Vector3d& end,
74  const Eigen::Vector3d& half_width,
75  const Eigen::Vector3d& color) {
76  int n = int(mesh.getVerticeSize());
77  mesh.addTriangle(Eigen::Vector3i(n, n + 1, n + 3));
78  mesh.addTriangle(Eigen::Vector3i(n, n + 3, n + 2));
79  mesh.addVertice(start - half_width);
80  mesh.addVertice(start + half_width);
81  mesh.addVertice(end - half_width);
82  mesh.addVertice(end + half_width);
83  mesh.addVertexColor(color);
84  mesh.addVertexColor(color);
85  mesh.addVertexColor(color);
86  mesh.addVertexColor(color);
87 }
88 
89 static std::shared_ptr<ccMesh> CreateTriangleMeshFromVoxelGrid(
90  const geometry::VoxelGrid& voxel_grid) {
91  ccPointCloud* baseVertices = new ccPointCloud("vertices");
92  assert(baseVertices);
93  auto mesh = std::make_shared<ccMesh>(baseVertices);
94  auto num_voxels = voxel_grid.voxels_.size();
95  if (!baseVertices->reserve(static_cast<unsigned>(36 * num_voxels))) {
96  utility::LogError("not enough memory!");
97  }
98  if (!baseVertices->reserveTheRGBTable()) {
99  utility::LogError("not enough memory!");
100  }
101 
102  std::vector<Eigen::Vector3d>
103  vertices; // putting outside loop enables reuse
104  for (auto& it : voxel_grid.voxels_) {
105  vertices.clear();
106  const geometry::Voxel& voxel = it.second;
107  // 8 vertices in a voxel
108  const Eigen::Matrix3d scaled_rot =
109  voxel_grid.rotation_ * voxel_grid.voxel_size_;
110  Eigen::Vector3d base_vertex =
111  voxel_grid.origin_ +
112  scaled_rot * voxel.grid_index_.cast<double>();
113  for (const Eigen::Vector3i& vertex_offset : kCuboidVertexOffsets) {
114  vertices.push_back(base_vertex +
115  scaled_rot * vertex_offset.cast<double>());
116  }
117 
118  // Voxel color (applied to all points)
119  Eigen::Vector3d voxel_color;
120  if (voxel_grid.HasColors()) {
121  voxel_color = voxel.color_;
122  } else {
123  voxel_color = kDefaultVoxelColor;
124  }
125 
126  AddVoxelFaces(*mesh, vertices, voxel_color);
127  }
128 
129  // do some cleaning
130  {
131  baseVertices->shrinkToFit();
132  mesh->shrinkToFit();
134  if (normals) {
135  normals->shrink_to_fit();
136  }
137  }
138 
139  baseVertices->setEnabled(false);
140  // DGM: no need to lock it as it is only used by one mesh!
141  baseVertices->setLocked(false);
142  mesh->addChild(baseVertices);
143 
144  return mesh;
145 }
146 
147 static std::shared_ptr<ccMesh> CreateTriangleMeshFromOctree(
148  const geometry::Octree& octree) {
149  auto mesh = std::make_shared<ccMesh>();
150  if (!mesh->CreateInternalCloud()) {
151  utility::LogError("creating internal cloud failed!");
152  return nullptr;
153  }
154 
155  // We cannot have a real line with a width in pixels, we can only fake a
156  // line as rectangles. This value works nicely on the assumption that the
157  // octree fills about 80% of the viewing area.
158  double line_half_width = 0.0015 * octree.size_;
159 
160  auto f = [&mesh = *mesh, line_half_width](
161  const std::shared_ptr<geometry::OctreeNode>& node,
162  const std::shared_ptr<geometry::OctreeNodeInfo>& node_info)
163  -> bool {
164  Eigen::Vector3d base_vertex = node_info->origin_.cast<double>();
165  std::vector<Eigen::Vector3d> vertices;
166  for (const Eigen::Vector3i& vertex_offset : kCuboidVertexOffsets) {
167  vertices.push_back(base_vertex + vertex_offset.cast<double>() *
168  double(node_info->size_));
169  }
170 
171  auto leaf_node =
172  std::dynamic_pointer_cast<geometry::OctreeColorLeafNode>(node);
173  if (leaf_node) {
174  AddVoxelFaces(mesh, vertices, leaf_node->color_);
175  } else {
176  // We cannot have lines in a ccMesh, obviously, so fake them
177  // with two crossing planes.
178  for (const Eigen::Vector2i& line_vertex_indices :
179  kCuboidLinesVertexIndices) {
180  auto& start = vertices[line_vertex_indices(0)];
181  auto& end = vertices[line_vertex_indices(1)];
182  Eigen::Vector3d w(line_half_width, 0.0, 0.0);
183  // if (end - start).dot({1, 0, 0}) ~= 0, then use z, not x
184  if (std::abs(end.y() - start.y()) < 0.1 &&
185  std::abs(end.z() - start.z()) < 0.1) {
186  w = {0.0, 0.0, line_half_width};
187  }
188  AddLineFace(mesh, start, end, w, {0.0, 0.0, 0.0});
189 
190  w = {0.0, line_half_width, 0.0};
191  // if (end - start).dot({0, 1, 0}) ~= 0, then use z, not y
192  if (std::abs(end.x() - start.x()) < 0.1 &&
193  std::abs(end.z() - start.z()) < 0.1) {
194  w = {0.0, 0.0, line_half_width};
195  }
196  AddLineFace(mesh, start, end, w, {0.0, 0.0, 0.0});
197  }
198  }
199 
200  return false;
201  };
202 
203  octree.Traverse(f);
204 
205  return mesh;
206 }
207 
208 } // namespace
209 
211 public:
212  explicit TemporaryLineSetBuilder(std::shared_ptr<geometry::LineSet> lines)
213  : LineSetBuffersBuilder(*lines), lines_(lines) {}
214 
215 private:
216  std::shared_ptr<geometry::LineSet> lines_;
217 };
218 
220 public:
221  explicit TemporaryMeshBuilder(std::shared_ptr<ccMesh> mesh)
222  : TriangleMeshBuffersBuilder(*mesh), mesh_(mesh) {}
223 
224 private:
225  std::shared_ptr<ccMesh> mesh_;
226 };
227 
228 std::unique_ptr<GeometryBuffersBuilder> GeometryBuffersBuilder::GetBuilder(
229  const ccHObject& geometry) {
230  using GT = CV_TYPES::GeometryType;
231 
232  switch (geometry.getClassID()) {
233  case GT::MESH:
234  return std::make_unique<TriangleMeshBuffersBuilder>(
235  static_cast<const ccMesh&>(geometry));
236 
237  case GT::POINT_CLOUD:
238  return std::make_unique<PointCloudBuffersBuilder>(
239  static_cast<const ccPointCloud&>(geometry));
240 
241  case GT::LINESET:
242  return std::make_unique<LineSetBuffersBuilder>(
243  static_cast<const geometry::LineSet&>(geometry));
244  case GT::ORIENTED_BBOX: {
245  auto obb = static_cast<const ecvOrientedBBox&>(geometry);
247  lines->PaintUniformColor(obb.color_);
248  return std::make_unique<TemporaryLineSetBuilder>(lines);
249  }
250  case GT::BBOX: {
251  auto aabb = static_cast<const ccBBox&>(geometry);
252  auto lines =
254  lines->PaintUniformColor(aabb.GetColor());
255  return std::make_unique<TemporaryLineSetBuilder>(lines);
256  }
257  case GT::VOXEL_GRID: {
258  auto voxel_grid = static_cast<const geometry::VoxelGrid&>(geometry);
259  auto mesh = CreateTriangleMeshFromVoxelGrid(voxel_grid);
260  return std::make_unique<TemporaryMeshBuilder>(mesh);
261  }
262  case GT::POINT_OCTREE2: {
263  auto octree = static_cast<const geometry::Octree&>(geometry);
264  auto mesh = CreateTriangleMeshFromOctree(octree);
265  if (!mesh) {
266  return nullptr;
267  }
268  return std::make_unique<TemporaryMeshBuilder>(mesh);
269  }
270  default:
271  break;
272  }
273 
274  return nullptr;
275 }
276 
277 std::unique_ptr<GeometryBuffersBuilder> GeometryBuffersBuilder::GetBuilder(
278  const t::geometry::Geometry& geometry) {
280 
281  switch (geometry.GetGeometryType()) {
282  case GT::PointCloud: {
283  const t::geometry::PointCloud& pointcloud =
284  static_cast<const t::geometry::PointCloud&>(geometry);
285  if (pointcloud.IsGaussianSplat()) {
286  return std::make_unique<TGaussianSplatBuffersBuilder>(
287  pointcloud);
288  } else {
289  return std::make_unique<TPointCloudBuffersBuilder>(pointcloud);
290  }
291  }
292  case GT::TriangleMesh:
293  return std::make_unique<TMeshBuffersBuilder>(
294  static_cast<const t::geometry::TriangleMesh&>(geometry));
295  case GT::LineSet:
296  return std::make_unique<TLineSetBuffersBuilder>(
297  static_cast<const t::geometry::LineSet&>(geometry));
298  default:
299  break;
300  }
301 
302  return nullptr;
303 }
304 
306  size_t size,
307  void* user_ptr) {
308  free(buffer);
309 }
310 
311 } // namespace rendering
312 } // namespace visualization
313 } // namespace cloudViewer
int size
math::float4 color
Array of compressed 3D normals (single index)
Bounding box structure.
Definition: ecvBBox.h:25
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
CV_CLASS_ENUM getClassID() const override
Returns class ID.
Definition: ecvHObject.h:232
Triangular mesh.
Definition: ecvMesh.h:35
NormsIndexesTableType * getTriNormsTable() const override
Returns per-triangle normals shared array.
Definition: ecvMesh.h:344
void addVertice(const Eigen::Vector3d &vertice)
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
unsigned int getVerticeSize() const
void shrinkToFit()
Removes unused capacity.
Definition: ecvMesh.h:302
void addVertexColor(const Eigen::Vector3d &color)
virtual void setLocked(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:117
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void shrinkToFit()
Removes unused capacity.
LineSet define a sets of lines in 3D. A typical application is to display the point cloud corresponde...
Definition: LineSet.h:29
static std::shared_ptr< LineSet > CreateFromOrientedBoundingBox(const ecvOrientedBBox &box)
Factory function to create a LineSet from an OrientedBoundingBox.
static std::shared_ptr< LineSet > CreateFromAxisAlignedBoundingBox(const ccBBox &box)
Factory function to create a LineSet from an ccBBox.
Octree datastructure.
Definition: Octree.h:250
VoxelGrid is a collection of voxels which are aligned in grid.
Definition: VoxelGrid.h:64
The base geometry class.
Definition: Geometry.h:23
GeometryType
Specifies possible geometry types.
Definition: Geometry.h:28
GeometryType GetGeometryType() const
Returns one of registered geometry types.
Definition: Geometry.h:79
A LineSet contains points and lines joining them and optionally attributes on the points and lines.
Definition: LineSet.h:85
A point cloud contains a list of 3D points.
Definition: PointCloud.h:82
A triangle mesh contains vertices and triangles.
Definition: TriangleMesh.h:98
static void DeallocateBuffer(void *buffer, size_t size, void *user_ptr)
static std::unique_ptr< GeometryBuffersBuilder > GetBuilder(const ccHObject &geometry)
double normals[3]
#define LogError(...)
Definition: Logging.h:60
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
GeometryType
Definition: CVTypes.h:101
@ POINT_CLOUD
Definition: CVTypes.h:104
@ VOXEL_GRID
Definition: CVTypes.h:151
@ LINESET
Definition: CVTypes.h:152
@ POINT_OCTREE2
Definition: CVTypes.h:157
@ BBOX
Definition: CVTypes.h:154
@ ORIENTED_BBOX
Definition: CVTypes.h:155
::ccPointCloud PointCloud
Definition: PointCloud.h:19
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
cloudViewer::DgmOctree * octree