Blender V4.5
texture_margin.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_assert.h"
10#include "BLI_math_geom.h"
11#include "BLI_math_vector.hh"
13#include "BLI_vector.hh"
14
15#include "BKE_attribute.hh"
16#include "BKE_customdata.hh"
17#include "BKE_mesh.hh"
19#include "BKE_mesh_mapping.hh"
20
21#include "IMB_imbuf.hh"
22#include "IMB_interp.hh"
23
24#include "MEM_guardedalloc.h"
25
26#include "zbuf.h" // for rasterizer
27
28#include "RE_texture_margin.h"
29
30#include <algorithm>
31#include <cmath>
32
34
40 static const int directions[8][2];
41 static const int distances[8];
42
44 Vector<int> loop_adjacency_map_;
46 Array<int> loop_to_face_map_;
47
48 int w_, h_;
49 float uv_offset_[2];
50 Vector<uint32_t> pixel_data_;
51 ZSpan zspan_;
52 uint32_t value_to_store_;
53 bool write_mask_;
54 char *mask_;
55
56 OffsetIndices<int> faces_;
57 Span<int> corner_edges_;
58 Span<float2> mloopuv_;
59 int totedge_;
60
61 public:
63 size_t h,
64 const float uv_offset[2],
65 const int totedge,
67 const Span<int> corner_edges,
68 const Span<float2> mloopuv)
69 : w_(w),
70 h_(h),
71 faces_(faces),
72 corner_edges_(corner_edges),
73 mloopuv_(mloopuv),
74 totedge_(totedge)
75 {
76 copy_v2_v2(uv_offset_, uv_offset);
77
78 pixel_data_.resize(w_ * h_, 0xFFFFFFFF);
79
80 zbuf_alloc_span(&zspan_, w_, h_);
81
82 build_tables();
83 }
84
86 {
87 zbuf_free_span(&zspan_);
88 }
89
90 void set_pixel(int x, int y, uint32_t value)
91 {
92 BLI_assert(x < w_);
93 BLI_assert(x >= 0);
94 pixel_data_[y * w_ + x] = value;
95 }
96
97 uint32_t get_pixel(int x, int y) const
98 {
99 if (x < 0 || y < 0 || x >= w_ || y >= h_) {
100 return 0xFFFFFFFF;
101 }
102
103 return pixel_data_[y * w_ + x];
104 }
105
106 void rasterize_tri(float *v1, float *v2, float *v3, uint32_t value, char *mask, bool writemask)
107 {
108 /* NOTE: This is not thread safe, because the value to be written by the rasterizer is
109 * a class member. If this is ever made multi-threaded each thread needs to get its own. */
110 value_to_store_ = value;
111 mask_ = mask;
112 write_mask_ = writemask;
114 &zspan_, this, &(v1[0]), &(v2[0]), &(v3[0]), TextureMarginMap::zscan_store_pixel);
115 }
116
117 static void zscan_store_pixel(
118 void *map, int x, int y, [[maybe_unused]] float u, [[maybe_unused]] float v)
119 {
120 /* NOTE: Not thread safe, see comment above. */
121 TextureMarginMap *m = static_cast<TextureMarginMap *>(map);
122 if (m->mask_) {
123 if (m->write_mask_) {
124 /* if there is a mask and write_mask_ is true, write to the mask */
125 m->mask_[y * m->w_ + x] = 1;
126 m->set_pixel(x, y, m->value_to_store_);
127 }
128 else {
129 /* if there is a mask and write_mask_ is false, read the mask
130 * to decide if the map needs to be written
131 */
132 if (m->mask_[y * m->w_ + x] != 0) {
133 m->set_pixel(x, y, m->value_to_store_);
134 }
135 }
136 }
137 else {
138 m->set_pixel(x, y, m->value_to_store_);
139 }
140 }
141
142/* The map contains 2 kinds of pixels: DijkstraPixels and face indices. The top bit determines
143 * what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 4 bits encode the
144 * direction of the shortest path and the remaining 27 bits are used to store the distance. If
145 * the top bit is not set, the rest of the bits is used to store the face index.
146 */
147#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 4) + (dir))
148#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 4)
149#define DijkstraPixelGetDirection(dp) ((dp) & 0xF)
150#define IsDijkstraPixel(dp) ((dp) & 0x80000000)
151#define DijkstraPixelIsUnset(dp) ((dp) == 0xFFFFFFFF)
152
157 void grow_dijkstra(int margin)
158 {
159 class DijkstraActivePixel {
160 public:
161 DijkstraActivePixel(int dist, int _x, int _y) : distance(dist), x(_x), y(_y) {}
162 int distance;
163 int x, y;
164 };
165 auto cmp_dijkstrapixel_fun = [](DijkstraActivePixel const &a1, DijkstraActivePixel const &a2) {
166 return a1.distance > a2.distance;
167 };
168
169 Vector<DijkstraActivePixel> active_pixels;
170 for (int y = 0; y < h_; y++) {
171 for (int x = 0; x < w_; x++) {
173 for (int i = 0; i < 8; i++) {
174 int xx = x - directions[i][0];
175 int yy = y - directions[i][1];
176
177 if (xx >= 0 && xx < w_ && yy >= 0 && yy < w_ && !IsDijkstraPixel(get_pixel(xx, yy))) {
178 set_pixel(x, y, PackDijkstraPixel(distances[i], i));
179 active_pixels.append(DijkstraActivePixel(distances[i], x, y));
180 break;
181 }
182 }
183 }
184 }
185 }
186
187 /* Not strictly needed because at this point it already is a heap. */
188#if 0
189 std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
190#endif
191
192 while (active_pixels.size()) {
193 std::pop_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
194 DijkstraActivePixel p = active_pixels.pop_last();
195
196 int dist = p.distance;
197
198 if (dist < 2 * (margin + 1)) {
199 for (int i = 0; i < 8; i++) {
200 int x = p.x + directions[i][0];
201 int y = p.y + directions[i][1];
202 if (x >= 0 && x < w_ && y >= 0 && y < h_) {
203 uint32_t dp = get_pixel(x, y);
204 if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist + distances[i])) {
206 set_pixel(x, y, PackDijkstraPixel(dist + distances[i], i));
207 active_pixels.append(DijkstraActivePixel(dist + distances[i], x, y));
208 std::push_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
209 }
210 }
211 }
212 }
213 }
214 }
215
221 void lookup_pixels(ImBuf *ibuf, char *mask, int maxPolygonSteps)
222 {
223 float4 *ibuf_ptr_fl = reinterpret_cast<float4 *>(ibuf->float_buffer.data);
224 uchar4 *ibuf_ptr_ch = reinterpret_cast<uchar4 *>(ibuf->byte_buffer.data);
225 size_t pixel_index = 0;
226 for (int y = 0; y < h_; y++) {
227 for (int x = 0; x < w_; x++) {
228 uint32_t dp = pixel_data_[pixel_index];
229 if (IsDijkstraPixel(dp) && !DijkstraPixelIsUnset(dp)) {
230 int dist = DijkstraPixelGetDistance(dp);
231 int direction = DijkstraPixelGetDirection(dp);
232
233 int xx = x;
234 int yy = y;
235
236 /* Follow the dijkstra directions to find the face this margin pixels belongs to. */
237 while (dist > 0) {
238 xx -= directions[direction][0];
239 yy -= directions[direction][1];
240 dp = get_pixel(xx, yy);
241 dist -= distances[direction];
242 BLI_assert(!dist || (dist == DijkstraPixelGetDistance(dp)));
243 direction = DijkstraPixelGetDirection(dp);
244 }
245
246 uint32_t face = get_pixel(xx, yy);
247
249
250 float destX, destY;
251
252 int other_poly;
253 bool found_pixel_in_polygon = false;
254 if (lookup_pixel_polygon_neighborhood(x, y, &face, &destX, &destY, &other_poly)) {
255
256 for (int i = 0; i < maxPolygonSteps; i++) {
257 /* Force to pixel grid. */
258 int nx = int(round(destX));
259 int ny = int(round(destY));
260 uint32_t polygon_from_map = get_pixel(nx, ny);
261 if (other_poly == polygon_from_map) {
262 found_pixel_in_polygon = true;
263 break;
264 }
265
266 float dist_to_edge;
267 /* Look up again, but starting from the face we were expected to land in. */
268 if (!lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly, &dist_to_edge)) {
269 found_pixel_in_polygon = false;
270 break;
271 }
272 }
273
274 if (found_pixel_in_polygon) {
275 if (ibuf_ptr_fl) {
276 ibuf_ptr_fl[pixel_index] = imbuf::interpolate_bilinear_border_fl(
277 ibuf, destX, destY);
278 }
279 if (ibuf_ptr_ch) {
280 ibuf_ptr_ch[pixel_index] = imbuf::interpolate_bilinear_border_byte(
281 ibuf, destX, destY);
282 }
283 /* Add our new pixels to the assigned pixel map. */
284 mask[pixel_index] = 1;
285 }
286 }
287 }
288 else if (DijkstraPixelIsUnset(dp) || !IsDijkstraPixel(dp)) {
289 /* These are not margin pixels, make sure the extend filter which is run after this step
290 * leaves them alone.
291 */
292 mask[pixel_index] = 1;
293 }
294 pixel_index++;
295 }
296 }
297 }
298
299 private:
300 float2 uv_to_xy(const float2 &mloopuv) const
301 {
302 float2 ret;
303 ret.x = (((mloopuv[0] - uv_offset_[0]) * w_) - (0.5f + 0.001f));
304 ret.y = (((mloopuv[1] - uv_offset_[1]) * h_) - (0.5f + 0.001f));
305 return ret;
306 }
307
308 void build_tables()
309 {
310 loop_to_face_map_ = blender::bke::mesh::build_corner_to_face_map(faces_);
311
312 loop_adjacency_map_.resize(corner_edges_.size(), -1);
313
314 Vector<int> tmpmap;
315 tmpmap.resize(totedge_, -1);
316
317 for (const int64_t i : corner_edges_.index_range()) {
318 int edge = corner_edges_[i];
319 if (tmpmap[edge] == -1) {
320 loop_adjacency_map_[i] = -1;
321 tmpmap[edge] = i;
322 }
323 else {
324 BLI_assert(tmpmap[edge] >= 0);
325 loop_adjacency_map_[i] = tmpmap[edge];
326 loop_adjacency_map_[tmpmap[edge]] = i;
327 }
328 }
329 }
330
337 bool lookup_pixel_polygon_neighborhood(
338 float x, float y, uint32_t *r_start_poly, float *r_destx, float *r_desty, int *r_other_poly)
339 {
340 float found_dist;
341 if (lookup_pixel(x, y, *r_start_poly, r_destx, r_desty, r_other_poly, &found_dist)) {
342 return true;
343 }
344
345 int loopstart = faces_[*r_start_poly].start();
346 int totloop = faces_[*r_start_poly].size();
347
348 float destx, desty;
349 int foundpoly;
350
351 float mindist = -1.0f;
352
353 /* Loop over all adjacent polygons and determine which edge is closest.
354 * This could be optimized by only inspecting neighbors which are on the edge of an island.
355 * But it seems fast enough for now and that would add a lot of complexity. */
356 for (int i = 0; i < totloop; i++) {
357 int otherloop = loop_adjacency_map_[i + loopstart];
358
359 if (otherloop < 0) {
360 continue;
361 }
362
363 uint32_t face = loop_to_face_map_[otherloop];
364
365 if (lookup_pixel(x, y, face, &destx, &desty, &foundpoly, &found_dist)) {
366 if (mindist < 0.0f || found_dist < mindist) {
367 mindist = found_dist;
368 *r_other_poly = foundpoly;
369 *r_destx = destx;
370 *r_desty = desty;
371 *r_start_poly = face;
372 }
373 }
374 }
375
376 return mindist >= 0.0f;
377 }
378
385 bool lookup_pixel(float x,
386 float y,
387 int src_poly,
388 float *r_destx,
389 float *r_desty,
390 int *r_other_poly,
391 float *r_dist_to_edge)
392 {
393 float2 point(x, y);
394
395 *r_destx = *r_desty = 0;
396
397 int found_edge = -1;
398 float found_dist = -1;
399 float found_t = 0;
400
401 /* Find the closest edge on which the point x,y can be projected.
402 */
403 for (size_t i = 0; i < faces_[src_poly].size(); i++) {
404 int l1 = faces_[src_poly].start() + i;
405 int l2 = l1 + 1;
406 if (l2 >= faces_[src_poly].start() + faces_[src_poly].size()) {
407 l2 = faces_[src_poly].start();
408 }
409 /* edge points */
410 float2 edgepoint1 = uv_to_xy(mloopuv_[l1]);
411 float2 edgepoint2 = uv_to_xy(mloopuv_[l2]);
412 /* Vector AB is the vector from the first edge point to the second edge point.
413 * Vector AP is the vector from the first edge point to our point under investigation. */
414 float2 ab = edgepoint2 - edgepoint1;
415 float2 ap = point - edgepoint1;
416
417 /* Project ap onto ab. */
418 float dotv = math::dot(ab, ap);
419
420 float ablensq = math::length_squared(ab);
421
422 float t = dotv / ablensq;
423
424 if (t >= 0.0 && t <= 1.0) {
425
426 /* Find the point on the edge closest to P */
427 float2 reflect_point = edgepoint1 + (t * ab);
428 /* This is the vector to P, so 90 degrees out from the edge. */
429 float2 reflect_vec = reflect_point - point;
430
431 float reflectLen = sqrt(reflect_vec[0] * reflect_vec[0] + reflect_vec[1] * reflect_vec[1]);
432 float cross = ab[0] * reflect_vec[1] - ab[1] * reflect_vec[0];
433 /* Only if P is on the outside of the edge, which means the cross product is positive,
434 * we consider this edge.
435 */
436 bool valid = (cross > 0.0);
437
438 if (valid && (found_dist < 0 || reflectLen < found_dist)) {
439 /* Stother_ab the info of the closest edge so far. */
440 found_dist = reflectLen;
441 found_t = t;
442 found_edge = i + faces_[src_poly].start();
443 }
444 }
445 }
446
447 if (found_edge < 0) {
448 return false;
449 }
450
451 *r_dist_to_edge = found_dist;
452
453 /* Get the 'other' edge. I.E. the UV edge from the neighbor face. */
454 int other_edge = loop_adjacency_map_[found_edge];
455
456 if (other_edge < 0) {
457 return false;
458 }
459
460 int dst_poly = loop_to_face_map_[other_edge];
461
462 if (r_other_poly) {
463 *r_other_poly = dst_poly;
464 }
465
466 int other_edge2 = other_edge + 1;
467 if (other_edge2 >= faces_[dst_poly].start() + faces_[dst_poly].size()) {
468 other_edge2 = faces_[dst_poly].start();
469 }
470
471 float2 other_edgepoint1 = uv_to_xy(mloopuv_[other_edge]);
472 float2 other_edgepoint2 = uv_to_xy(mloopuv_[other_edge2]);
473
474 /* Calculate the vector from the order edges last point to its first point. */
475 float2 other_ab = other_edgepoint1 - other_edgepoint2;
476 float2 other_reflect_point = other_edgepoint2 + (found_t * other_ab);
477 float2 perpendicular_other_ab;
478 perpendicular_other_ab.x = other_ab.y;
479 perpendicular_other_ab.y = -other_ab.x;
480
481 /* The new point is dound_dist distance from other_reflect_point at a 90 degree angle to
482 * other_ab */
483 float2 new_point = other_reflect_point + (found_dist / math::length(perpendicular_other_ab)) *
484 perpendicular_other_ab;
485
486 *r_destx = new_point.x;
487 *r_desty = new_point.y;
488
489 return true;
490 }
491}; // class TextureMarginMap
492
493const int TextureMarginMap::directions[8][2] = {
494 {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}};
495const int TextureMarginMap::distances[8] = {2, 3, 2, 3, 2, 3, 2, 3};
496
497static void generate_margin(ImBuf *ibuf,
498 char *mask,
499 const int margin,
500 const Span<float3> vert_positions,
501 const int edges_num,
503 const Span<int> corner_edges,
504 const Span<int> corner_verts,
505 const Span<float2> mloopuv,
506 const float uv_offset[2])
507{
508 Array<int3> corner_tris(poly_to_tri_count(faces.size(), corner_edges.size()));
509 bke::mesh::corner_tris_calc(vert_positions, faces, corner_verts, corner_tris);
510
511 Array<int> tri_faces(corner_tris.size());
513
514 TextureMarginMap map(ibuf->x, ibuf->y, uv_offset, edges_num, faces, corner_edges, mloopuv);
515
516 bool draw_new_mask = false;
517 /* Now the map contains 3 sorts of values: 0xFFFFFFFF for empty pixels, `0x80000000 + polyindex`
518 * for margin pixels, just `polyindex` for face pixels. */
519 if (mask) {
520 mask = (char *)MEM_dupallocN(mask);
521 }
522 else {
523 mask = MEM_calloc_arrayN<char>(size_t(ibuf->x) * size_t(ibuf->y), __func__);
524 draw_new_mask = true;
525 }
526
527 for (const int i : corner_tris.index_range()) {
528 const int3 tri = corner_tris[i];
529 float vec[3][2];
530
531 for (int a = 0; a < 3; a++) {
532 const float *uv = mloopuv[tri[a]];
533
534 /* NOTE(@ideasman42): workaround for pixel aligned UVs which are common and can screw up
535 * our intersection tests where a pixel gets in between 2 faces or the middle of a quad,
536 * camera aligned quads also have this problem but they are less common.
537 * Add a small offset to the UVs, fixes bug #18685. */
538 vec[a][0] = (uv[0] - uv_offset[0]) * float(ibuf->x) - (0.5f + 0.001f);
539 vec[a][1] = (uv[1] - uv_offset[1]) * float(ibuf->y) - (0.5f + 0.002f);
540 }
541
542 /* NOTE: we need the top bit for the dijkstra distance map. */
543 BLI_assert(tri_faces[i] < 0x80000000);
544
545 map.rasterize_tri(vec[0], vec[1], vec[2], tri_faces[i], mask, draw_new_mask);
546 }
547
548 char *tmpmask = (char *)MEM_dupallocN(mask);
549 /* Extend (with averaging) by 2 pixels. Those will be overwritten, but it
550 * helps linear interpolations on the edges of polygons. */
551 IMB_filter_extend(ibuf, tmpmask, 2);
552 MEM_freeN(tmpmask);
553
554 map.grow_dijkstra(margin);
555
556 /* Looking further than 3 polygons away leads to so much cumulative rounding
557 * that it isn't worth it. So hard-code it to 3. */
558 map.lookup_pixels(ibuf, mask, 3);
559
560 /* Use the extend filter to fill in the missing pixels at the corners, not strictly correct, but
561 * the visual difference seems very minimal. This also catches pixels we missed because of very
562 * narrow polygons.
563 */
564 IMB_filter_extend(ibuf, mask, margin);
565
567}
568
569} // namespace blender::render::texturemargin
570
572 char *mask,
573 const int margin,
574 const Mesh *mesh,
575 char const *uv_layer,
576 const float uv_offset[2])
577{
578 using namespace blender;
579 const blender::StringRef uv_map_name = (uv_layer && uv_layer[0]) ?
580 uv_layer :
583 const blender::bke::AttributeAccessor attributes = mesh->attributes();
584 const VArraySpan<float2> uv_map = *attributes.lookup<float2>(uv_map_name,
586
588 mask,
589 margin,
590 mesh->vert_positions(),
591 mesh->edges_num,
592 mesh->faces(),
593 mesh->corner_edges(),
594 mesh->corner_verts(),
595 uv_map,
596 uv_offset);
597}
598
600 ImBuf *ibuf, char *mask, const int margin, DerivedMesh *dm, const float uv_offset[2])
601{
602 const blender::float2 *mloopuv = static_cast<const blender::float2 *>(
604
606 ibuf,
607 mask,
608 margin,
609 {reinterpret_cast<const blender::float3 *>(dm->getVertArray(dm)), dm->getNumVerts(dm)},
610 dm->getNumEdges(dm),
611 blender::Span(dm->getPolyArray(dm), dm->getNumPolys(dm) + 1),
612 {dm->getCornerEdgeArray(dm), dm->getNumLoops(dm)},
613 {dm->getCornerVertArray(dm), dm->getNumLoops(dm)},
614 {mloopuv, dm->getNumLoops(dm)},
615 uv_offset);
616}
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
MINLINE void copy_v2_v2(float r[2], const float a[2])
@ CD_PROP_FLOAT2
void IMB_filter_extend(ImBuf *ibuf, char *mask, int filter)
Definition filter.cc:317
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
AttributeSet attributes
int64_t size() const
Definition BLI_array.hh:245
IndexRange index_range() const
Definition BLI_array.hh:349
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
void append(const T &value)
void resize(const int64_t new_size)
GAttributeReader lookup(const StringRef attribute_id) const
void rasterize_tri(float *v1, float *v2, float *v3, uint32_t value, char *mask, bool writemask)
void set_pixel(int x, int y, uint32_t value)
TextureMarginMap(size_t w, size_t h, const float uv_offset[2], const int totedge, const OffsetIndices< int > faces, const Span< int > corner_edges, const Span< float2 > mloopuv)
void lookup_pixels(ImBuf *ibuf, char *mask, int maxPolygonSteps)
static void zscan_store_pixel(void *map, int x, int y, float u, float v)
#define round
#define sqrt
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static char faces[256]
void corner_tris_calc(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, MutableSpan< int3 > corner_tris)
Array< int > build_corner_to_face_map(OffsetIndices< int > faces)
void corner_tris_calc_face_indices(OffsetIndices< int > faces, MutableSpan< int > tri_faces)
uchar4 interpolate_bilinear_border_byte(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:74
float4 interpolate_bilinear_border_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:78
T length_squared(const VecBase< T, Size > &a)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
static void generate_margin(ImBuf *ibuf, char *mask, const int margin, const Span< float3 > vert_positions, const int edges_num, const OffsetIndices< int > faces, const Span< int > corner_edges, const Span< int > corner_verts, const Span< float2 > mloopuv, const float uv_offset[2])
blender::VecBase< uint8_t, 4 > uchar4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
return ret
int *(* getPolyArray)(DerivedMesh *dm)
int(* getNumVerts)(DerivedMesh *dm)
int(* getNumPolys)(DerivedMesh *dm)
int(* getNumEdges)(DerivedMesh *dm)
float *(* getVertArray)(DerivedMesh *dm)
void *(* getLoopDataArray)(DerivedMesh *dm, eCustomDataType type)
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
int edges_num
CustomData corner_data
Definition zbuf.h:12
float x
float y
i
Definition text_draw.cc:230
#define PackDijkstraPixel(dist, dir)
void RE_generate_texturemargin_adjacentfaces(ImBuf *ibuf, char *mask, const int margin, const Mesh *mesh, char const *uv_layer, const float uv_offset[2])
void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf, char *mask, const int margin, DerivedMesh *dm, const float uv_offset[2])
#define DijkstraPixelIsUnset(dp)
#define DijkstraPixelGetDistance(dp)
#define DijkstraPixelGetDirection(dp)
#define IsDijkstraPixel(dp)
void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void(*func)(void *, int, int, float, float))
Definition zbuf.cc:151
void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty)
Definition zbuf.cc:33
void zbuf_free_span(ZSpan *zspan)
Definition zbuf.cc:44