ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
RemoteFunctions.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 <Logging.h>
11 #include <ecvHObjectCaster.h>
12 
13 #include <Eigen/Geometry>
14 #include <zmq.hpp>
15 
16 #include "core/Dispatch.h"
17 #include "io/rpc/Connection.h"
18 #include "io/rpc/MessageUtils.h"
19 #include "io/rpc/Messages.h"
20 
21 using namespace cloudViewer::utility;
22 
23 namespace cloudViewer {
24 namespace io {
25 namespace rpc {
26 
27 bool SetPointCloud(const ccPointCloud& pcd,
28  const std::string& path,
29  int time,
30  const std::string& layer,
31  std::shared_ptr<ConnectionBase> connection) {
32  // TODO use SetMeshData here after switching to the new PointCloud class.
33  if (!pcd.hasPoints()) {
34  LogInfo("SetMeshData: point cloud is empty");
35  return false;
36  }
37 
39  msg.path = path;
40  msg.time = time;
41  msg.layer = layer;
42 
43  msg.data.vertices = messages::Array::FromPtr(
44  (PointCoordinateType*)pcd.getPoints().data(),
45  {int64_t(pcd.size()), 3});
46  if (pcd.hasNormals()) {
47  std::vector<CCVector3*> normals = pcd.getPointNormalsPtr();
48  msg.data.vertex_attributes["normals"] = messages::Array::FromPtr(
49  (PointCoordinateType*)(*normals.data()),
50  {int64_t(normals.size()), 3});
51  }
52  if (pcd.hasColors()) {
53  msg.data.vertex_attributes["colors"] = messages::Array::FromPtr(
54  (ColorCompType*)pcd.getPointColors().data(),
55  {int64_t(pcd.getPointColors().size()), 3});
56  }
57 
58  msgpack::sbuffer sbuf;
59  messages::Request request{msg.MsgId()};
60  msgpack::pack(sbuf, request);
61  msgpack::pack(sbuf, msg);
62 
63  zmq::message_t send_msg(sbuf.data(), sbuf.size());
64  if (!connection) {
65  connection = std::shared_ptr<Connection>(new Connection());
66  }
67  auto reply = connection->Send(send_msg);
68  return ReplyIsOKStatus(*reply);
69 }
70 
71 bool SetTriangleMesh(const ccMesh& mesh,
72  const std::string& path,
73  int time,
74  const std::string& layer,
75  std::shared_ptr<ConnectionBase> connection) {
76  // TODO use SetMeshData here after switching to the new TriangleMesh class.
77  if (!mesh.hasTriangles()) {
78  LogInfo("SetMeshData: triangle mesh is empty");
79  return false;
80  }
81 
83  msg.path = path;
84  msg.time = time;
85  msg.layer = layer;
86 
87  if (!mesh.getAssociatedCloud()) {
88  LogInfo("SetMeshData: triangle vertices is empty");
89  return false;
90  }
91 
92  ccPointCloud& associatedCloud =
94 
95  msg.data.vertices = messages::Array::FromPtr(
96  (PointCoordinateType*)associatedCloud.getPoints().data(),
97  {int64_t(associatedCloud.size()), 3});
98 
99  msg.data.faces = messages::Array::FromPtr(
100  (unsigned*)(*mesh.getTrianglesPtr()).data(),
101  {int64_t((*mesh.getTrianglesPtr()).size()), 3});
102  if (mesh.hasNormals()) {
103  std::vector<CCVector3*> normals = associatedCloud.getPointNormalsPtr();
104  msg.data.vertex_attributes["normals"] = messages::Array::FromPtr(
105  (PointCoordinateType*)(*normals.data()),
106  {int64_t(normals.size()), 3});
107  }
108  if (mesh.hasColors()) {
109  msg.data.vertex_attributes["colors"] = messages::Array::FromPtr(
110  (ColorCompType*)associatedCloud.getPointColors().data(),
111  {int64_t(associatedCloud.getPointColors().size()), 3});
112  }
113  if (mesh.hasTriNormals()) {
114  std::vector<CCVector3*> triNormals = mesh.getTriangleNormalsPtr();
115  msg.data.face_attributes["normals"] = messages::Array::FromPtr(
116  (PointCoordinateType*)(*triNormals.data()),
117  {int64_t(triNormals.size()), 3});
118  }
119  if (mesh.hasTriangleUvs()) {
120  msg.data.face_attributes["uvs"] = messages::Array::FromPtr(
121  (double*)mesh.triangle_uvs_.data(),
122  {int64_t(mesh.triangle_uvs_.size()), 2});
123  }
124  if (mesh.hasTextures()) {
125  int tex_id = 0;
126  for (const auto& image : mesh.textures_) {
127  if (!image.IsEmpty()) {
128  std::vector<int64_t> shape(
129  {image.height_, image.width_, image.num_of_channels_});
130  if (image.bytes_per_channel_ == sizeof(uint8_t)) {
131  msg.data.texture_maps[std::to_string(tex_id)] =
132  messages::Array::FromPtr(
133  (uint8_t*)image.data_.data(), shape);
134  } else if (image.bytes_per_channel_ == sizeof(float)) {
135  msg.data.texture_maps[std::to_string(tex_id)] =
136  messages::Array::FromPtr((float*)image.data_.data(),
137  shape);
138  } else if (image.bytes_per_channel_ == sizeof(double)) {
139  msg.data.texture_maps[std::to_string(tex_id)] =
140  messages::Array::FromPtr(
141  (double*)image.data_.data(), shape);
142  }
143  }
144  ++tex_id;
145  }
146  }
147 
148  msgpack::sbuffer sbuf;
149  messages::Request request{msg.MsgId()};
150  msgpack::pack(sbuf, request);
151  msgpack::pack(sbuf, msg);
152 
153  zmq::message_t send_msg(sbuf.data(), sbuf.size());
154  if (!connection) {
155  connection = std::shared_ptr<Connection>(new Connection());
156  }
157  auto reply = connection->Send(send_msg);
158  return ReplyIsOKStatus(*reply);
159 }
160 
162  const std::string& path,
163  int time,
164  const std::string& layer,
165  std::shared_ptr<ConnectionBase> connection) {
166  std::map<std::string, core::Tensor> vertex_attributes(
167  mesh.GetVertexAttr().begin(), mesh.GetVertexAttr().end());
168  std::map<std::string, core::Tensor> face_attributes(
169  mesh.GetTriangleAttr().begin(), mesh.GetTriangleAttr().end());
170 
171  std::string material_name;
172  std::map<std::string, float> material_scalar_attributes;
173  std::map<std::string, std::array<float, 4>> material_vector_attributes;
174  std::map<std::string, t::geometry::Image> texture_maps;
175  if (mesh.HasMaterial()) {
176  const auto& material = mesh.GetMaterial();
177  material_name = material.GetMaterialName();
178  material_scalar_attributes = std::map<std::string, float>(
179  material.GetScalarProperties().begin(),
180  material.GetScalarProperties().end());
181  for (const auto& it : material.GetVectorProperties()) {
182  std::array<float, 4> vec = {it.second(0), it.second(1),
183  it.second(2), it.second(3)};
184  material_vector_attributes[it.first] = vec;
185  }
186  texture_maps = std::map<std::string, t::geometry::Image>(
187  material.GetTextureMaps().begin(),
188  material.GetTextureMaps().end());
189  }
190 
191  messages::MeshData o3d_type;
192  o3d_type.SetO3DTypeToTriangleMesh();
193 
194  return SetMeshData(path, time, layer, mesh.GetVertexPositions(),
195  vertex_attributes, mesh.GetTriangleIndices(),
196  face_attributes, core::Tensor(),
197  std::map<std::string, core::Tensor>(), material_name,
198  material_scalar_attributes, material_vector_attributes,
199  texture_maps, o3d_type.o3d_type, connection);
200 }
201 
202 bool SetMeshData(const std::string& path,
203  int time,
204  const std::string& layer,
205  const core::Tensor& vertices,
206  const std::map<std::string, core::Tensor>& vertex_attributes,
207  const core::Tensor& faces,
208  const std::map<std::string, core::Tensor>& face_attributes,
209  const core::Tensor& lines,
210  const std::map<std::string, core::Tensor>& line_attributes,
211  const std::string& material,
212  const std::map<std::string, float>& material_scalar_attributes,
213  const std::map<std::string, std::array<float, 4>>&
214  material_vector_attributes,
215  const std::map<std::string, t::geometry::Image>& texture_maps,
216  const std::string& o3d_type,
217  std::shared_ptr<ConnectionBase> connection) {
219  msg.path = path;
220  msg.time = time;
221  msg.layer = layer;
222  msg.data.o3d_type = o3d_type;
223 
224  if (vertices.NumElements()) {
225  if (vertices.NumDims() != 2) {
226  LogError("SetMeshData: vertices ndim must be 2 but is {}",
227  vertices.NumDims());
228  }
229  if (vertices.GetDtype() != core::Float32 &&
230  vertices.GetDtype() != core::Float64) {
231  LogError(
232  "SetMeshData: vertices must have dtype Float32 or Float64 "
233  "but "
234  "is {}",
235  vertices.GetDtype().ToString());
236  }
237  msg.data.vertices = messages::Array::FromTensor(vertices);
238  }
239 
240  for (const auto& item : vertex_attributes) {
241  if (item.second.NumDims() >= 1) {
242  msg.data.vertex_attributes[item.first] =
243  messages::Array::FromTensor(item.second);
244  } else {
245  LogError("SetMeshData: Attribute {} has incompatible shape {}",
246  item.first, item.second.GetShape().ToString());
247  }
248  }
249 
250  if (faces.NumElements()) {
251  if (faces.GetDtype() != core::Int32 &&
252  faces.GetDtype() != core::Int64) {
253  LogError(
254  "SetMeshData: faces must have dtype Int32 or Int64 but "
255  "is {}",
256  faces.GetDtype().ToString());
257  } else if (faces.NumDims() != 2) {
258  LogError("SetMeshData: faces must have rank 2 but is {}",
259  faces.NumDims());
260  } else if (faces.GetShape()[1] < 3) {
261  LogError("SetMeshData: last dim of faces must be >=3 but is {}",
262  faces.GetShape()[1]);
263  } else {
264  msg.data.faces = messages::Array::FromTensor(faces);
265  }
266  }
267 
268  for (const auto& item : face_attributes) {
269  if (item.second.NumDims() >= 1) {
270  msg.data.face_attributes[item.first] =
271  messages::Array::FromTensor(item.second);
272  } else {
273  LogError(
274  "SetMeshData: Attribute {} has incompatible shape "
275  "{}",
276  item.first, item.second.GetShape().ToString());
277  }
278  }
279 
280  if (lines.NumElements()) {
281  if (lines.GetDtype() != core::Int32 &&
282  lines.GetDtype() != core::Int64) {
283  LogError(
284  "SetMeshData: lines must have dtype Int32 or Int64 but "
285  "is {}",
286  vertices.GetDtype().ToString());
287  } else if (lines.NumDims() != 2) {
288  LogError("SetMeshData: lines must have rank 2 but is {}",
289  lines.NumDims());
290  } else if (lines.GetShape()[1] < 2) {
291  LogError("SetMeshData: last dim of lines must be >=2 but is {}",
292  lines.GetShape()[1]);
293  } else {
294  msg.data.lines = messages::Array::FromTensor(lines);
295  }
296  }
297 
298  for (const auto& item : line_attributes) {
299  if (item.second.NumDims() >= 1) {
300  msg.data.line_attributes[item.first] =
301  messages::Array::FromTensor(item.second);
302  } else {
303  LogError(
304  "SetMeshData: Attribute {} has incompatible shape "
305  "{}",
306  item.first, item.second.GetShape().ToString());
307  }
308  }
309 
310  if (!material.empty()) {
311  msg.data.material = material;
312  msg.data.material_scalar_attributes = material_scalar_attributes;
313  for (const auto& item : material_vector_attributes) {
314  msg.data.material_vector_attributes[item.first] = item.second;
315  }
316  for (const auto& texture_map : texture_maps) {
317  if (texture_map.second.IsEmpty()) {
318  LogError("SetMeshData: Texture map {} is empty",
319  texture_map.first);
320  } else {
321  msg.data.texture_maps[texture_map.first] =
322  messages::Array::FromTensor(
323  texture_map.second.AsTensor());
324  }
325  }
326 
327  } else if (!material_scalar_attributes.empty() ||
328  !material_vector_attributes.empty() || !texture_maps.empty()) {
329  LogError("{}",
330  "SetMeshData: Please provide a material for the texture maps");
331  }
332 
333  {
334  std::string errstr;
335  if (!msg.data.CheckMessage(errstr)) {
336  LogError("SetMeshData: {}", errstr);
337  }
338  }
339 
340  msgpack::sbuffer sbuf;
341  messages::Request request{msg.MsgId()};
342  msgpack::pack(sbuf, request);
343  msgpack::pack(sbuf, msg);
344 
345  zmq::message_t send_msg(sbuf.data(), sbuf.size());
346  if (!connection) {
347  connection = std::shared_ptr<Connection>(new Connection());
348  }
349  auto reply = connection->Send(send_msg);
350  return ReplyIsOKStatus(*reply);
351 }
352 
354  const std::string& path,
355  int time,
356  const std::string& layer,
357  std::shared_ptr<ConnectionBase> connection) {
359  msg.path = path;
360  msg.time = time;
361  msg.layer = layer;
362 
363  // convert extrinsics
364  Eigen::Matrix3d R = camera.extrinsic_.block<3, 3>(0, 0);
365  Eigen::Vector3d t = camera.extrinsic_.block<3, 1>(0, 3);
366  Eigen::Quaterniond q(R);
367  msg.data.R[0] = q.x();
368  msg.data.R[1] = q.y();
369  msg.data.R[2] = q.z();
370  msg.data.R[3] = q.w();
371 
372  msg.data.t[0] = t[0];
373  msg.data.t[1] = t[1];
374  msg.data.t[2] = t[2];
375 
376  // convert intrinsics
377  if (camera.intrinsic_.IsValid()) {
378  msg.data.width = camera.intrinsic_.width_;
379  msg.data.height = camera.intrinsic_.height_;
380  msg.data.intrinsic_model = "PINHOLE";
381  msg.data.intrinsic_parameters.resize(4);
382  msg.data.intrinsic_parameters[0] =
383  camera.intrinsic_.intrinsic_matrix_(0, 0);
384  msg.data.intrinsic_parameters[1] =
385  camera.intrinsic_.intrinsic_matrix_(1, 1);
386  msg.data.intrinsic_parameters[2] =
387  camera.intrinsic_.intrinsic_matrix_(0, 2);
388  msg.data.intrinsic_parameters[3] =
389  camera.intrinsic_.intrinsic_matrix_(1, 2);
390  if (camera.intrinsic_.GetSkew() != 0.0) {
391  LogWarning(
392  "SetLegacyCamera: Nonzero skew parameteer in "
393  "PinholeCameraParameters will be ignored!");
394  }
395  }
396 
397  msgpack::sbuffer sbuf;
398  messages::Request request{msg.MsgId()};
399  msgpack::pack(sbuf, request);
400  msgpack::pack(sbuf, msg);
401 
402  zmq::message_t send_msg(sbuf.data(), sbuf.size());
403  if (!connection) {
404  connection = std::shared_ptr<Connection>(new Connection());
405  }
406  auto reply = connection->Send(send_msg);
407  return ReplyIsOKStatus(*reply);
408 }
409 
410 bool SetTime(int time, std::shared_ptr<ConnectionBase> connection) {
411  messages::SetTime msg;
412  msg.time = time;
413 
414  msgpack::sbuffer sbuf;
415  messages::Request request{msg.MsgId()};
416  msgpack::pack(sbuf, request);
417  msgpack::pack(sbuf, msg);
418 
419  zmq::message_t send_msg(sbuf.data(), sbuf.size());
420  if (!connection) {
421  connection = std::shared_ptr<Connection>(new Connection());
422  }
423  auto reply = connection->Send(send_msg);
424  return ReplyIsOKStatus(*reply);
425 }
426 
427 bool SetActiveCamera(const std::string& path,
428  std::shared_ptr<ConnectionBase> connection) {
430  msg.path = path;
431 
432  msgpack::sbuffer sbuf;
433  messages::Request request{msg.MsgId()};
434  msgpack::pack(sbuf, request);
435  msgpack::pack(sbuf, msg);
436 
437  zmq::message_t send_msg(sbuf.data(), sbuf.size());
438  if (!connection) {
439  connection = std::shared_ptr<Connection>(new Connection());
440  }
441  auto reply = connection->Send(send_msg);
442  return ReplyIsOKStatus(*reply);
443 }
444 
445 } // namespace rpc
446 } // namespace io
447 } // namespace cloudViewer
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::shared_ptr< core::Tensor > image
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Triangular mesh.
Definition: ecvMesh.h:35
triangleIndexesContainer * getTrianglesPtr() const
Definition: ecvMesh.h:279
std::vector< Eigen::Vector2d > triangle_uvs_
List of uv coordinates per triangle.
Definition: ecvMesh.h:625
bool hasColors() const override
Returns whether colors are enabled or not.
ccGenericPointCloud * getAssociatedCloud() const override
Returns the vertices cloud.
Definition: ecvMesh.h:143
bool hasTextures() const override
Returns whether textures are available for this mesh.
std::vector< CCVector3 * > getTriangleNormalsPtr() const
bool hasNormals() const override
Returns whether normals are enabled or not.
std::vector< cloudViewer::geometry::Image > textures_
Textures of the image.
Definition: ecvMesh.h:709
bool hasTriangleUvs() const
Definition: ecvMesh.h:717
bool hasTriNormals() const override
Returns whether the mesh has per-triangle normals.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
const ColorsTableType & getPointColors() const
std::vector< CCVector3 * > getPointNormalsPtr() const
bool hasColors() const override
Returns whether colors are enabled or not.
virtual bool hasPoints() const
Definition: GenericCloud.h:37
virtual bool hasTriangles() const
Definition: GenericMesh.h:60
std::vector< CCVector3 > & getPoints()
bool IsValid() const
Returns true iff both the width and height are greater than 0.
Contains both intrinsic and extrinsic pinhole camera parameters.
PinholeCameraIntrinsic intrinsic_
PinholeCameraIntrinsic object.
Eigen::Matrix4d_u extrinsic_
Camera extrinsic parameters.
std::string ToString() const
Definition: Dtype.h:65
int64_t NumDims() const
Definition: Tensor.h:1172
Dtype GetDtype() const
Definition: Tensor.h:1164
int64_t NumElements() const
Definition: Tensor.h:1170
SizeVector GetShape() const
Definition: Tensor.h:1127
bool HasMaterial() const
Check if a material has been applied to this Geometry with SetMaterial.
visualization::rendering::Material & GetMaterial()
Get material associated with this Geometry.
A triangle mesh contains vertices and triangles.
Definition: TriangleMesh.h:98
const TensorMap & GetVertexAttr() const
Getter for vertex_attr_ TensorMap. Used in Pybind.
Definition: TriangleMesh.h:133
const TensorMap & GetTriangleAttr() const
Getter for triangle_attr_ TensorMap. Used in Pybind.
Definition: TriangleMesh.h:159
const std::string & GetMaterialName() const
Get the name of the material.
Definition: Material.h:57
double normals[3]
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
const Dtype Int64
Definition: Dtype.cpp:47
const Dtype Float64
Definition: Dtype.cpp:43
const Dtype Int32
Definition: Dtype.cpp:46
const Dtype Float32
Definition: Dtype.cpp:42
bool SetTriangleMesh(const t::geometry::TriangleMesh &mesh, const std::string &path, int time, const std::string &layer, std::shared_ptr< ConnectionBase > connection)
bool SetPointCloud(const ccPointCloud &pcd, const std::string &path, int time, const std::string &layer, std::shared_ptr< ConnectionBase > connection)
bool SetActiveCamera(const std::string &path, std::shared_ptr< ConnectionBase > connection)
bool ReplyIsOKStatus(const zmq::message_t &msg)
Convenience function for checking if the message is an OK.
bool SetLegacyCamera(const camera::PinholeCameraParameters &camera, const std::string &path, int time, const std::string &layer, std::shared_ptr< ConnectionBase > connection)
bool SetMeshData(const std::string &path, int time, const std::string &layer, const core::Tensor &vertices, const std::map< std::string, core::Tensor > &vertex_attributes, const core::Tensor &faces, const std::map< std::string, core::Tensor > &face_attributes, const core::Tensor &lines, const std::map< std::string, core::Tensor > &line_attributes, const std::string &material, const std::map< std::string, float > &material_scalar_attributes, const std::map< std::string, std::array< float, 4 >> &material_vector_attributes, const std::map< std::string, t::geometry::Image > &texture_maps, const std::string &o3d_type, std::shared_ptr< ConnectionBase > connection)
bool SetTime(int time, std::shared_ptr< ConnectionBase > connection)
static const std::string path
Definition: PointCloud.cpp:59
Generic file read and write utility for python interface.
std::string to_string(const T &n)
Definition: Common.h:20
std::vector< double > intrinsic_parameters
Definition: Messages.h:443
std::array< double, 3 > t
translation
Definition: Messages.h:437
int width
image dimensions in pixels
Definition: Messages.h:446
std::array< double, 4 > R
rotation R as quaternion [x,y,z,w]
Definition: Messages.h:435
struct for storing MeshData, e.g., PointClouds, TriangleMesh, ..
Definition: Messages.h:255
std::map< std::string, float > material_scalar_attributes
Material scalar properties.
Definition: Messages.h:294
std::map< std::string, Array > vertex_attributes
Definition: Messages.h:268
std::map< std::string, Array > face_attributes
stores arbitrary attributes for each face
Definition: Messages.h:278
bool CheckMessage(std::string &errstr) const
Definition: Messages.h:368
std::map< std::string, std::array< float, 4 > > material_vector_attributes
Material vector[4] properties.
Definition: Messages.h:296
std::string material
Material for DrawableGeometry.
Definition: Messages.h:292
std::map< std::string, Array > line_attributes
stores arbitrary attributes for each line
Definition: Messages.h:289
std::map< std::string, Array > texture_maps
map of arrays that can be interpreted as textures
Definition: Messages.h:298
Array vertices
shape must be [num_verts,3]
Definition: Messages.h:265
CameraData data
The data to be set.
Definition: Messages.h:471
int32_t time
The time for which to return the data.
Definition: Messages.h:466
std::string path
Path defining the location in the scene tree.
Definition: Messages.h:464
std::string layer
The layer for which to return the data.
Definition: Messages.h:468
std::string path
Path defining the location in the scene tree.
Definition: Messages.h:397
int32_t time
The time associated with this data.
Definition: Messages.h:399
std::string layer
The layer for this data.
Definition: Messages.h:401
MeshData data
The data to be set.
Definition: Messages.h:404