Save std::vector<FPointData> to disk in .ply format asynchronously. This function not only saves the spatial coordinates of each point in the point cloud but also saves the color information of each point, encoding the RGB color components in the output .ply file.
73{
74 if (points.empty())
75 {
76 return;
77 }
78
79 if (!FullFileName.EndsWith(TEXT(".ply")))
80 {
81 UE_LOG(LogTemp, Warning, TEXT("PointcloudUtilities.cpp: Invalid file format. Expecting filename to end with '.ply'!"));
82 return;
83 }
84
85 FString Directory = FPaths::GetPath(FullFileName);
86 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
87 if (!PlatformFile.DirectoryExists(*Directory))
88 {
89 if (!PlatformFile.CreateDirectoryTree(*Directory))
90 {
91 return;
92 }
93 }
94
95 AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [FullFileName, points]()
96 {
97
98 std::string header = "ply\nformat binary_little_endian 1.0\n"
99 "element vertex " + std::to_string(points.size()) + "\n"
100 "property float x\nproperty float y\nproperty float z\n"
101 "property uchar red\nproperty uchar green\nproperty uchar blue\n"
102 "end_header\n";
103
104
105 size_t numPoints = points.size();
106 size_t bufferSize = numPoints * (sizeof(float) * 3 + sizeof(uint8_t) * 3);
107 std::vector<char> buffer;
108 buffer.reserve(bufferSize);
109
110
111 for (const auto& point : points)
112 {
113
114 buffer.insert(buffer.end(), reinterpret_cast<const char*>(&point.X), reinterpret_cast<const char*>(&point.X) + sizeof(float));
115 buffer.insert(buffer.end(), reinterpret_cast<const char*>(&point.Y), reinterpret_cast<const char*>(&point.Y) + sizeof(float));
116 buffer.insert(buffer.end(), reinterpret_cast<const char*>(&point.Z), reinterpret_cast<const char*>(&point.Z) + sizeof(float));
117
118
119 uint8_t red = (point.RGB >> 16) & 0xFF;
120 uint8_t green = (point.RGB >> 8) & 0xFF;
121 uint8_t blue = point.RGB & 0xFF;
122 buffer.insert(buffer.end(), reinterpret_cast<const char*>(&red), reinterpret_cast<const char*>(&red) + sizeof(uint8_t));
123 buffer.insert(buffer.end(), reinterpret_cast<const char*>(&green), reinterpret_cast<const char*>(&green) + sizeof(uint8_t));
124 buffer.insert(buffer.end(), reinterpret_cast<const char*>(&blue), reinterpret_cast<const char*>(&blue) + sizeof(uint8_t));
125 }
126
127
128 std::ofstream plyFile(TCHAR_TO_UTF8(*FullFileName), std::ios::out | std::ios::binary);
129 if (!plyFile.is_open())
130 {
131 UE_LOG(LogTemp, Warning, TEXT("Failed to open file for writing: %s"), *FullFileName);
132 return;
133 }
134
135
136 plyFile.write(header.c_str(), header.size());
137
138
139 plyFile.write(buffer.data(), buffer.size());
140
141
142 plyFile.close();
143 });
144}