23#ifndef VCL_LOAD_SAVE_PLY_DETAIL_FACE_H
24#define VCL_LOAD_SAVE_PLY_DETAIL_FACE_H
28#include <vclib/algorithms/mesh/face_topology.h>
29#include <vclib/exceptions/io.h>
30#include <vclib/io/file_type.h>
31#include <vclib/io/read.h>
32#include <vclib/io/write.h>
33#include <vclib/mesh/requirements.h>
34#include <vclib/misc/tokenizer.h>
36namespace vcl::detail {
38template<FaceMeshConcept MeshType, FaceConcept FaceType>
39void writePlyFaceIndices(
43 const std::vector<uint>& vIndices,
47 using VertexType = MeshType::VertexType;
49 uint fsize = f.vertexNumber();
50 io::writeProperty(file, fsize, p.listSizeType, format);
51 for (
const VertexType* v : f.
vertices()) {
52 io::writeProperty(file, vIndices[m.index(v)], p.type, format);
56template<FaceMeshConcept MeshType, FaceConcept FaceType>
57void setPlyFaceIndices(FaceType& f, MeshType& m,
const std::vector<uint>& vids)
59 bool splitFace =
false;
61 if constexpr (FaceType::VERTEX_NUMBER < 0) {
63 f.resizeVertices(vids.size());
65 else if (FaceType::VERTEX_NUMBER != vids.size()) {
74 for (uint i = 0; i < f.vertexNumber(); ++i) {
75 if (vids[i] >= m.vertexNumber()) {
76 throw MalformedFileException(
77 "Bad vertex index for face " + std::to_string(f.index()));
79 f.setVertex(i, vids[i]);
83 addTriangleFacesFromPolygon(m, f, vids);
87template<FaceMeshConcept MeshType, FaceConcept FaceType,
typename Scalar>
88void setPlyFaceWedgeTexCoords(
91 const std::vector<uint>& vids,
92 const std::vector<std::pair<Scalar, Scalar>>& wedges)
94 bool splitFace =
false;
95 if (FaceType::VERTEX_NUMBER > 0 && FaceType::VERTEX_NUMBER != wedges.size())
98 for (uint i = 0; i < wedges.size(); ++i) {
99 f.wedgeTexCoord(i).u() = wedges[i].first;
100 f.wedgeTexCoord(i).v() = wedges[i].second;
105 for (uint ff = m.index(f); ff < m.faceNumber(); ++ff) {
107 for (uint i = 0; i < m.face(ff).vertexNumber(); ++i) {
109 uint vid = m.index(m.face(ff).vertex(i));
110 auto it = std::find(vids.begin(), vids.end(), vid);
111 if (it == vids.end()) {
114 throw MalformedFileException(
115 "Bad vertex index for face " + std::to_string(ff));
118 uint p = it - vids.begin();
119 m.face(ff).wedgeTexCoord(i).u() = wedges[p].first;
120 m.face(ff).wedgeTexCoord(i).v() = wedges[p].second;
126template<FaceMeshConcept MeshType, FaceConcept FaceType,
typename Stream>
127void readPlyFaceProperty(
132 MeshInfo& loadedInfo,
133 std::endian end = std::endian::little)
135 bool hasBeenRead =
false;
136 std::vector<uint> vids;
137 if (p.name == ply::vertex_indices) {
138 uint fSize = io::readPrimitiveType<uint>(file, p.listSizeType, end);
139 loadedInfo.updateMeshType(fSize);
141 for (uint i = 0; i < fSize; ++i) {
142 vids[i] = io::readPrimitiveType<size_t>(file, p.type, end);
146 setPlyFaceIndices(f, mesh, vids);
148 if (p.name == ply::texcoord) {
149 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
151 using Scalar = FaceType::WedgeTexCoordType::ScalarType;
153 io::readPrimitiveType<uint>(file, p.listSizeType, end);
154 uint fSize = uvSize / 2;
155 std::vector<std::pair<Scalar, Scalar>> wedges(fSize);
156 for (uint i = 0; i < fSize; ++i) {
157 Scalar u = io::readPrimitiveType<Scalar>(file, p.type, end);
158 Scalar v = io::readPrimitiveType<Scalar>(file, p.type, end);
160 wedges[i].second = v;
163 setPlyFaceWedgeTexCoords(f, mesh, vids, wedges);
168 if (p.name == ply::texnumber) {
169 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
171 uint n = io::readPrimitiveType<uint>(file, p.type, end);
175 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
176 mesh.face(ff).textureIndex() = n;
182 if (p.name >= ply::nx && p.name <= ply::nz) {
183 if constexpr (HasPerFaceNormal<MeshType>) {
185 using Scalar = FaceType::NormalType::ScalarType;
186 int a = p.name - ply::nx;
187 Scalar n = io::readPrimitiveType<Scalar>(file, p.type, end);
191 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
192 mesh.face(ff).normal()[a] = n;
198 if (p.name >= ply::red && p.name <= ply::alpha) {
199 if constexpr (HasPerFaceColor<MeshType>) {
201 int a = p.name - ply::red;
203 io::readPrimitiveType<unsigned char>(file, p.type, end);
207 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
208 mesh.face(ff).color()[a] = c;
213 if (p.name == ply::quality) {
214 if constexpr (HasPerFaceQuality<MeshType>) {
215 using QualityType = FaceType::QualityType;
218 io::readPrimitiveType<QualityType>(file, p.type, end);
222 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
223 mesh.face(ff).quality() = s;
228 if (p.name == ply::unknown) {
229 if constexpr (HasPerFaceCustomComponents<MeshType>) {
230 if (mesh.hasPerFaceCustomComponent(p.unknownPropertyName)) {
231 io::readCustomComponent(
232 file, f, p.unknownPropertyName, p.type, end);
241 uint s = io::readPrimitiveType<int>(file, p.listSizeType, end);
242 for (uint i = 0; i < s; ++i)
243 io::readPrimitiveType<int>(file, p.type, end);
246 io::readPrimitiveType<int>(file, p.type, end);
251template<FaceConcept FaceType, MeshConcept MeshType>
256 MeshInfo& loadedInfo,
257 const std::list<PlyProperty>& faceProperties)
259 Tokenizer spaceTokenizer = readAndTokenizeNextNonEmptyLine(file);
260 Tokenizer::iterator token = spaceTokenizer.begin();
261 for (
const PlyProperty& p : faceProperties) {
262 if (token == spaceTokenizer.end()) {
263 throw MalformedFileException(
"Unexpected end of line.");
265 readPlyFaceProperty(token, mesh, f, p, loadedInfo);
269template<FaceConcept FaceType, MeshConcept MeshType>
274 MeshInfo& loadedInfo,
275 const std::list<PlyProperty>& faceProperties,
278 for (
const PlyProperty& p : faceProperties) {
279 readPlyFaceProperty(file, mesh, f, p, loadedInfo, end);
283template<FaceMeshConcept MeshType>
286 const PlyHeader& header,
287 const MeshType& mesh)
289 using FaceType = MeshType::FaceType;
292 if (header.format() == ply::ASCII) {
293 format.isBinary =
false;
295 else if (header.format() == ply::BINARY_BIG_ENDIAN) {
296 format.endian = std::endian::big;
300 std::vector<uint> vIndices = mesh.vertexCompactIndices();
302 for (
const FaceType& f : mesh.
faces()) {
303 for (
const PlyProperty& p : header.faceProperties()) {
304 bool hasBeenWritten =
false;
305 if (p.name == ply::vertex_indices) {
306 detail::writePlyFaceIndices(file, p, mesh, vIndices, f, format);
307 hasBeenWritten =
true;
309 if (p.name >= ply::nx && p.name <= ply::nz) {
310 if constexpr (HasPerFaceNormal<MeshType>) {
312 file, f.normal()[p.name - ply::nx], p.type, format);
313 hasBeenWritten =
true;
316 if (p.name >= ply::red && p.name <= ply::alpha) {
317 if constexpr (HasPerFaceColor<MeshType>) {
319 file, f.color()[p.name - ply::red], p.type, format);
320 hasBeenWritten =
true;
323 if (p.name == ply::quality) {
324 if constexpr (HasPerFaceQuality<MeshType>) {
325 io::writeProperty(file, f.quality(), p.type, format);
326 hasBeenWritten =
true;
329 if (p.name == ply::texcoord) {
330 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
332 file, f.vertexNumber() * 2, p.listSizeType, format);
333 for (
const auto& tc : f.wedgeTexCoords()) {
334 io::writeProperty(file, tc.u(), p.type, format);
335 io::writeProperty(file, tc.v(), p.type, format);
337 hasBeenWritten =
true;
340 if (p.name == ply::texnumber) {
341 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
342 io::writeProperty(file, f.textureIndex(), p.type, format);
343 hasBeenWritten =
true;
346 if (p.name == ply::unknown) {
347 if constexpr (HasPerFaceCustomComponents<MeshType>) {
348 if (mesh.hasPerFaceCustomComponent(p.unknownPropertyName)) {
349 io::writeCustomComponent(
350 file, f, p.unknownPropertyName, p.type, format);
351 hasBeenWritten =
true;
355 if (!hasBeenWritten) {
358 io::writeProperty(file, 0, p.type, format);
361 if (!format.isBinary)
366template<FaceMeshConcept MeshType, LoggerConcept LogType>
369 const PlyHeader& header,
371 MeshInfo& loadedInfo,
374 using FaceType = MeshType::FaceType;
375 mesh.reserveFaces(header.numberFaces());
377 log.startProgress(
"Reading faces", header.numberFaces());
379 for (uint fid = 0; fid < header.numberFaces(); ++fid) {
380 uint ffid = mesh.addFace();
381 FaceType& f = mesh.face(ffid);
382 if (header.format() == ply::ASCII) {
383 detail::readPlyFaceTxt(
384 file, f, mesh, loadedInfo, header.faceProperties());
387 std::endian end = header.format() == ply::BINARY_BIG_ENDIAN ?
390 detail::readPlyFaceBin(
391 file, f, mesh, loadedInfo, header.faceProperties(), end);
bool isPerFaceWedgeTexCoordsAvailable(const MeshType &m)
Returns true if the WedgeTexCoords component is available (enabled) in the Face element of the input ...
Definition face_requirements.h:571
bool isPerFaceNormalAvailable(const MeshType &m)
Returns true if the Normal component is available (enabled) in the Face element of the input mesh m.
Definition face_requirements.h:330
bool isPerFaceQualityAvailable(const MeshType &m)
Returns true if the Quality component is available (enabled) in the Face element of the input mesh m.
Definition face_requirements.h:451
bool isPerFaceColorAvailable(const MeshType &m)
Returns true if the Color component is available (enabled) in the Face element of the input mesh m.
Definition face_requirements.h:214
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:52
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:60