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
path: root/source
diff options
context:
space:
mode:
authorMartijn Versteegh <Baardaap>2022-01-17 20:00:35 +0300
committerBrecht Van Lommel <brecht@blender.org>2022-01-17 21:36:13 +0300
commit449db0ab1e34976f3936310b846ab38cc2d6467d (patch)
treea0eb26c834c1a6b89e5461d219c042abde200b5f /source
parent8af22719d0c98e24104d01089ecb0cff840df7e8 (diff)
Baking: new method to generate margin, based on adjacent faces
This significantly reduces discontinuities on UV seams, by giving a better match of the texture filtered colors on both sides of the seam. It works by using pixels from adjacent faces across the UV seam. This new option is called "Adjacent Faces" and is the default. The old option is called "Extend", and extends border pixels outwards. Differential Revision: https://developer.blender.org/D13303
Diffstat (limited to 'source')
-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
15 files changed, 751 insertions, 26 deletions
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);
+}