Petras,
Below is my C++ code for reading a point cloud with colors and adding it to a Rhino Doc.
Code to read a point cloud from file_name and make a colored point cloud:
// Converts block of pointcloud data to long string containing lines of X,Y,Z,R,G,B.
DLLEXPORT void read_cloud(wchar_t *file_name, char sep, double *xyz, uint32_t *colors, uint32_t &i,
char *memblock, int32_t block_size, uint64_t offset, size_t bufsize) {
static double pow10[17] = { 1., 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16 };
// Define variables for 3 doubles and fraction.
double r1 = 0, r2 = 0, r3 = 0, f;
// Define variables for 3 integers, number of bytes read from block and power of ten for fraction.
int32_t R = 0, G = 0, B = 0, n;
uint64_t ii, bytes_read = 0;
// Define variables for pointing to memblock and saving pointer location at start of parsing block.
const char *p, *p_start;
// Initialize flags for negating value and controlling continuing parsing.
bool neg = false;
// Set buffer size for 64 transfers.
//const size_t bufsize = max(4096, 2 << (int32_t)log2(block_size >> 6));
unique_ptr<char[]> buf(new char[bufsize]);
// Set file_size as streampos type.
streampos size_of_block = block_size;
// Set file offset to start of next block.
streampos file_offset = offset;
//
// Open file for binary read.
//
ifstream in_file(file_name, ios::binary | ios::in);
// Import block of point cloud.
if (in_file.is_open()) {
// Increase default buffer size. Improves write speed 4.5X, from 0.66 GB/s to 3.1 GB/s
in_file.rdbuf()->pubsetbuf(buf.get(), bufsize); // IMPORTANT
// Move file pointer to start of block.
in_file.seekg(file_offset);
// Read memblock of data into memory.
in_file.read(memblock, size_of_block);
// Parse each line of block.
p = memblock;
while (true) {
// Get pointer location at start.
p_start = p;
// Skip white space and convert first double at start of line.
while (*p == ' ' || *p == sep) { ++p; }
// Get possible minius sign at start.
if (*p == '-') { neg = true; ++p; }
// Convert digits before decimal points.
if (*p >= '0' && *p <= '9') {
r1 = (*p - '0'); ++p;
while (*p >= '0' && *p <= '9') { r1 = (r1*10.0) + (*p - '0'); ++p; }
}
// Get digits after decimal point.
if (*p == '.') {
// Advance pointer to first digit.
++p;
// If char is a number, add to f, advance pointer and power of ten.
if (*p >= '0' && *p <= '9') {
f = (*p - '0'); ++p; n = 1;
// Convert additional chars, advancing pointer and power of ten.
while (*p >= '0' && *p <= '9') { f = (f*10.0) + (*p - '0'); ++p; ++n; }
// Scale digits by power of 10 to make fraction.
r1 += f * pow10[n];
}
}
// Create negative result if minus sign was present and reset minus flag.
if (neg) { r1 = -r1; neg = false; }
//
// Skip white space and convert second double.
//
while (*p == ' ' || *p == sep) { ++p; }
if (*p == '-') { neg = true; ++p; }
if (*p >= '0' && *p <= '9') {
r2 = (*p - '0'); ++p;
while (*p >= '0' && *p <= '9') { r2 = (r2*10.0) + (*p - '0'); ++p; }
}
if (*p == '.') {
++p;
if (*p >= '0' && *p <= '9') {
f = (*p - '0'); ++p; n = 1;
while (*p >= '0' && *p <= '9') { f = (f*10.0) + (*p - '0'); ++p; ++n; }
r2 += f * pow10[n];
}
}
if (neg) { r2 = -r2; neg = false; }
//
// Skip white space and convert third double.
//
while (*p == ' ' || *p == sep) { ++p; }
if (*p == '-') { neg = true; ++p; }
if (*p >= '0' && *p <= '9') {
r3 = (*p - '0'); ++p;
while (*p >= '0' && *p <= '9') { r3 = (r3*10.0) + (*p - '0'); ++p; }
}
if (*p == '.') {
++p;
if (*p >= '0' && *p <= '9') {
f = (*p - '0'); ++p; n = 1;
while (*p >= '0' && *p <= '9') { f = (f*10.0) + (*p - '0'); ++p; ++n; }
r3 += f * pow10[n];
}
}
if (neg) { r3 = -r3; neg = false; }
//
// Skip white space and convert 3 RGB integers towards end of line.
//
while (*p == ' ' || *p == sep) { ++p; }
if (*p >= '0' && *p <= '9') {
R = (*p - '0'); ++p;
while (*p >= '0' && *p <= '9') { R = (R * 10) + (*p - '0'); ++p; }
}
while (*p == ' ' || *p == sep) { ++p; }
if (*p >= '0' && *p <= '9') {
G = (*p - '0'); ++p;
while (*p >= '0' && *p <= '9') { G = (G * 10) + (*p - '0'); ++p; }
}
while (*p == ' ' || *p == sep) { ++p; }
if (*p >= '0' && *p <= '9') {
B = (*p - '0'); ++p;
while (*p >= '0' && *p <= '9') { B = (B * 10) + (*p - '0'); ++p; }
}
// Store X,Y,Z at 3*i, 3*i+1, 3*i+2.
ii = 3 * i;
xyz[ii] = r1; xyz[++ii] = r2; xyz[++ii] = r3;
// Store 32-bit color in ABGR format.
colors[i++] = (((B << 8) + G) << 8) + R;
// Increment past \r\n at end of line.
while (*p == '\r' || *p == '\n') { ++p; }
// Increment bytes read counter.
bytes_read += p - p_start;
// If end of block reached, break out of while loop and return.
if (!bytes_read || bytes_read >= block_size) { break; }
}
}
}
DLLEXPORT void read_make_cloud(wchar_t *file_name, char sep, uint64_t bufsize, uint32_t doc_serial_number, uint32_t &pc_serial_number,
bool has_colors, int32_t nxyz, int32_t ncolors, uint32_t *block_sizes, uint64_t *offsets, int32_t &count, int32_t &duration_d1, int32_t &duration_d2, int32_t &duration_d3) {
chrono::steady_clock::time_point time1 = chrono::steady_clock::now();
int32_t i = 0;
uint32_t num0 = 0; uint32_t num1 = 0; uint32_t num2 = 0; uint32_t num3 = 0; uint32_t num4 = 0; uint32_t num5 = 0; uint32_t num6 = 0; uint32_t num7 = 0;
uint32_t num8 = 0; uint32_t num9 = 0; uint32_t num10 = 0; uint32_t num11 = 0; uint32_t num12 = 0; uint32_t num13 = 0; uint32_t num14 = 0; uint32_t num15 = 0;
double *xyz0 = new double[nxyz]; double *xyz1 = new double[nxyz]; double *xyz2 = new double[nxyz]; double *xyz3 = new double[nxyz];
double *xyz4 = new double[nxyz]; double *xyz5 = new double[nxyz]; double *xyz6 = new double[nxyz]; double *xyz7 = new double[nxyz];
double *xyz8 = new double[nxyz]; double *xyz9 = new double[nxyz]; double *xyz10 = new double[nxyz]; double *xyz11 = new double[nxyz];
double *xyz12 = new double[nxyz]; double *xyz13 = new double[nxyz]; double *xyz14 = new double[nxyz]; double *xyz15 = new double[nxyz];
uint32_t *colors0 = new uint32_t[ncolors]; uint32_t *colors1 = new uint32_t[ncolors]; uint32_t *colors2 = new uint32_t[ncolors]; uint32_t *colors3 = new uint32_t[ncolors];
uint32_t *colors4 = new uint32_t[ncolors]; uint32_t *colors5 = new uint32_t[ncolors]; uint32_t *colors6 = new uint32_t[ncolors]; uint32_t *colors7 = new uint32_t[ncolors];
uint32_t *colors8 = new uint32_t[ncolors]; uint32_t *colors9 = new uint32_t[ncolors]; uint32_t *colors10 = new uint32_t[ncolors]; uint32_t *colors11 = new uint32_t[ncolors];
uint32_t *colors12 = new uint32_t[ncolors]; uint32_t *colors13 = new uint32_t[ncolors]; uint32_t *colors14 = new uint32_t[ncolors]; uint32_t *colors15 = new uint32_t[ncolors];
char *memblock0 = new char[block_sizes[0]]; char *memblock1 = new char[block_sizes[1]]; char *memblock2 = new char[block_sizes[2]]; char *memblock3 = new char[block_sizes[3]];
char *memblock4 = new char[block_sizes[4]]; char *memblock5 = new char[block_sizes[5]]; char *memblock6 = new char[block_sizes[6]]; char *memblock7 = new char[block_sizes[7]];
char *memblock8 = new char[block_sizes[8]]; char *memblock9 = new char[block_sizes[9]]; char *memblock10 = new char[block_sizes[10]]; char *memblock11 = new char[block_sizes[11]];
char *memblock12 = new char[block_sizes[12]]; char *memblock13 = new char[block_sizes[13]]; char *memblock14 = new char[block_sizes[14]]; char *memblock15 = new char[block_sizes[15]];
// Read pointcloud data.
parallel_invoke(
[&] {parallel_invoke(
[&] { read_cloud(file_name, sep, xyz0, colors0, num0, memblock0, block_sizes[0], offsets[0], bufsize); },
[&] { read_cloud(file_name, sep, xyz1, colors1, num1, memblock1, block_sizes[1], offsets[1], bufsize); },
[&] { read_cloud(file_name, sep, xyz2, colors2, num2, memblock2, block_sizes[2], offsets[2], bufsize); },
[&] { read_cloud(file_name, sep, xyz3, colors3, num3, memblock3, block_sizes[3], offsets[3], bufsize); },
[&] { read_cloud(file_name, sep, xyz4, colors4, num4, memblock4, block_sizes[4], offsets[4], bufsize); },
[&] { read_cloud(file_name, sep, xyz5, colors5, num5, memblock5, block_sizes[5], offsets[5], bufsize); },
[&] { read_cloud(file_name, sep, xyz6, colors6, num6, memblock6, block_sizes[6], offsets[6], bufsize); },
[&] { read_cloud(file_name, sep, xyz7, colors7, num7, memblock7, block_sizes[7], offsets[7], bufsize); });
},
[&] {parallel_invoke(
[&] { read_cloud(file_name, sep, xyz8, colors8, num8, memblock8, block_sizes[8], offsets[8], bufsize); },
[&] { read_cloud(file_name, sep, xyz9, colors9, num9, memblock9, block_sizes[9], offsets[9], bufsize); },
[&] { read_cloud(file_name, sep, xyz10, colors10, num10, memblock10, block_sizes[10], offsets[10], bufsize); },
[&] { read_cloud(file_name, sep, xyz11, colors11, num11, memblock11, block_sizes[11], offsets[11], bufsize); },
[&] { read_cloud(file_name, sep, xyz12, colors12, num12, memblock12, block_sizes[12], offsets[12], bufsize); },
[&] { read_cloud(file_name, sep, xyz13, colors13, num13, memblock13, block_sizes[13], offsets[13], bufsize); },
[&] { read_cloud(file_name, sep, xyz14, colors14, num14, memblock14, block_sizes[14], offsets[14], bufsize); },
[&] { read_cloud(file_name, sep, xyz15, colors15, num15, memblock15, block_sizes[15], offsets[15], bufsize); });
}
);
chrono::steady_clock::time_point time2 = chrono::steady_clock::now();
duration_d1 = (int32_t)chrono::duration_cast<chrono::microseconds> (time2 - time1).count();
uint32_t nums[16] = { num0, num1, num2, num3, num4, num5, num6, num7, num8, num9, num10, num11, num12, num13, num14, num15 };
// Find total number of points in pointcloud.
for (i = 0; i < 16; i++) { count += nums[i]; }
// Add points and colors to pointcloud.
// Runs 40% slower if points & colors are added group by group because of increased cache misses.
// So all points and then all colors are added which is in the same order as their c-types declaration order.
// For points, set destination for memcpy to be Point Array in pointcloud.
// Initialize pointcloud with capacity of count+100 points.
ON_PointCloud pc = ON_PointCloud(count + 100);
ON_3dPoint *pdest = pc.m_P.Array();
pc.m_C.SetCapacity(count + 100);
// Use memcpy to add points to pointcloud.
int32_t m = sizeof(ON_3dPoint);
int32_t n1 = nums[0], n2 = n1 + nums[1], n3 = n2 + nums[2], n4 = n3 + nums[3], n5 = n4 + nums[4], n6 = n5 + nums[5], n7 = n6 + nums[6], n8 = n7 + nums[7],
n9 = n8 + nums[8], n10 = n9 + nums[9], n11 = n10 + nums[10], n12 = n11 + nums[11], n13 = n12 + nums[12], n14 = n13 + nums[13], n15 = n14 + nums[14];
parallel_invoke(
[&] {parallel_invoke(
[&] { ::memcpy(pdest, xyz0, nums[0] * m); },
[&] { ::memcpy(pdest + n1, xyz1, nums[1] * m); },
[&] { ::memcpy(pdest + n2, xyz2, nums[2] * m); },
[&] { ::memcpy(pdest + n3, xyz3, nums[3] * m); },
[&] { ::memcpy(pdest + n4, xyz4, nums[4] * m); },
[&] { ::memcpy(pdest + n5, xyz5, nums[5] * m); },
[&] { ::memcpy(pdest + n6, xyz6, nums[6] * m); },
[&] { ::memcpy(pdest + n7, xyz7, nums[7] * m); });
},
[&] {parallel_invoke(
[&] { ::memcpy(pdest + n8, xyz8, nums[8] * m); },
[&] { ::memcpy(pdest + n9, xyz9, nums[9] * m); },
[&] { ::memcpy(pdest + n10, xyz10, nums[10] * m); },
[&] { ::memcpy(pdest + n11, xyz11, nums[11] * m); },
[&] { ::memcpy(pdest + n12, xyz12, nums[12] * m); },
[&] { ::memcpy(pdest + n13, xyz13, nums[13] * m); },
[&] { ::memcpy(pdest + n14, xyz14, nums[14] * m); },
[&] { ::memcpy(pdest + n15, xyz15, nums[15] * m); });
},
[&] { if (has_colors) {
pc.m_C.SetCapacity(count + 100);
ON_Color *cdest = pc.m_C.Array();
int32_t m = sizeof(uint32_t);
parallel_invoke(
[&] {parallel_invoke(
[&] { ::memcpy(cdest, colors0, nums[0] * m); },
[&] { ::memcpy(cdest + n1, colors1, nums[1] * m); },
[&] { ::memcpy(cdest + n2, colors2, nums[2] * m); },
[&] { ::memcpy(cdest + n3, colors3, nums[3] * m); },
[&] { ::memcpy(cdest + n4, colors4, nums[4] * m); },
[&] { ::memcpy(cdest + n5, colors5, nums[5] * m); },
[&] { ::memcpy(cdest + n6, colors6, nums[6] * m); },
[&] { ::memcpy(cdest + n7, colors7, nums[7] * m); });
},
[&] {parallel_invoke(
[&] { ::memcpy(cdest + n8, colors8, nums[8] * m); },
[&] { ::memcpy(cdest + n9, colors9, nums[9] * m); },
[&] { ::memcpy(cdest + n10, colors10, nums[10] * m); },
[&] { ::memcpy(cdest + n11, colors11, nums[11] * m); },
[&] { ::memcpy(cdest + n12, colors12, nums[12] * m); },
[&] { ::memcpy(cdest + n13, colors13, nums[13] * m); },
[&] { ::memcpy(cdest + n14, colors14, nums[14] * m); },
[&] { ::memcpy(cdest + n15, colors15, nums[15] * m); });
});
pc.m_C.SetCount(count);
}
});
pc.m_P.SetCount(count);
chrono::steady_clock::time_point time3 = chrono::steady_clock::now();
// Get doc from RuntimeSerialNumber passed as uint_32_t.
CRhinoDoc *pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial_number);
// Add pointcloud to document.
// Use next line in order to take advantage of Rhino checks.
*const ON_PointCloud &cpc = *pc;
CRhinoPointCloudObject *pcObject = pDoc->AddPointCloudObject(cpc);
// Get RuntimeSerialNumber of pointcloud so it can be found in Python script.
pc_serial_number = pcObject ? pcObject->RuntimeSerialNumber() : 0;
chrono::steady_clock::time_point time4 = chrono::steady_clock::now();
// Remove memory created on heap to support operations.
delete[] xyz0, xyz1, xyz2, xyz3, xyz4, xyz5, xyz6, xyz7, xyz8, xyz9, xyz10, xyz11, xyz12, xyz13, xyz14, xyz14;
delete[] colors0, colors1, colors2, colors3, colors4, colors5, colors6, colors7, colors8, colors9, colors10, colors11, colors12, colors13, colors14, colors15;
delete[] memblock0, memblock1, memblock2, memblock3, memblock4, memblock5, memblock6, memblock7, memblock8, memblock9, memblock10, memblock11, memblock12, memblock13, memblock14, memblock15;
duration_d2 = (int32_t)chrono::duration_cast<chrono::microseconds> (time3 - time2).count();
duration_d3 = (int32_t)chrono::duration_cast<chrono::microseconds> (time4 - time3).count();
}
Hope this helps you. This code reads a colored point cloud and adds it to the Rhino Doc over 10X faster than Rhino Import does.
Regards,
Terry.