Visual Computing Library  devel
Loading...
Searching...
No Matches
material.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2026 *
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_MATERIAL_H
24#define VCL_IO_MESH_PLY_DETAIL_MATERIAL_H
25
26#include "header.h"
27
28#include <vclib/io/read.h>
29#include <vclib/io/write.h>
30
31#include <vclib/mesh.h>
32
33namespace vcl::detail {
34
35template<typename Stream>
36void readPlyMaterialProperty(
37 Stream& file,
38 Material& mat,
39 PlyProperty p,
40 std::endian end = std::endian::little)
41{
42 bool hasBeenRead = false;
43 using enum Material::TextureType;
44
45 // lambda to load texture path
46 auto readTexturePath = [&](Material::TextureType textureType) {
47 uint size = io::readPrimitiveType<uint>(file, p.listSizeType, end);
48 std::string path;
49 path.resize(size);
50 for (uint i = 0; i < size; ++i) {
51 path[i] = io::readPrimitiveType<char>(file, p.type, end);
52 }
53 mat.textureDescriptor(textureType) = TextureDescriptor(path);
54 hasBeenRead = true;
55 };
56
57 if (p.name == ply::name) {
58 uint size = io::readPrimitiveType<uint>(file, p.listSizeType, end);
59 std::string name;
60 name.resize(size);
61 for (uint i = 0; i < size; ++i) {
62 name[i] = io::readPrimitiveType<char>(file, p.type, end);
63 }
64 mat.name() = name;
65 hasBeenRead = true;
66 }
67 else if (p.name >= ply::red && p.name <= ply::alpha) {
68 uint idx = p.name - ply::red;
69 mat.baseColor()[idx] =
70 io::readPrimitiveType<unsigned char>(file, p.type, end);
71 hasBeenRead = true;
72 }
73 else if (p.name == ply::metallic) {
74 mat.metallic() = io::readPrimitiveType<float>(file, p.type, end);
75 hasBeenRead = true;
76 }
77 else if (p.name == ply::roughness) {
78 mat.roughness() = io::readPrimitiveType<float>(file, p.type, end);
79 hasBeenRead = true;
80 }
81 else if (p.name >= ply::emissive_red && p.name <= ply::emissive_blue) {
82 uint idx = p.name - ply::emissive_red;
83 mat.emissiveColor()[idx] =
84 io::readPrimitiveType<unsigned char>(file, p.type, end);
85 hasBeenRead = true;
86 }
87 else if (p.name == ply::alpha_mode) {
88 mat.alphaMode() = static_cast<Material::AlphaMode>(
89 io::readPrimitiveType<uint>(file, p.type, end));
90 hasBeenRead = true;
91 }
92 else if (p.name == ply::alpha_cutoff) {
93 mat.alphaCutoff() = io::readPrimitiveType<float>(file, p.type, end);
94 hasBeenRead = true;
95 }
96 else if (p.name == ply::normal_scale) {
97 mat.normalScale() = io::readPrimitiveType<float>(file, p.type, end);
98 hasBeenRead = true;
99 }
100 else if (p.name == ply::occlusion_strength) {
101 mat.occlusionStrength() =
102 io::readPrimitiveType<float>(file, p.type, end);
103 hasBeenRead = true;
104 }
105 else if (p.name == ply::double_sided) {
106 mat.doubleSided() =
107 static_cast<bool>(io::readPrimitiveType<uint>(file, p.type, end));
108 hasBeenRead = true;
109 }
110 else if (p.name == ply::base_color_texture) {
111 readTexturePath(BASE_COLOR);
112 }
113 else if (p.name == ply::metallic_roughness_texture) {
114 readTexturePath(METALLIC_ROUGHNESS);
115 }
116 else if (p.name == ply::normal_texture) {
117 readTexturePath(NORMAL);
118 }
119 else if (p.name == ply::occlusion_texture) {
120 readTexturePath(OCCLUSION);
121 }
122 else if (p.name == ply::emissive_texture) {
123 readTexturePath(EMISSIVE);
124 }
125
126 // if nothing has been read, it means that there is some data we don't know
127 // we still need to read and discard what we read
128 if (!hasBeenRead) {
129 if (p.list) {
130 uint s = io::readPrimitiveType<uint>(file, p.listSizeType, end);
131 for (uint i = 0; i < s; ++i)
132 io::readPrimitiveType<int>(file, p.type, end);
133 }
134 else {
135 io::readPrimitiveType<int>(file, p.type, end);
136 }
137 }
138}
139
140inline void readPlyMaterialTxt(
141 std::istream& file,
142 Material& mat,
143 const std::list<PlyProperty>& matProperties)
144{
145 Tokenizer spaceTokenizer = readAndTokenizeNextNonEmptyLine(file);
146 Tokenizer::iterator token = spaceTokenizer.begin();
147 for (const PlyProperty& p : matProperties) {
148 if (token == spaceTokenizer.end()) {
149 throw MalformedFileException("Unexpected end of line.");
150 }
151 readPlyMaterialProperty(token, mat, p);
152 }
153}
154
155inline void readPlyMaterialBin(
156 std::istream& file,
157 Material& mat,
158 const std::list<PlyProperty>& matProperties,
159 std::endian end)
160{
161 for (const PlyProperty& p : matProperties) {
162 readPlyMaterialProperty(file, mat, p, end);
163 }
164}
165
166template<MeshConcept MeshType>
167void writePlyMaterials(
168 std::ostream& file,
169 const PlyHeader& header,
170 const MeshType& mesh)
171{
172 FileType format;
173 if (header.format() == ply::ASCII) {
174 format.isBinary = false;
175 }
176 else if (header.format() == ply::BINARY_BIG_ENDIAN) {
177 format.endian = std::endian::big;
178 }
179
180 for (const Material& mat : mesh.materials()) {
181 using enum Material::TextureType;
182 for (const PlyProperty& p : header.materialProperties()) {
183 bool hasBeenWritten = false;
184
185 // lambda to write texture path
186 auto writeTexturePath = [&](Material::TextureType textureType) {
187 const std::string& path =
188 mat.textureDescriptor(textureType).path();
189 io::writeProperty(file, path.size(), p.listSizeType, format);
190 for (const char& c : path) {
191 io::writeProperty(file, c, p.type, format);
192 }
193 hasBeenWritten = true;
194 };
195
196 if (p.name == ply::name) {
197 const std::string& name = mat.name();
198 io::writeProperty(file, name.size(), p.listSizeType, format);
199 for (const char& c : name) {
200 io::writeProperty(file, c, p.type, format);
201 }
202 hasBeenWritten = true;
203 }
204 else if (p.name >= ply::red && p.name <= ply::alpha) {
205 io::writeProperty(
206 file, mat.baseColor()[p.name - ply::red], p.type, format);
207 hasBeenWritten = true;
208 }
209 else if (p.name == ply::metallic) {
210 io::writeProperty(file, mat.metallic(), p.type, format);
211 hasBeenWritten = true;
212 }
213 else if (p.name == ply::roughness) {
214 io::writeProperty(file, mat.roughness(), p.type, format);
215 hasBeenWritten = true;
216 }
217 else if (
218 p.name >= ply::emissive_red && p.name <= ply::emissive_blue) {
219 io::writeProperty(
220 file,
221 mat.emissiveColor()[p.name - ply::emissive_red],
222 p.type,
223 format);
224 hasBeenWritten = true;
225 }
226 else if (p.name == ply::alpha_mode) {
227 io::writeProperty(
228 file, toUnderlying(mat.alphaMode()), p.type, format);
229 hasBeenWritten = true;
230 }
231 else if (p.name == ply::alpha_cutoff) {
232 io::writeProperty(file, mat.alphaCutoff(), p.type, format);
233 hasBeenWritten = true;
234 }
235 else if (p.name == ply::normal_scale) {
236 io::writeProperty(file, mat.normalScale(), p.type, format);
237 hasBeenWritten = true;
238 }
239 else if (p.name == ply::occlusion_strength) {
240 io::writeProperty(
241 file, mat.occlusionStrength(), p.type, format);
242 hasBeenWritten = true;
243 }
244 else if (p.name == ply::double_sided) {
245 io::writeProperty(
246 file, static_cast<uint>(mat.doubleSided()), p.type, format);
247 hasBeenWritten = true;
248 }
249 else if (p.name == ply::base_color_texture) {
250 writeTexturePath(BASE_COLOR);
251 }
252 else if (p.name == ply::metallic_roughness_texture) {
253 writeTexturePath(METALLIC_ROUGHNESS);
254 }
255 else if (p.name == ply::normal_texture) {
256 writeTexturePath(NORMAL);
257 }
258 else if (p.name == ply::occlusion_texture) {
259 writeTexturePath(OCCLUSION);
260 }
261 else if (p.name == ply::emissive_texture) {
262 writeTexturePath(EMISSIVE);
263 }
264
265 if (!hasBeenWritten) {
266 // be sure to write something if the header declares some
267 // property that is not in the mesh
268 io::writeProperty(file, 0, p.type, format);
269 }
270 }
271 if (!format.isBinary)
272 file << std::endl;
273 }
274}
275
276template<MeshConcept MeshType, LoggerConcept LogType>
277void readPlyMaterials(
278 std::istream& file,
279 const PlyHeader& header,
280 MeshType& mesh,
281 LogType& log)
282{
283 log.startProgress("Reading materials", header.materialCount());
284
285 for (uint mid = 0; mid < header.materialCount(); ++mid) {
286 Material mat;
287 if (header.format() == ply::ASCII) {
288 detail::readPlyMaterialTxt(file, mat, header.materialProperties());
289 }
290 else {
291 std::endian end = header.format() == ply::BINARY_BIG_ENDIAN ?
292 std::endian::big :
293 std::endian::little;
294 detail::readPlyMaterialBin(
295 file, mat, header.materialProperties(), end);
296 }
297 mesh.pushMaterial(mat);
298
299 log.progress(mid);
300 }
301
302 log.endProgress();
303}
304
305} // namespace vcl::detail
306
307#endif // VCL_IO_MESH_PLY_DETAIL_MATERIAL_H
TextureType
Defines the types of textures used in the PBR material model.
Definition material.h:62
AlphaMode
Defines the alpha rendering mode of the material.
Definition material.h:50