GCC Code Coverage Report


Directory: ./
File: mesh/mesh_baking.cpp
Date: 2026-09-27 03:10:11
Exec Total Coverage
Lines: 0 138 0.0%
Functions: 0 9 0.0%
Branches: 0 282 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/mesh/mesh_baking.h>
41 #include <geogram/mesh/mesh.h>
42 #include <geogram/mesh/mesh_geometry.h>
43 #include <geogram/mesh/mesh_AABB.h>
44 #include <geogram/image/image_rasterizer.h>
45 #include <geogram/points/kd_tree.h>
46
47 namespace {
48 using namespace GEO;
49
50 /**
51 * \brief Reads a (facet or vertex or facet corner) vector attribute
52 * as a color.
53 * \details If the vector attribute has less components than 4, then
54 * the additional color components are set to (0 + \p bias)* \p scale.
55 * If the vector attribute has more components than 4, then the additional
56 * attribute components are ignored.
57 * \param[out] C the color
58 * \param[in] attribute a reference to the attribute.
59 * \param[in] attrib_loc one of MESH_FACETS,MESH_VERTICES,MESH_FACET_CORNERS
60 * \param[in] f the facet
61 * \param[in] c the facet corner
62 * \param[in] v the vertex
63 * \param[in] bias optional value to be added to the attribute values
64 * before storing them into the color components.
65 * \param[in] scale optional value that scales the attribute values
66 * after \p bias is added and before storing them into the color
67 * components.
68 */
69 ✗ inline void get_attribute_as_color(
70 Color& C,
71 Attribute<double>& attribute,
72 MeshElementsFlags attrib_loc,
73 index_t f, index_t c, index_t v,
74 double bias = 0.0,
75 double scale = 1.0
76 ) {
77 index_t dim = attribute.dimension();
78 index_t base = NO_INDEX;
79 ✗ if(attrib_loc == MESH_FACETS) {
80 ✗ base = f*dim;
81 ✗ } else if(attrib_loc == MESH_VERTICES) {
82 ✗ base = v*dim;
83 ✗ } else if(attrib_loc == MESH_FACET_CORNERS) {
84 ✗ base = c*dim;
85 } else {
86 ✗ geo_assert_not_reached;
87 }
88 ✗ C.set_r(attribute[base]);
89 ✗ C.set_g((dim >= 2) ? attribute[base+1] : 0.0);
90 ✗ C.set_b((dim >= 3) ? attribute[base+2] : 0.0);
91 ✗ C.set_a((dim >= 4) ? attribute[base+3] : 0.0);
92
93 ✗ C.set_r(scale*(C.r() + bias));
94 ✗ C.set_g(scale*(C.g() + bias));
95 ✗ C.set_b(scale*(C.b() + bias));
96 ✗ C.set_a(scale*(C.a() + bias));
97 ✗ }
98 }
99
100 namespace GEO {
101
102 /**************************************************************************/
103
104 ✗ void bake_mesh_facet_normals(Mesh* mesh, Image* target) {
105 Attribute<double> tex_coord;
106 ✗ tex_coord.bind_if_is_defined(
107 ✗ mesh->facet_corners.attributes(), "tex_coord"
108 );
109 ✗ geo_assert(tex_coord.is_bound() && tex_coord.dimension() == 2);
110 ✗ ImageRasterizer rasterizer(target);
111 ✗ for(index_t f: mesh->facets) {
112 ✗ vec3 N = normalize(Geom::mesh_facet_normal(*mesh, f));
113 ✗ Color C = 0.5*Color(N.x+1.0, N.y+1.0, N.z+1.0, 2.0);
114 index_t c1 = mesh->facets.corners_begin(f);
115 vec2 p1(tex_coord[2*c1], tex_coord[2*c1+1]);
116 ✗ for(index_t c2 = c1+1;
117 ✗ c2+1 < mesh->facets.corners_end(f); ++c2) {
118 index_t c3 = c2+1;
119 vec2 p2(tex_coord[2*c2], tex_coord[2*c2+1]);
120 vec2 p3(tex_coord[2*c3], tex_coord[2*c3+1]);
121 ✗ rasterizer.triangle(
122 p1, C,
123 p2, C,
124 p3, C
125 );
126 }
127 }
128 ✗ }
129
130 /**************************************************************************/
131
132 ✗ void bake_mesh_vertex_normals(Mesh* mesh, Image* normal_map) {
133
134 // Step 1: compute vertex normals.
135 Attribute<double> N;
136 ✗ N.create_vector_attribute(mesh->vertices.attributes(), "N", 3);
137 ✗ for(index_t v: mesh->vertices) {
138 ✗ N[3*v] = 0.0;
139 ✗ N[3*v+1] = 0.0;
140 ✗ N[3*v+2] = 0.0;
141 }
142 ✗ for(index_t f: mesh->facets) {
143 ✗ vec3 Nf = GEO::Geom::mesh_facet_normal(*mesh, f);
144 ✗ for(index_t corner : mesh->facets.corners(f)) {
145 index_t v = mesh->facet_corners.vertex(corner);
146 ✗ N[3*v] += Nf.x;
147 ✗ N[3*v+1] += Nf.y ;
148 ✗ N[3*v+2] += Nf.z;
149 }
150 }
151 ✗ for(index_t v: mesh->vertices) {
152 vec3 Nf(N[3*v], N[3*v+1], N[3*v+2]);
153 ✗ Nf = normalize(Nf);
154 ✗ N[3*v] = Nf.x;
155 ✗ N[3*v+1] = Nf.y;
156 ✗ N[3*v+2] = Nf.z;
157 }
158
159 // Step 2: bake interpolated vertex normals.
160 ✗ bake_mesh_attribute(mesh, normal_map, N);
161
162 // Step 3: normalize interpolated normals.
163 ✗ FOR(y, normal_map->height()) {
164 ✗ FOR(x, normal_map->width()) {
165 double* pix = normal_map->pixel_base_float64_ptr(x,y);
166 vec3 Npix(pix);
167 ✗ Npix = normalize(Npix);
168 ✗ pix[0] = Npix.x;
169 ✗ pix[1] = Npix.y;
170 ✗ pix[2] = Npix.z;
171 }
172 }
173 ✗ N.unbind();
174 ✗ mesh->vertices.attributes().delete_attribute_store("N");
175 ✗ }
176
177 /**************************************************************************/
178
179 ✗ void bake_mesh_attribute(
180 Mesh* mesh, Image* target, Attribute<double>& attribute,
181 double bias, double scale
182 ) {
183 Attribute<double> tex_coord;
184 ✗ tex_coord.bind_if_is_defined(
185 ✗ mesh->facet_corners.attributes(), "tex_coord"
186 );
187 ✗ geo_assert(tex_coord.is_bound() && tex_coord.dimension() == 2);
188 ✗ geo_assert(attribute.is_bound());
189
190 MeshElementsFlags attrib_loc = MESH_NONE;
191
192 ✗ if(attribute.manager() == &mesh->vertices.attributes()) {
193 attrib_loc = MESH_VERTICES;
194 ✗ } else if(attribute.manager() == &mesh->facets.attributes()) {
195 attrib_loc = MESH_FACETS;
196 ✗ } else if(attribute.manager() == &mesh->facet_corners.attributes()) {
197 attrib_loc = MESH_FACET_CORNERS;
198 } else {
199 // Attribute is not a vertex/facet/facet_corner attribute
200 // or it is bound to a different mesh.
201 ✗ geo_assert_not_reached;
202 }
203
204 ✗ ImageRasterizer rasterizer(target);
205 ✗ for(index_t f: mesh->facets) {
206 Color C1,C2,C3;
207 index_t c1 = mesh->facets.corners_begin(f);
208 vec2 p1(tex_coord[2*c1], tex_coord[2*c1+1]);
209 index_t v1 = mesh->facet_corners.vertex(c1);
210 ✗ get_attribute_as_color(
211 C1, attribute, attrib_loc, f, c1, v1, bias, scale
212 );
213 ✗ for(index_t c2 = c1+1;
214 ✗ c2+1 < mesh->facets.corners_end(f); ++c2) {
215 index_t c3 = c2+1;
216 vec2 p2(tex_coord[2*c2], tex_coord[2*c2+1]);
217 vec2 p3(tex_coord[2*c3], tex_coord[2*c3+1]);
218 index_t v2 = mesh->facet_corners.vertex(c2);
219 index_t v3 = mesh->facet_corners.vertex(c3);
220 ✗ get_attribute_as_color(
221 C2, attribute, attrib_loc, f, c2, v2, bias, scale
222 );
223 ✗ get_attribute_as_color(
224 C3, attribute, attrib_loc, f, c3, v3, bias, scale
225 );
226 ✗ rasterizer.triangle(
227 p1, C1,
228 p2, C2,
229 p3, C3
230 );
231 }
232 }
233 ✗ }
234
235 /**************************************************************************/
236
237 ✗ void bake_mesh_geometry(Mesh* mesh, Image* target, bool clear) {
238 ✗ geo_assert(target->component_encoding() == Image::FLOAT64);
239 Attribute<double> point;
240 ✗ point.bind_if_is_defined(mesh->vertices.attributes(), "point");
241 ✗ geo_assert(point.is_bound());
242
243 ✗ if(clear) {
244 double* base_mem = target->base_mem_float64_ptr();
245 ✗ size_t nb = target->nb_pixels()*target->components_per_pixel();
246 ✗ for(size_t i=0; i<nb; ++i) {
247 ✗ base_mem[i] = Numeric::max_float64();
248 }
249 }
250 ✗ bake_mesh_attribute(mesh, target, point);
251 ✗ }
252
253 /**************************************************************************/
254
255 ✗ void bake_mesh_facet_normals_indirect(
256 Image* geometry, Image* target, Mesh* highres
257 ) {
258 ✗ geo_assert(geometry->dimension() == 2);
259 ✗ geo_assert(target->dimension() == 2);
260 ✗ geo_assert(
261 geometry->width() == target->width() &&
262 geometry->height() == target->height()
263 );
264 ✗ geo_assert(geometry->component_encoding() == Image::FLOAT64);
265
266 ✗ MeshFacetsAABB AABB(*highres);
267 vector<Color> normal(highres->facets.nb());
268 ✗ for(index_t f: highres->facets) {
269 ✗ vec3 N = normalize(Geom::mesh_facet_normal(*highres,f));
270 ✗ normal[f] = Color(
271 ✗ 0.5*(N.x+1.0), 0.5*(N.y+1.0), 0.5*(N.z+1.0)
272 ✗ );
273 }
274 ✗ ImageRasterizer rasterizer(target);
275
276 ✗ parallel_for(
277 0, target->height(),
278 ✗ [geometry, target, &AABB, &rasterizer, &normal](index_t y) {
279 ✗ index_t nearest_facet = NO_FACET;
280 vec3 nearest_point;
281 double sq_dist;
282 ✗ for(index_t x=0; x<target->width(); ++x) {
283 ✗ vec3 p((double*)(void*)geometry->pixel_base(x,y));
284 ✗ if(
285 ✗ p[0] == Numeric::max_float64() &&
286 ✗ p[1] == Numeric::max_float64() &&
287 p[2] == Numeric::max_float64()
288 ) {
289 ✗ continue;
290 }
291 ✗ if(nearest_facet == NO_FACET) {
292 ✗ nearest_facet =
293 ✗ AABB.nearest_facet(p, nearest_point, sq_dist
294 );
295 } else {
296 ✗ sq_dist = length2(p - nearest_point);
297 ✗ AABB.nearest_facet_with_hint(
298 p,nearest_facet,nearest_point,sq_dist
299 );
300 }
301 ✗ rasterizer.set_pixel(
302 ✗ int(x),int(y),normal[nearest_facet]
303 );
304 }
305 ✗ }
306 );
307 ✗ }
308
309 /**************************************************************************/
310
311 ✗ void bake_mesh_points_attribute_indirect(
312 Image* geometry, Image* target,
313 Mesh* highres, Attribute<double>& attribute,
314 double bias, double scale
315 ) {
316 ✗ geo_assert(geometry->dimension() == 2);
317 ✗ geo_assert(target->dimension() == 2);
318 ✗ geo_assert(
319 geometry->width() == target->width() &&
320 geometry->height() == target->height()
321 );
322 ✗ geo_assert(geometry->component_encoding() == Image::FLOAT64);
323 ✗ geo_assert(highres->vertices.dimension() >= 3);
324
325 ✗ NearestNeighborSearch_var kd_tree = new BalancedKdTree(3);
326
327 ✗ kd_tree->set_points(
328 highres->vertices.nb(), highres->vertices.point_ptr(0),
329 highres->vertices.dimension()
330 );
331
332 ✗ ImageRasterizer rasterizer(target);
333
334 ✗ parallel_for(
335 0, target->height(),
336 ✗ [geometry, target, kd_tree,
337 &rasterizer, &attribute, bias, scale
338 ✗ ](index_t y) {
339
340 ✗ index_t nearest_vertex = NO_VERTEX;
341 double sq_dist;
342 Color C;
343 ✗ for(index_t x=0; x<target->width(); ++x) {
344 ✗ vec3 p((double*)(void*)geometry->pixel_base(x,y));
345 ✗ if(
346 ✗ p[0] == Numeric::max_float64() &&
347 ✗ p[1] == Numeric::max_float64() &&
348 p[2] == Numeric::max_float64()
349 ) {
350 ✗ continue;
351 }
352 ✗ kd_tree->get_nearest_neighbors(
353 1, p.data(), &nearest_vertex, &sq_dist
354 );
355 ✗ get_attribute_as_color(
356 C, attribute, MESH_VERTICES,
357 NO_INDEX, NO_INDEX, nearest_vertex,
358 bias, scale
359 );
360 ✗ rasterizer.set_pixel(int(x),int(y),C);
361 }
362 ✗ }
363 );
364 ✗ }
365
366 /**************************************************************************/
367
368 }
369