Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/blender/addon/ui.py36
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c3
-rw-r--r--source/blender/editors/object/object_bake.c15
-rw-r--r--source/blender/editors/object/object_bake_api.c38
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h4
-rw-r--r--source/blender/makesdna/DNA_scene_types.h13
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h1
-rw-r--r--source/blender/makesrna/RNA_enum_items.h1
-rw-r--r--source/blender/makesrna/intern/rna_scene.c33
-rw-r--r--source/blender/render/CMakeLists.txt2
-rw-r--r--source/blender/render/RE_bake.h8
-rw-r--r--source/blender/render/RE_multires_bake.h3
-rw-r--r--source/blender/render/RE_texture_margin.h45
-rw-r--r--source/blender/render/intern/bake.c19
-rw-r--r--source/blender/render/intern/multires_bake.c23
-rw-r--r--source/blender/render/intern/texture_margin.cc569
16 files changed, 783 insertions, 30 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index e4fbc898070..ddef3f63641 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -1803,18 +1803,45 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel):
rd = scene.render
if rd.use_bake_multires:
- layout.prop(rd, "bake_margin")
layout.prop(rd, "use_bake_clear", text="Clear Image")
-
if rd.bake_type == 'DISPLACEMENT':
layout.prop(rd, "use_bake_lores_mesh")
else:
layout.prop(cbk, "target")
-
if cbk.target == 'IMAGE_TEXTURES':
- layout.prop(cbk, "margin")
layout.prop(cbk, "use_clear", text="Clear Image")
+class CYCLES_RENDER_PT_bake_output_margin(CyclesButtonsPanel, Panel):
+ bl_label = "Margin"
+ bl_context = "render"
+ bl_parent_id = "CYCLES_RENDER_PT_bake_output"
+ COMPAT_ENGINES = {'CYCLES'}
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ cbk = scene.render.bake
+ return cbk.target == 'IMAGE_TEXTURES'
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ scene = context.scene
+ cscene = scene.cycles
+ cbk = scene.render.bake
+ rd = scene.render
+
+ if rd.use_bake_multires:
+ layout.prop(rd, "bake_margin_type", text="Type")
+ layout.prop(rd, "bake_margin", text="Size")
+ else:
+ if cbk.target == 'IMAGE_TEXTURES':
+ layout.prop(cbk, "margin_type", text="Type")
+ layout.prop(cbk, "margin", text="Size")
+
+
class CYCLES_RENDER_PT_debug(CyclesDebugButtonsPanel, Panel):
bl_label = "Debug"
@@ -2183,6 +2210,7 @@ classes = (
CYCLES_RENDER_PT_bake_influence,
CYCLES_RENDER_PT_bake_selected_to_active,
CYCLES_RENDER_PT_bake_output,
+ CYCLES_RENDER_PT_bake_output_margin,
CYCLES_RENDER_PT_debug,
node_panel(CYCLES_MATERIAL_PT_settings),
node_panel(CYCLES_MATERIAL_PT_settings_surface),
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index 2fceb42262e..94720ad0b0a 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -1859,7 +1859,8 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (bmain->subversionfile < 4) {
for (sce = bmain->scenes.first; sce; sce = sce->id.next) {
sce->r.bake_mode = 1; /* prevent to include render stuff here */
- sce->r.bake_filter = 16;
+ sce->r.bake_margin = 16;
+ sce->r.bake_margin_type = R_BAKE_ADJACENT_FACES;
sce->r.bake_flag = R_BAKE_CLEAR;
}
}
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index 1b6b0c78037..a5e6e7f0852 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -108,8 +108,10 @@ typedef struct {
ListBase data;
/** Clear the images before baking */
bool bake_clear;
- /** Bake-filter, aka margin */
- int bake_filter;
+ /** margin size in pixels*/
+ int bake_margin;
+ /** margin type */
+ char bake_margin_type;
/** mode of baking (displacement, normals, AO) */
short mode;
/** Use low-resolution mesh when baking displacement maps */
@@ -372,7 +374,8 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
/* copy data stored in job descriptor */
bkr.scene = scene;
- bkr.bake_filter = scene->r.bake_filter;
+ bkr.bake_margin = scene->r.bake_margin;
+ bkr.bake_margin_type = scene->r.bake_margin_type;
bkr.mode = scene->r.bake_mode;
bkr.use_lores_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
bkr.bias = scene->r.bake_biasdist;
@@ -416,7 +419,8 @@ static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
/* backup scene settings, so their changing in UI would take no effect on baker */
bkj->scene = scene;
- bkj->bake_filter = scene->r.bake_filter;
+ bkj->bake_margin = scene->r.bake_margin;
+ bkj->bake_margin_type = scene->r.bake_margin_type;
bkj->mode = scene->r.bake_mode;
bkj->use_lores_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
bkj->bake_clear = scene->r.bake_flag & R_BAKE_CLEAR;
@@ -477,7 +481,8 @@ static void multiresbake_startjob(void *bkv, short *stop, short *do_update, floa
/* copy data stored in job descriptor */
bkr.scene = bkj->scene;
- bkr.bake_filter = bkj->bake_filter;
+ bkr.bake_margin = bkj->bake_margin;
+ bkr.bake_margin_type = bkj->bake_margin_type;
bkr.mode = bkj->mode;
bkr.use_lores_mesh = bkj->use_lores_mesh;
bkr.user_scale = bkj->user_scale;
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 0de34e21462..d56d0edd5a2 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -89,6 +89,7 @@ typedef struct BakeAPIRender {
eScenePassType pass_type;
int pass_filter;
int margin;
+ eBakeMarginType margin_type;
bool is_clear;
bool is_selected_to_active;
@@ -184,8 +185,11 @@ static bool write_internal_bake_pixels(Image *image,
const int width,
const int height,
const int margin,
+ const char margin_type,
const bool is_clear,
- const bool is_noncolor)
+ const bool is_noncolor,
+ Mesh const *mesh,
+ char const *uv_layer)
{
ImBuf *ibuf;
void *lock;
@@ -281,7 +285,7 @@ static bool write_internal_bake_pixels(Image *image,
/* margins */
if (margin > 0) {
- RE_bake_margin(ibuf, mask_buffer, margin);
+ RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh, uv_layer);
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
@@ -327,8 +331,11 @@ static bool write_external_bake_pixels(const char *filepath,
const int width,
const int height,
const int margin,
+ const int margin_type,
ImageFormatData *im_format,
- const bool is_noncolor)
+ const bool is_noncolor,
+ Mesh const *mesh,
+ char const *uv_layer)
{
ImBuf *ibuf = NULL;
bool ok = false;
@@ -385,7 +392,7 @@ static bool write_external_bake_pixels(const char *filepath,
mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask");
RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer);
- RE_bake_margin(ibuf, mask_buffer, margin);
+ RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh, uv_layer);
if (mask_buffer) {
MEM_freeN(mask_buffer);
@@ -770,6 +777,7 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr,
ReportList *reports)
{
bool all_ok = true;
+ const Mesh *me = (Mesh *)ob->data;
for (int i = 0; i < targets->num_images; i++) {
BakeImage *bk_image = &targets->images[i];
@@ -780,8 +788,11 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr,
bk_image->width,
bk_image->height,
bkr->margin,
+ bkr->margin_type,
bkr->is_clear,
- targets->is_noncolor);
+ targets->is_noncolor,
+ me,
+ bkr->uv_layer);
/* might be read by UI to set active image for display */
bake_update_image(bkr->area, bk_image->image);
@@ -895,8 +906,11 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
bk_image->width,
bk_image->height,
bkr->margin,
+ bkr->margin_type,
&bake->im_format,
- targets->is_noncolor);
+ targets->is_noncolor,
+ me,
+ bkr->uv_layer);
if (!ok) {
BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name);
@@ -1625,6 +1639,7 @@ static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr)
bkr->pass_type = RNA_enum_get(op->ptr, "type");
bkr->pass_filter = RNA_enum_get(op->ptr, "pass_filter");
bkr->margin = RNA_int_get(op->ptr, "margin");
+ bkr->margin_type = RNA_enum_get(op->ptr, "margin_type");
bkr->save_mode = (eBakeSaveMode)RNA_enum_get(op->ptr, "save_mode");
bkr->target = (eBakeTarget)RNA_enum_get(op->ptr, "target");
@@ -1818,6 +1833,11 @@ static void bake_set_props(wmOperator *op, Scene *scene)
RNA_property_int_set(op->ptr, prop, bake->margin);
}
+ prop = RNA_struct_find_property(op->ptr, "margin_type");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_enum_set(op->ptr, prop, bake->margin_type);
+ }
+
prop = RNA_struct_find_property(op->ptr, "use_selected_to_active");
if (!RNA_property_is_set(op->ptr, prop)) {
RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_TO_ACTIVE) != 0);
@@ -2008,6 +2028,12 @@ void OBJECT_OT_bake(wmOperatorType *ot)
"Extends the baked result as a post process filter",
0,
64);
+ RNA_def_enum(ot->srna,
+ "margin_type",
+ rna_enum_bake_margin_type_items,
+ R_BAKE_EXTEND,
+ "Margin Type",
+ "Which algorithm to use to generate the margin");
RNA_def_boolean(ot->srna,
"use_selected_to_active",
false,
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index d2c4f22bc23..7ba054e3133 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -47,6 +47,7 @@
.width = 512, \
.height = 512, \
.margin = 16, \
+ .margin_type = R_BAKE_ADJACENT_FACES, \
.normal_space = R_BAKE_SPACE_TANGENT, \
.normal_swizzle = {R_BAKE_POSX, R_BAKE_POSY, R_BAKE_POSZ}, \
}
@@ -102,7 +103,8 @@
.dither_intensity = 1.0f, \
\
.bake_mode = 0, \
- .bake_filter = 16, \
+ .bake_margin = 16, \
+ .bake_margin_type = R_BAKE_ADJACENT_FACES, \
.bake_flag = R_BAKE_CLEAR, \
.bake_samples = 256, \
.bake_biasdist = 0.001f, \
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index a491e131d71..864358e040c 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -563,11 +563,18 @@ typedef struct BakeData {
char target;
char save_mode;
- char _pad[6];
+ char margin_type;
+ char _pad[5];
struct Object *cage_object;
} BakeData;
+/** #BakeData.margin_type (char) */
+typedef enum eBakeMarginType {
+ R_BAKE_ADJACENT_FACES = 0,
+ R_BAKE_EXTEND = 1,
+} eBakeMarginType;
+
/** #BakeData.normal_swizzle (char) */
typedef enum eBakeNormalSwizzle {
R_BAKE_POSX = 0,
@@ -715,7 +722,9 @@ typedef struct RenderData {
/* Bake Render options */
short bake_mode, bake_flag;
- short bake_filter, bake_samples;
+ short bake_margin, bake_samples;
+ short bake_margin_type;
+ char _pad9[6];
float bake_biasdist, bake_user_scale;
/* path to render output */
diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h
index c5769d7eee4..cb8052856a7 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -107,6 +107,7 @@ DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights)
DNA_STRUCT_RENAME_ELEM(RigidBodyWorld, steps_per_second, substeps_per_frame)
+DNA_STRUCT_RENAME_ELEM(RenderData, bake_filter, bake_margin)
DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type)
DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numverts, num_bind_verts)
DNA_STRUCT_RENAME_ELEM(Text, name, filepath)
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index 531af92c544..baa9ddba1be 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -72,6 +72,7 @@ DEF_ENUM(rna_enum_image_generated_type_items)
DEF_ENUM(rna_enum_normal_space_items)
DEF_ENUM(rna_enum_normal_swizzle_items)
DEF_ENUM(rna_enum_bake_save_mode_items)
+DEF_ENUM(rna_enum_bake_margin_type_items)
DEF_ENUM(rna_enum_bake_target_items)
DEF_ENUM(rna_enum_views_format_items)
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 2cda19737da..5bfbd42866b 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -432,6 +432,16 @@ const EnumPropertyItem rna_enum_normal_swizzle_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_bake_margin_type_items[] = {
+ {R_BAKE_ADJACENT_FACES,
+ "ADJACENT_FACES",
+ 0,
+ "Adjacent Faces",
+ "Use pixels from adjacent faces across UV seams"},
+ {R_BAKE_EXTEND, "EXTEND", 0, "Extend", "Extend border pixels outwards"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_bake_target_items[] = {
{R_BAKE_TARGET_IMAGE_TEXTURES,
"IMAGE_TEXTURES",
@@ -5055,6 +5065,11 @@ static void rna_def_bake_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Margin", "Extends the baked result as a post process filter");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+ prop = RNA_def_property(srna, "margin_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_bake_margin_type_items);
+ RNA_def_property_ui_text(prop, "Margin Type", "Algorithm to extend the baked result");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
prop = RNA_def_property(srna, "max_ray_distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 1.0, 1, 3);
@@ -5846,6 +5861,16 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem bake_margin_type_items[] = {
+ {R_BAKE_ADJACENT_FACES,
+ "ADJACENT_FACES",
+ 0,
+ "Adjacent Faces",
+ "Use pixels from adjacent faces across UV seams"},
+ {R_BAKE_EXTEND, "EXTEND", 0, "Extend", "Extend border pixels outwards"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
static const EnumPropertyItem pixel_size_items[] = {
{0, "AUTO", 0, "Automatic", "Automatic pixel size, depends on the user interface scale"},
{1, "1", 0, "1x", "Render at full resolution"},
@@ -6261,11 +6286,17 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "bake_margin", PROP_INT, PROP_PIXEL);
- RNA_def_property_int_sdna(prop, NULL, "bake_filter");
+ RNA_def_property_int_sdna(prop, NULL, "bake_margin");
RNA_def_property_range(prop, 0, 64);
RNA_def_property_ui_text(prop, "Margin", "Extends the baked result as a post process filter");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+ prop = RNA_def_property(srna, "bake_margin_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "bake_margin_type");
+ RNA_def_property_enum_items(prop, bake_margin_type_items);
+ RNA_def_property_ui_text(prop, "Margin Type", "Algorithm to generate the margin");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
prop = RNA_def_property(srna, "bake_bias", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "bake_biasdist");
RNA_def_property_range(prop, 0.0, 1000.0);
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index 494415a4077..a7c1b12982c 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -49,6 +49,7 @@ set(SRC
intern/pipeline.c
intern/render_result.c
intern/texture_image.c
+ intern/texture_margin.cc
intern/texture_pointdensity.c
intern/texture_procedural.c
intern/zbuf.c
@@ -58,6 +59,7 @@ set(SRC
RE_multires_bake.h
RE_pipeline.h
RE_texture.h
+ RE_texture_margin.h
intern/pipeline.h
intern/render_result.h
diff --git a/source/blender/render/RE_bake.h b/source/blender/render/RE_bake.h
index b7ce3da71ff..d61c0a8bf90 100644
--- a/source/blender/render/RE_bake.h
+++ b/source/blender/render/RE_bake.h
@@ -27,6 +27,7 @@ struct Depsgraph;
struct ImBuf;
struct Mesh;
struct Render;
+struct MLoopUV;
#ifdef __cplusplus
extern "C" {
@@ -112,7 +113,12 @@ void RE_bake_pixels_populate(struct Mesh *me,
void RE_bake_mask_fill(const BakePixel pixel_array[], size_t num_pixels, char *mask);
-void RE_bake_margin(struct ImBuf *ibuf, char *mask, int margin);
+void RE_bake_margin(struct ImBuf *ibuf,
+ char *mask,
+ int margin,
+ char margin_type,
+ struct Mesh const *me,
+ char const *uv_layer);
void RE_bake_normal_world_to_object(const BakePixel pixel_array[],
size_t num_pixels,
diff --git a/source/blender/render/RE_multires_bake.h b/source/blender/render/RE_multires_bake.h
index 42ee2c57fbb..6df80c27c40 100644
--- a/source/blender/render/RE_multires_bake.h
+++ b/source/blender/render/RE_multires_bake.h
@@ -33,7 +33,8 @@ extern "C" {
typedef struct MultiresBakeRender {
Scene *scene;
DerivedMesh *lores_dm, *hires_dm;
- int bake_filter; /* Bake-filter, aka margin */
+ int bake_margin;
+ char bake_margin_type;
int lvl, tot_lvl;
short mode;
bool use_lores_mesh; /* Use low-resolution mesh when baking displacement maps */
diff --git a/source/blender/render/RE_texture_margin.h b/source/blender/render/RE_texture_margin.h
new file mode 100644
index 00000000000..0533b93a286
--- /dev/null
+++ b/source/blender/render/RE_texture_margin.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Mesh;
+struct IMBuf;
+struct MLoopUV;
+struct ImBuf;
+struct DerivedMesh;
+
+void RE_generate_texturemargin_adjacentfaces(
+ struct ImBuf *ibuf, char *mask, const int margin, struct Mesh const *me, char const *uv_layer);
+
+void RE_generate_texturemargin_adjacentfaces_dm(struct ImBuf *ibuf,
+ char *mask,
+ const int margin,
+ struct DerivedMesh *dm);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index 2d7f964a968..93d2f721cc5 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -83,6 +83,7 @@
#include "IMB_imbuf_types.h"
#include "RE_bake.h"
+#include "RE_texture_margin.h"
/* local include */
#include "render_types.h"
@@ -154,10 +155,24 @@ void RE_bake_mask_fill(const BakePixel pixel_array[], const size_t num_pixels, c
}
}
-void RE_bake_margin(ImBuf *ibuf, char *mask, const int margin)
+void RE_bake_margin(ImBuf *ibuf,
+ char *mask,
+ const int margin,
+ const char margin_type,
+ Mesh const *me,
+ char const *uv_layer)
{
/* margin */
- IMB_filter_extend(ibuf, mask, margin);
+ switch (margin_type) {
+ case R_BAKE_ADJACENT_FACES:
+ RE_generate_texturemargin_adjacentfaces(ibuf, mask, margin, me, uv_layer);
+ break;
+ default:
+ /* fall through */
+ case R_BAKE_EXTEND:
+ IMB_filter_extend(ibuf, mask, margin);
+ break;
+ }
if (ibuf->planes != R_IMF_PLANES_RGBA) {
/* clear alpha added by filtering */
diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c
index d3e7dca2035..5cf328a3a73 100644
--- a/source/blender/render/intern/multires_bake.c
+++ b/source/blender/render/intern/multires_bake.c
@@ -47,6 +47,7 @@
#include "RE_multires_bake.h"
#include "RE_pipeline.h"
#include "RE_texture.h"
+#include "RE_texture_margin.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -1296,14 +1297,23 @@ static void apply_ao_callback(DerivedMesh *lores_dm,
/* ******$***************** Post processing ************************* */
-static void bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
+static void bake_ibuf_filter(
+ ImBuf *ibuf, char *mask, const int margin, const char margin_type, DerivedMesh *dm)
{
/* must check before filtering */
const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf);
- /* Margin */
- if (filter) {
- IMB_filter_extend(ibuf, mask, filter);
+ if (margin) {
+ switch (margin_type) {
+ case R_BAKE_ADJACENT_FACES:
+ RE_generate_texturemargin_adjacentfaces_dm(ibuf, mask, margin, dm);
+ break;
+ default:
+ /* fall through */
+ case R_BAKE_EXTEND:
+ IMB_filter_extend(ibuf, mask, margin);
+ break;
+ }
}
/* if the bake results in new alpha then change the image setting */
@@ -1311,7 +1321,7 @@ static void bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
ibuf->planes = R_IMF_PLANES_RGBA;
}
else {
- if (filter && ibuf->planes != R_IMF_PLANES_RGBA) {
+ if (margin && ibuf->planes != R_IMF_PLANES_RGBA) {
/* clear alpha added by filtering */
IMB_rectfill_alpha(ibuf, 1.0f);
}
@@ -1460,7 +1470,8 @@ static void finish_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
result->height_max);
}
- bake_ibuf_filter(ibuf, userdata->mask_buffer, bkr->bake_filter);
+ bake_ibuf_filter(
+ ibuf, userdata->mask_buffer, bkr->bake_margin, bkr->bake_margin_type, bkr->lores_dm);
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
BKE_image_mark_dirty(ima, ibuf);
diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc
new file mode 100644
index 00000000000..3798d7dbe87
--- /dev/null
+++ b/source/blender/render/intern/texture_margin.cc
@@ -0,0 +1,569 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup render
+ */
+
+#include "BLI_assert.h"
+#include "BLI_math_geom.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_mesh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "zbuf.h" // for rasterizer
+
+#include "RE_texture_margin.h"
+
+#include <algorithm>
+#include <math.h>
+#include <valarray>
+
+namespace blender::render::texturemargin {
+
+/* The map class contains both a pixel map which maps out polygon indices for all UV-polygons and
+ * adjacency tables.
+ */
+class TextureMarginMap {
+ static const int directions[4][2];
+
+ /* Maps UV-edges to their corresponding UV-edge. */
+ Vector<int> loop_adjacency_map_;
+ /* Maps UV-edges to their corresponding polygon. */
+ Vector<int> loop_to_poly_map_;
+
+ int w_, h_;
+ Vector<uint32_t> pixel_data_;
+ ZSpan zspan_;
+ uint32_t value_to_store_;
+ char *mask_;
+
+ MPoly const *mpoly_;
+ MLoop const *mloop_;
+ MLoopUV const *mloopuv_;
+ int totpoly_;
+ int totloop_;
+ int totedge_;
+
+ public:
+ TextureMarginMap(size_t w,
+ size_t h,
+ MPoly const *mpoly,
+ MLoop const *mloop,
+ MLoopUV const *mloopuv,
+ int totpoly,
+ int totloop,
+ int totedge)
+ : w_(w),
+ h_(h),
+ mpoly_(mpoly),
+ mloop_(mloop),
+ mloopuv_(mloopuv),
+ totpoly_(totpoly),
+ totloop_(totloop),
+ totedge_(totedge)
+ {
+ pixel_data_.resize(w_ * h_, 0xFFFFFFFF);
+
+ zbuf_alloc_span(&zspan_, w_, h_);
+
+ build_tables();
+ }
+
+ ~TextureMarginMap()
+ {
+ zbuf_free_span(&zspan_);
+ }
+
+ inline void set_pixel(int x, int y, uint32_t value)
+ {
+ BLI_assert(x < w_);
+ BLI_assert(x >= 0);
+ pixel_data_[y * w_ + x] = value;
+ }
+
+ inline uint32_t get_pixel(int x, int y) const
+ {
+ if (x < 0 || y < 0 || x >= w_ || y >= h_) {
+ return 0xFFFFFFFF;
+ }
+
+ return pixel_data_[y * w_ + x];
+ }
+
+ void rasterize_tri(float *v1, float *v2, float *v3, uint32_t value, char *mask)
+ {
+ /* NOTE: This is not thread safe, because the value to be written by the rasterizer is
+ * a class member. If this is ever made multithreaded each therad needs to get it's own.
+ */
+ value_to_store_ = value;
+ mask_ = mask;
+ zspan_scanconvert(
+ &zspan_, this, &(v1[0]), &(v2[0]), &(v3[0]), TextureMarginMap::zscan_store_pixel);
+ }
+
+ static void zscan_store_pixel(void *map, int x, int y, float, float)
+ {
+ /* NOTE: Not thread safe, see comment above.
+ *
+ */
+ TextureMarginMap *m = static_cast<TextureMarginMap *>(map);
+ m->set_pixel(x, y, m->value_to_store_);
+ if (m->mask_) {
+ m->mask_[y * m->w_ + x] = 1;
+ }
+ }
+
+/* The map contains 2 kinds of pixels: DijkstraPixels and polygon indices. The top bit determines
+ * what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 3 bits encode the
+ * direction of the shortest path and the remaining 28 bits are used to store the distance. If
+ * the top bit is not set, the rest of the bits is used to store the polygon index.
+ */
+#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 3) + (dir))
+#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 3)
+#define DijkstraPixelGetDirection(dp) ((dp)&0x7)
+#define IsDijkstraPixel(dp) ((dp)&0x80000000)
+#define DijkstraPixelIsUnset(dp) ((dp) == 0xFFFFFFFF)
+
+ /* Use dijkstra's algorithm to 'grow' a border around the polygons marked in the map.
+ * For each pixel mark which direction is the shortest way to a polygon.
+ */
+ void grow_dijkstra(int margin)
+ {
+ class DijkstraActivePixel {
+ public:
+ DijkstraActivePixel(int dist, int _x, int _y) : distance(dist), x(_x), y(_y)
+ {
+ }
+ int distance;
+ int x, y;
+ };
+ auto cmp_dijkstrapixel_fun = [](DijkstraActivePixel const &a1, DijkstraActivePixel const &a2) {
+ return a1.distance > a2.distance;
+ };
+
+ Vector<DijkstraActivePixel> active_pixels;
+ for (int y = 0; y < h_; y++) {
+ for (int x = 0; x < w_; x++) {
+ if (DijkstraPixelIsUnset(get_pixel(x, y))) {
+ for (int i = 0; i < 4; i++) {
+ int xx = x - directions[i][0];
+ int yy = y - directions[i][1];
+
+ if (xx >= 0 && xx < w_ && yy >= 0 && yy < w_ && !IsDijkstraPixel(get_pixel(xx, yy))) {
+ set_pixel(x, y, PackDijkstraPixel(1, i));
+ active_pixels.append(DijkstraActivePixel(1, x, y));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
+ // Not strictly needed because at this point it already is a heap.
+
+ while (active_pixels.size()) {
+ std::pop_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
+ DijkstraActivePixel p = active_pixels.pop_last();
+
+ int dist = p.distance;
+
+ dist++;
+ if (dist < margin) {
+ for (int i = 0; i < 4; i++) {
+ int x = p.x + directions[i][0];
+ int y = p.y + directions[i][1];
+ if (x >= 0 && x < w_ && y >= 0 && y < h_) {
+ uint32_t dp = get_pixel(x, y);
+ if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist)) {
+ BLI_assert(abs((int)DijkstraPixelGetDirection(dp) - (int)i) != 2);
+ set_pixel(x, y, PackDijkstraPixel(dist, i));
+ active_pixels.append(DijkstraActivePixel(dist, x, y));
+ std::push_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Walk over the map and for margin pixels follow the direction stored in the bottom 3
+ * bits back to the polygon.
+ * Then look up the pixel from the next polygon.
+ */
+ void lookup_pixels(ImBuf *ibuf, char *mask, int maxPolygonSteps)
+ {
+ for (int y = 0; y < h_; y++) {
+ for (int x = 0; x < w_; x++) {
+ uint32_t dp = get_pixel(x, y);
+ if (IsDijkstraPixel(dp) && !DijkstraPixelIsUnset(dp)) {
+ int dist = DijkstraPixelGetDistance(dp);
+ int direction = DijkstraPixelGetDirection(dp);
+
+ int xx = x;
+ int yy = y;
+
+ /* Follow the dijkstra directions to find the polygon this margin pixels belongs to. */
+ while (dist > 0) {
+ xx -= directions[direction][0];
+ yy -= directions[direction][1];
+ dp = get_pixel(xx, yy);
+ dist--;
+ BLI_assert(!dist || (dist == DijkstraPixelGetDistance(dp)));
+ direction = DijkstraPixelGetDirection(dp);
+ }
+
+ uint32_t poly = get_pixel(xx, yy);
+
+ BLI_assert(!IsDijkstraPixel(poly));
+
+ float destX, destY;
+
+ int other_poly;
+ bool found_pixel_in_polygon = false;
+ if (lookup_pixel(x, y, poly, &destX, &destY, &other_poly)) {
+
+ for (int i = 0; i < maxPolygonSteps; i++) {
+ /* Force to pixel grid. */
+ int nx = (int)round(destX);
+ int ny = (int)round(destY);
+ uint32_t polygon_from_map = get_pixel(nx, ny);
+ if (other_poly == polygon_from_map) {
+ found_pixel_in_polygon = true;
+ break;
+ }
+
+ /* Look up again, but starting from the polygon we were expected to land in. */
+ lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly);
+ }
+
+ if (found_pixel_in_polygon) {
+ bilinear_interpolation(ibuf, ibuf, destX, destY, x, y);
+ /* Add our new pixels to the assigned pixel map. */
+ mask[y * w_ + x] = 1;
+ }
+ }
+ }
+ else if (DijkstraPixelIsUnset(dp) || !IsDijkstraPixel(dp)) {
+ /* These are not margin pixels, make sure the extend filter which is run after this step
+ * leaves them alone.
+ */
+ mask[y * w_ + x] = 1;
+ }
+ }
+ }
+ }
+
+ private:
+ float2 uv_to_xy(MLoopUV const &mloopuv) const
+ {
+ float2 ret;
+ ret.x = ((mloopuv.uv[0] * w_) - (0.5f + 0.001f));
+ ret.y = ((mloopuv.uv[1] * h_) - (0.5f + 0.001f));
+ return ret;
+ }
+
+ void build_tables()
+ {
+ loop_to_poly_map_.resize(totloop_);
+ for (int i = 0; i < totpoly_; i++) {
+ for (int j = 0; j < mpoly_[i].totloop; j++) {
+ int l = j + mpoly_[i].loopstart;
+ loop_to_poly_map_[l] = i;
+ }
+ }
+
+ loop_adjacency_map_.resize(totloop_, -1);
+
+ Vector<int> tmpmap;
+ tmpmap.resize(totedge_, -1);
+
+ for (size_t i = 0; i < totloop_; i++) {
+ int edge = mloop_[i].e;
+ if (tmpmap[edge] == -1) {
+ loop_adjacency_map_[i] = -1;
+ tmpmap[edge] = i;
+ }
+ else {
+ BLI_assert(tmpmap[edge] >= 0);
+ loop_adjacency_map_[i] = tmpmap[edge];
+ loop_adjacency_map_[tmpmap[edge]] = i;
+ }
+ }
+ }
+
+ /* Find which edge of the src_poly is closest to x,y. Look up it's adjacent UV-edge and polygon.
+ * Then return the location of the equivalent pixel in the other polygon.
+ * Returns true if a new pixel location was found, false if it wasn't, which can happen if the
+ * margin pixel is on a corner, or the UV-edge doesnt have an adjacent polygon.
+ */
+ bool lookup_pixel(
+ float x, float y, int src_poly, float *r_destx, float *r_desty, int *r_other_poly)
+ {
+ float2 point(x, y);
+
+ *r_destx = *r_desty = 0;
+
+ int found_edge = -1;
+ float found_dist = -1;
+ float found_t = 0;
+
+ /* Find the closest edge on which the point x,y can be projected.
+ */
+ for (size_t i = 0; i < mpoly_[src_poly].totloop; i++) {
+ int l1 = mpoly_[src_poly].loopstart + i;
+ int l2 = l1 + 1;
+ if (l2 >= mpoly_[src_poly].loopstart + mpoly_[src_poly].totloop) {
+ l2 = mpoly_[src_poly].loopstart;
+ }
+ /* edge points */
+ float2 edgepoint1 = uv_to_xy(mloopuv_[l1]);
+ float2 edgepoint2 = uv_to_xy(mloopuv_[l2]);
+ /* Vector AB is the vector from the first edge point to the second edge point.
+ * Vector AP is the vector from the first edge point to our point under investigation. */
+ float2 ab = edgepoint2 - edgepoint1;
+ float2 ap = point - edgepoint1;
+
+ /* Project ap onto ab. */
+ float dotv = math::dot(ab, ap);
+
+ float ablensq = math::length_squared(ab);
+
+ float t = dotv / ablensq;
+
+ if (t >= 0.0 && t <= 1.0) {
+
+ /* Find the point on the edge closest to P */
+ float2 reflect_point = edgepoint1 + (t * ab);
+ /* This is the vector to P, so 90 degrees out from the edge. */
+ float2 reflect_vec = reflect_point - point;
+
+ float reflectLen = sqrt(reflect_vec[0] * reflect_vec[0] + reflect_vec[1] * reflect_vec[1]);
+ float cross = ab[0] * reflect_vec[1] - ab[1] * reflect_vec[0];
+ /* Only if P is on the outside of the edge, which means the cross product is positive,
+ * we consider this edge.
+ */
+ bool valid = (cross > 0.0);
+
+ if (valid && (found_dist < 0 || reflectLen < found_dist)) {
+ /* Stother_ab the info of the closest edge so far. */
+ found_dist = reflectLen;
+ found_t = t;
+ found_edge = i + mpoly_[src_poly].loopstart;
+ }
+ }
+ }
+
+ if (found_edge < 0) {
+ return false;
+ }
+
+ /* Get the 'other' edge. I.E. the UV edge from the neighbour polygon. */
+ int other_edge = loop_adjacency_map_[found_edge];
+
+ if (other_edge < 0) {
+ return false;
+ }
+
+ int dst_poly = loop_to_poly_map_[other_edge];
+
+ if (r_other_poly) {
+ *r_other_poly = dst_poly;
+ }
+
+ int other_edge2 = other_edge + 1;
+ if (other_edge2 >= mpoly_[dst_poly].loopstart + mpoly_[dst_poly].totloop) {
+ other_edge2 = mpoly_[dst_poly].loopstart;
+ }
+
+ float2 other_edgepoint1 = uv_to_xy(mloopuv_[other_edge]);
+ float2 other_edgepoint2 = uv_to_xy(mloopuv_[other_edge2]);
+
+ /* Calculate the vector from the oder edges last point to it's first point. */
+ float2 other_ab = other_edgepoint1 - other_edgepoint2;
+ float2 other_reflect_point = other_edgepoint2 + (found_t * other_ab);
+ float2 perpendicular_other_ab;
+ perpendicular_other_ab.x = other_ab.y;
+ perpendicular_other_ab.y = -other_ab.x;
+
+ /* The new point is dound_dist distance from other_reflect_point at a 90 degree angle to
+ * other_ab */
+ float2 new_point = other_reflect_point + (found_dist / math::length(perpendicular_other_ab)) *
+ perpendicular_other_ab;
+
+ *r_destx = new_point.x;
+ *r_desty = new_point.y;
+
+ return true;
+ }
+}; // class TextureMarginMap
+
+const int TextureMarginMap::directions[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
+
+static void generate_margin(ImBuf *ibuf,
+ char *mask,
+ const int margin,
+ const Mesh *me,
+ DerivedMesh *dm,
+ char const *uv_layer)
+{
+
+ MPoly *mpoly;
+ MLoop *mloop;
+ MLoopUV const *mloopuv;
+ int totpoly, totloop, totedge;
+
+ int tottri;
+ MLoopTri const *looptri;
+ MLoopTri *looptri_mem = NULL;
+
+ if (me) {
+ BLI_assert(dm == NULL);
+ totpoly = me->totpoly;
+ totloop = me->totloop;
+ totedge = me->totedge;
+ mpoly = me->mpoly;
+ mloop = me->mloop;
+
+ if ((uv_layer == NULL) || (uv_layer[0] == '\0')) {
+ mloopuv = static_cast<MLoopUV const *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV));
+ }
+ else {
+ int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer);
+ mloopuv = static_cast<MLoopUV const *>(
+ CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, uv_id));
+ }
+
+ tottri = poly_to_tri_count(me->totpoly, me->totloop);
+ looptri_mem = static_cast<MLoopTri *>(MEM_mallocN(sizeof(*looptri) * tottri, __func__));
+ BKE_mesh_recalc_looptri(
+ me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri_mem);
+ looptri = looptri_mem;
+ }
+ else {
+ BLI_assert(dm != NULL);
+ BLI_assert(me == NULL);
+ BLI_assert(mloopuv == NULL);
+ totpoly = dm->getNumPolys(dm);
+ totedge = dm->getNumEdges(dm);
+ totloop = dm->getNumLoops(dm);
+ mpoly = dm->getPolyArray(dm);
+ mloop = dm->getLoopArray(dm);
+ mloopuv = (MLoopUV const *)dm->getLoopDataArray(dm, CD_MLOOPUV);
+
+ looptri = dm->getLoopTriArray(dm);
+ tottri = dm->getNumLoopTri(dm);
+ }
+
+ TextureMarginMap map(ibuf->x, ibuf->y, mpoly, mloop, mloopuv, totpoly, totloop, totedge);
+
+ bool draw_new_mask = false;
+ /* Now the map contains 3 sorts of values: 0xFFFFFFFF for empty pixels, 0x80000000 + polyindex
+ * for margin pixels, just polyindex for poly pixels.
+ */
+ if (mask) {
+ mask = (char *)MEM_dupallocN(mask);
+ }
+ else {
+ mask = (char *)MEM_callocN(sizeof(char) * ibuf->x * ibuf->y, __func__);
+ draw_new_mask = true;
+ }
+
+ for (int i = 0; i < tottri; i++) {
+ const MLoopTri *lt = &looptri[i];
+ float vec[3][2];
+
+ for (int a = 0; a < 3; a++) {
+ const float *uv = mloopuv[lt->tri[a]].uv;
+
+ /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our
+ * intersection tests where a pixel gets in between 2 faces or the middle of a quad,
+ * camera aligned quads also have this problem but they are less common.
+ * Add a small offset to the UVs, fixes bug T18685. */
+ vec[a][0] = uv[0] * (float)ibuf->x - (0.5f + 0.001f);
+ vec[a][1] = uv[1] * (float)ibuf->y - (0.5f + 0.002f);
+ }
+
+ BLI_assert(lt->poly < 0x80000000); // NOTE: we need the top bit for the dijkstra distance map
+ map.rasterize_tri(vec[0], vec[1], vec[2], lt->poly, draw_new_mask ? mask : NULL);
+ }
+
+ char *tmpmask = (char *)MEM_dupallocN(mask);
+ /* Extend (with averaging) by 2 pixels. Those will be overwritten, but it
+ * helps linear interpolations on the edges of polygons. */
+ IMB_filter_extend(ibuf, tmpmask, 2);
+ MEM_freeN(tmpmask);
+
+ map.grow_dijkstra(margin);
+
+ /* Looking further than 3 polygons away leads to so much cumulative rounding
+ * that it isn't worth it. So hardcode it to 3.
+ */
+ map.lookup_pixels(ibuf, mask, 3);
+
+ /* Use the extend filter to fill in the missing pixels at the corners, not strictly correct, but
+ * the visual difference seems very minimal. This also catches pixels we missed because of very
+ * narrow polygons.
+ */
+ IMB_filter_extend(ibuf, mask, margin);
+
+ MEM_freeN(mask);
+
+ if (looptri_mem) {
+ MEM_freeN(looptri_mem);
+ }
+}
+
+} // namespace blender::render::texturemargin
+
+/**
+ * Generate a margin around the textures uv islands by copying pixels from the adjacent polygon.
+ *
+ * \param ibuf: the texture image.
+ * \param mask: pixels with a mask value of 1 are not written to.
+ * \param margin: the size of the margin in pixels.
+ * \param me: the mesh to use the polygons of.
+ * \param mloopuv: the uv data to use.
+ */
+
+void RE_generate_texturemargin_adjacentfaces(
+ ImBuf *ibuf, char *mask, const int margin, const Mesh *me, char const *uv_layer)
+{
+ blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, NULL, uv_layer);
+}
+
+void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf,
+ char *mask,
+ const int margin,
+ DerivedMesh *dm)
+{
+ blender::render::texturemargin::generate_margin(ibuf, mask, margin, NULL, dm, NULL);
+}