Visual Computing Library  devel
Loading...
Searching...
No Matches
face.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_IO_MESH_PLY_DETAIL_FACE_H
24#define VCL_IO_MESH_PLY_DETAIL_FACE_H
25
26#include "header.h"
27
28#include <vclib/io/file_type.h>
29#include <vclib/io/read.h>
30#include <vclib/io/write.h>
31
32#include <vclib/algorithms/mesh.h>
33#include <vclib/mesh.h>
34
35namespace vcl::detail {
36
37template<FaceMeshConcept MeshType, FaceConcept FaceType>
38void writePlyFaceIndices(
39 std::ostream& file,
40 PlyProperty p,
41 const MeshType& m,
42 const std::vector<uint>& vIndices,
43 const FaceType& f,
44 FileType format)
45{
46 using VertexType = MeshType::VertexType;
47
48 uint fsize = f.vertexNumber();
49 io::writeProperty(file, fsize, p.listSizeType, format);
50 for (const VertexType* v : f.vertices()) {
51 io::writeProperty(file, vIndices[m.index(v)], p.type, format);
52 }
53}
54
55template<FaceMeshConcept MeshType, FaceConcept FaceType>
56void setPlyFaceIndices(FaceType& f, MeshType& m, const std::vector<uint>& vids)
57{
58 bool splitFace = false;
59 // we have a polygonal mesh
60 if constexpr (FaceType::VERTEX_NUMBER < 0) {
61 // need to resize the face to the right number of verts
62 f.resizeVertices(vids.size());
63 }
64 else if (FaceType::VERTEX_NUMBER != vids.size()) {
65 // we have faces with static sizes (triangles), but we are loading faces
66 // with number of verts > 3. Need to split the face we are loading in n
67 // faces!
68 splitFace = true;
69 }
70
71 if (!splitFace) { // classic load, no split needed
72 uint i = 0;
73 for (uint i = 0; i < f.vertexNumber(); ++i) {
74 if (vids[i] >= m.vertexNumber()) {
75 throw MalformedFileException(
76 "Bad vertex index for face " + std::to_string(f.index()));
77 }
78 f.setVertex(i, vids[i]);
79 }
80 }
81 else { // split needed
82 addTriangleFacesFromPolygon(m, f, vids);
83 }
84}
85
86template<FaceMeshConcept MeshType, FaceConcept FaceType, typename Scalar>
87void setPlyFaceWedgeTexCoords(
88 FaceType& f,
89 MeshType& m,
90 const std::vector<uint>& vids,
91 const std::vector<std::pair<Scalar, Scalar>>& wedges)
92{
93 bool splitFace = false;
94 if (FaceType::VERTEX_NUMBER > 0 && FaceType::VERTEX_NUMBER != wedges.size())
95 splitFace = true;
96 if (!splitFace) {
97 for (uint i = 0; i < wedges.size(); ++i) {
98 f.wedgeTexCoord(i).u() = wedges[i].first;
99 f.wedgeTexCoord(i).v() = wedges[i].second;
100 }
101 }
102 else { // the face has been splitted
103 // for each triangle generated by the polygon
104 for (uint ff = m.index(f); ff < m.faceNumber(); ++ff) {
105 // for each vertex/wedge of the triangle
106 for (uint i = 0; i < m.face(ff).vertexNumber(); ++i) {
107 // get the position of this vertex in the original polygon
108 uint vid = m.index(m.face(ff).vertex(i));
109 auto it = std::find(vids.begin(), vids.end(), vid);
110 if (it == vids.end()) { // should never happen
111 // if this happens, it means that this triangle was not
112 // generated by the polygon
113 throw MalformedFileException(
114 "Bad vertex index for face " + std::to_string(ff));
115 }
116 // p is the position of the vertex in the polygon
117 uint p = it - vids.begin();
118 m.face(ff).wedgeTexCoord(i).u() = wedges[p].first;
119 m.face(ff).wedgeTexCoord(i).v() = wedges[p].second;
120 }
121 }
122 }
123}
124
125template<FaceMeshConcept MeshType, FaceConcept FaceType, typename Stream>
126void readPlyFaceProperty(
127 Stream& file,
128 MeshType& mesh,
129 FaceType& f,
130 PlyProperty p,
131 MeshInfo& loadedInfo,
132 std::endian end = std::endian::little)
133{
134 bool hasBeenRead = false;
135 std::vector<uint> vids; // contains the vertex ids of the actual face
136 if (p.name == ply::vertex_indices) { // loading vertex indices
137 uint fSize = io::readPrimitiveType<uint>(file, p.listSizeType, end);
138 loadedInfo.updateMeshType(fSize);
139 vids.resize(fSize);
140 for (uint i = 0; i < fSize; ++i) {
141 vids[i] = io::readPrimitiveType<size_t>(file, p.type, end);
142 }
143 hasBeenRead = true;
144 // will manage the case of loading a polygon in a triangle mesh
145 setPlyFaceIndices(f, mesh, vids);
146 }
147 if (p.name == ply::texcoord) { // loading wedge texcoords
148 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
150 using Scalar = FaceType::WedgeTexCoordType::ScalarType;
151 uint uvSize =
152 io::readPrimitiveType<uint>(file, p.listSizeType, end);
153 uint fSize = uvSize / 2;
154 std::vector<std::pair<Scalar, Scalar>> wedges(fSize);
155 for (uint i = 0; i < fSize; ++i) {
156 Scalar u = io::readPrimitiveType<Scalar>(file, p.type, end);
157 Scalar v = io::readPrimitiveType<Scalar>(file, p.type, end);
158 wedges[i].first = u;
159 wedges[i].second = v;
160 }
161 hasBeenRead = true;
162 setPlyFaceWedgeTexCoords(f, mesh, vids, wedges);
163 }
164 }
165 }
166 // loading texture id associated to ALL the wedges
167 if (p.name == ply::texnumber) {
168 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
170 uint n = io::readPrimitiveType<uint>(file, p.type, end);
171 hasBeenRead = true;
172 // in case the loaded polygon has been triangulated in the last
173 // n triangles of mesh
174 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
175 mesh.face(ff).textureIndex() = n;
176 }
177 }
178 }
179 }
180 // loading one of the normal components
181 if (p.name >= ply::nx && p.name <= ply::nz) {
182 if constexpr (HasPerFaceNormal<MeshType>) {
183 if (isPerFaceNormalAvailable(mesh)) {
184 using Scalar = FaceType::NormalType::ScalarType;
185 int a = p.name - ply::nx;
186 Scalar n = io::readPrimitiveType<Scalar>(file, p.type, end);
187 hasBeenRead = true;
188 // in case the loaded polygon has been triangulated in the last
189 // n triangles of mesh
190 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
191 mesh.face(ff).normal()[a] = n;
192 }
193 }
194 }
195 }
196 // loading one of the color components
197 if (p.name >= ply::red && p.name <= ply::alpha) {
198 if constexpr (HasPerFaceColor<MeshType>) {
199 if (isPerFaceColorAvailable(mesh)) {
200 int a = p.name - ply::red;
201 unsigned char c =
202 io::readPrimitiveType<unsigned char>(file, p.type, end);
203 hasBeenRead = true;
204 // in case the loaded polygon has been triangulated in the last
205 // n triangles of mesh
206 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
207 mesh.face(ff).color()[a] = c;
208 }
209 }
210 }
211 }
212 if (p.name == ply::quality) { // loading the quality component
213 if constexpr (HasPerFaceQuality<MeshType>) {
214 using QualityType = FaceType::QualityType;
215 if (isPerFaceQualityAvailable(mesh)) {
216 QualityType s =
217 io::readPrimitiveType<QualityType>(file, p.type, end);
218 hasBeenRead = true;
219 // in case the loaded polygon has been triangulated in the last
220 // n triangles of mesh
221 for (uint ff = mesh.index(f); ff < mesh.faceNumber(); ++ff) {
222 mesh.face(ff).quality() = s;
223 }
224 }
225 }
226 }
227 if (p.name == ply::unknown) {
228 if constexpr (HasPerFaceCustomComponents<MeshType>) {
229 if (mesh.hasPerFaceCustomComponent(p.unknownPropertyName)) {
230 io::readCustomComponent(
231 file, f, p.unknownPropertyName, p.type, end);
232 hasBeenRead = true;
233 }
234 }
235 }
236 // if nothing has been read, it means that there is some data we don't know
237 // we still need to read and discard what we read
238 if (!hasBeenRead) {
239 if (p.list) {
240 uint s = io::readPrimitiveType<int>(file, p.listSizeType, end);
241 for (uint i = 0; i < s; ++i)
242 io::readPrimitiveType<int>(file, p.type, end);
243 }
244 else {
245 io::readPrimitiveType<int>(file, p.type, end);
246 }
247 }
248}
249
250template<FaceConcept FaceType, MeshConcept MeshType>
251void readPlyFaceTxt(
252 std::istream& file,
253 FaceType& f,
254 MeshType& mesh,
255 MeshInfo& loadedInfo,
256 const std::list<PlyProperty>& faceProperties)
257{
258 Tokenizer spaceTokenizer = readAndTokenizeNextNonEmptyLine(file);
259 Tokenizer::iterator token = spaceTokenizer.begin();
260 for (const PlyProperty& p : faceProperties) {
261 if (token == spaceTokenizer.end()) {
262 throw MalformedFileException("Unexpected end of line.");
263 }
264 readPlyFaceProperty(token, mesh, f, p, loadedInfo);
265 }
266}
267
268template<FaceConcept FaceType, MeshConcept MeshType>
269void readPlyFaceBin(
270 std::istream& file,
271 FaceType& f,
272 MeshType& mesh,
273 MeshInfo& loadedInfo,
274 const std::list<PlyProperty>& faceProperties,
275 std::endian end)
276{
277 for (const PlyProperty& p : faceProperties) {
278 readPlyFaceProperty(file, mesh, f, p, loadedInfo, end);
279 }
280}
281
282template<FaceMeshConcept MeshType>
283void writePlyFaces(
284 std::ostream& file,
285 const PlyHeader& header,
286 const MeshType& mesh)
287{
288 using FaceType = MeshType::FaceType;
289
290 FileType format;
291 if (header.format() == ply::ASCII) {
292 format.isBinary = false;
293 }
294 else if (header.format() == ply::BINARY_BIG_ENDIAN) {
295 format.endian = std::endian::big;
296 }
297
298 // indices of vertices that do not consider deleted vertices
299 std::vector<uint> vIndices = mesh.vertexCompactIndices();
300
301 for (const FaceType& f : mesh.faces()) {
302 for (const PlyProperty& p : header.faceProperties()) {
303 bool hasBeenWritten = false;
304 if (p.name == ply::vertex_indices) {
305 detail::writePlyFaceIndices(file, p, mesh, vIndices, f, format);
306 hasBeenWritten = true;
307 }
308 if (p.name >= ply::nx && p.name <= ply::nz) {
309 if constexpr (HasPerFaceNormal<MeshType>) {
310 io::writeProperty(
311 file, f.normal()[p.name - ply::nx], p.type, format);
312 hasBeenWritten = true;
313 }
314 }
315 if (p.name >= ply::red && p.name <= ply::alpha) {
316 if constexpr (HasPerFaceColor<MeshType>) {
317 io::writeProperty(
318 file, f.color()[p.name - ply::red], p.type, format);
319 hasBeenWritten = true;
320 }
321 }
322 if (p.name == ply::quality) {
323 if constexpr (HasPerFaceQuality<MeshType>) {
324 io::writeProperty(file, f.quality(), p.type, format);
325 hasBeenWritten = true;
326 }
327 }
328 if (p.name == ply::texcoord) {
329 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
330 io::writeProperty(
331 file, f.vertexNumber() * 2, p.listSizeType, format);
332 for (const auto& tc : f.wedgeTexCoords()) {
333 io::writeProperty(file, tc.u(), p.type, format);
334 io::writeProperty(file, tc.v(), p.type, format);
335 }
336 hasBeenWritten = true;
337 }
338 }
339 if (p.name == ply::texnumber) {
340 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
341 io::writeProperty(file, f.textureIndex(), p.type, format);
342 hasBeenWritten = true;
343 }
344 }
345 if (p.name == ply::unknown) {
346 if constexpr (HasPerFaceCustomComponents<MeshType>) {
347 if (mesh.hasPerFaceCustomComponent(p.unknownPropertyName)) {
348 io::writeCustomComponent(
349 file, f, p.unknownPropertyName, p.type, format);
350 hasBeenWritten = true;
351 }
352 }
353 }
354 if (!hasBeenWritten) {
355 // be sure to write something if the header declares some
356 // property that is not in the mesh
357 io::writeProperty(file, 0, p.type, format);
358 }
359 }
360 if (!format.isBinary)
361 file << std::endl;
362 }
363}
364
365template<FaceMeshConcept MeshType, LoggerConcept LogType>
366void readPlyFaces(
367 std::istream& file,
368 const PlyHeader& header,
369 MeshType& mesh,
370 MeshInfo& loadedInfo,
371 LogType& log)
372{
373 using FaceType = MeshType::FaceType;
374 mesh.reserveFaces(header.numberFaces());
375
376 log.startProgress("Reading faces", header.numberFaces());
377
378 for (uint fid = 0; fid < header.numberFaces(); ++fid) {
379 uint ffid = mesh.addFace();
380 FaceType& f = mesh.face(ffid);
381 if (header.format() == ply::ASCII) {
382 detail::readPlyFaceTxt(
383 file, f, mesh, loadedInfo, header.faceProperties());
384 }
385 else {
386 std::endian end = header.format() == ply::BINARY_BIG_ENDIAN ?
387 std::endian::big :
388 std::endian::little;
389 detail::readPlyFaceBin(
390 file, f, mesh, loadedInfo, header.faceProperties(), end);
391 }
392
393 log.progress(fid);
394 }
395
396 log.endProgress();
397}
398
399} // namespace vcl::detail
400
401#endif // VCL_IO_MESH_PLY_DETAIL_FACE_H
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:833
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:592
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:713
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:476
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:84
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:92