GCC Code Coverage Report


Directory: ./
File: lib/geogram/mesh/mesh_baking.cpp
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 0 173 0.0%
Functions: 0 9 0.0%
Branches: 0 390 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