Bake: add UDIM tile baking support

Works for both Cycles and multires bake. Triangles are baked to multiple
UDIM images if they span across them, though such UV layouts are generally
discouraged as there is no filtering across UDIM tiles.

The bake margin currently only works within UDIM tiles. For the extend method
this is logical, for the adjacent faces method it may be useful to support
copying pixels from other UDIM tiles, though this seems somewhat complicated.

Fixes T95190

Ref T72390
This commit is contained in:
Brecht Van Lommel 2022-04-22 20:44:49 +02:00
parent bdc537e0a7
commit 6787cc13d4
Notes: blender-bot 2023-06-27 14:46:57 +02:00
Referenced by issue #97553, Regression: Sculpt Brushes not supported in Multiresolution and Dyntopo Mode
Referenced by issue #95190, Multiple UDIM tiles are not baked
Referenced by issue #72390, Improve UDIM functionality
Referenced by issue #109302, Baking in multi-Tiles textures (Udim) from MultiRes only use the height of the last tile as reference
9 changed files with 317 additions and 217 deletions

View File

@ -414,6 +414,8 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
const float uv[2],
float r_uv[2],
float r_ofs[2]);
void BKE_image_get_tile_uv(const struct Image *ima, const int tile_number, float r_uv[2]);
/**
* Return the tile_number for the closest UDIM tile.
*/

View File

@ -829,10 +829,7 @@ ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser)
return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser));
}
int BKE_image_get_tile_from_pos(struct Image *ima,
const float uv[2],
float r_uv[2],
float r_ofs[2])
int BKE_image_get_tile_from_pos(Image *ima, const float uv[2], float r_uv[2], float r_ofs[2])
{
float local_ofs[2];
if (r_ofs == nullptr) {
@ -860,6 +857,18 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
return tile_number;
}
void BKE_image_get_tile_uv(const Image *ima, const int tile_number, float r_uv[2])
{
if (ima->source != IMA_SRC_TILED) {
zero_v2(r_uv);
}
else {
const int tile_index = tile_number - 1001;
r_uv[0] = static_cast<float>(tile_index % 10);
r_uv[1] = static_cast<float>(tile_index / 10);
}
}
int BKE_image_find_nearest_tile(const Image *image, const float co[2])
{
const float co_floor[2] = {floorf(co[0]), floorf(co[1])};
@ -868,17 +877,15 @@ int BKE_image_find_nearest_tile(const Image *image, const float co[2])
int tile_number_best = -1;
LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
const int tile_index = tile->tile_number - 1001;
/* Coordinates of the current tile. */
const float tile_index_co[2] = {static_cast<float>(tile_index % 10),
static_cast<float>(tile_index / 10)};
float uv_offset[2];
BKE_image_get_tile_uv(image, tile->tile_number, uv_offset);
if (equals_v2v2(co_floor, tile_index_co)) {
if (equals_v2v2(co_floor, uv_offset)) {
return tile->tile_number;
}
/* Distance between co[2] and UDIM tile. */
const float dist_sq = len_squared_v2v2(tile_index_co, co);
const float dist_sq = len_squared_v2v2(uv_offset, co);
if (dist_sq < dist_best_sq) {
dist_best_sq = dist_sq;

View File

@ -172,28 +172,35 @@ static bool multiresbake_check(bContext *C, wmOperator *op)
ok = false;
}
else {
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
if (!ibuf) {
BKE_report(op->reports, RPT_ERROR, "Baking should happen to image with image buffer");
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
if (!ibuf) {
BKE_report(
op->reports, RPT_ERROR, "Baking should happen to image with image buffer");
ok = false;
}
else {
if (ibuf->rect == NULL && ibuf->rect_float == NULL) {
ok = false;
}
else {
if (ibuf->rect == NULL && ibuf->rect_float == NULL) {
ok = false;
}
if (ibuf->rect_float && !(ELEM(ibuf->channels, 0, 4))) {
ok = false;
if (ibuf->rect_float && !(ELEM(ibuf->channels, 0, 4))) {
ok = false;
}
if (!ok) {
BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type");
}
}
if (!ok) {
BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type");
}
BKE_image_release_ibuf(ima, ibuf, NULL);
}
BKE_image_release_ibuf(ima, ibuf, NULL);
}
}
}
@ -274,21 +281,27 @@ static void clear_single_image(Image *image, ClearFlag flag)
const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f};
if ((image->id.tag & LIB_TAG_DOIT) == 0) {
ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL);
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
if (flag == CLEAR_TANGENT_NORMAL) {
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
}
else if (flag == CLEAR_DISPLACEMENT) {
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
}
else {
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
}
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, NULL);
image->id.tag |= LIB_TAG_DOIT;
if (flag == CLEAR_TANGENT_NORMAL) {
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
}
else if (flag == CLEAR_DISPLACEMENT) {
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
}
else {
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
}
BKE_image_release_ibuf(image, ibuf, NULL);
image->id.tag |= LIB_TAG_DOIT;
BKE_image_release_ibuf(image, ibuf, NULL);
}
}
}

View File

@ -19,6 +19,7 @@
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BKE_context.h"
#include "BKE_global.h"
@ -165,6 +166,7 @@ static void bake_update_image(ScrArea *area, Image *image)
}
static bool write_internal_bake_pixels(Image *image,
const int image_tile_number,
BakePixel pixel_array[],
float *buffer,
const int width,
@ -174,7 +176,8 @@ static bool write_internal_bake_pixels(Image *image,
const bool is_clear,
const bool is_noncolor,
Mesh const *mesh_eval,
char const *uv_layer)
char const *uv_layer,
const float uv_offset[2])
{
ImBuf *ibuf;
void *lock;
@ -182,7 +185,10 @@ static bool write_internal_bake_pixels(Image *image,
char *mask_buffer = NULL;
const size_t pixels_num = (size_t)width * (size_t)height;
ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = image_tile_number;
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
if (!ibuf) {
return false;
@ -270,7 +276,7 @@ static bool write_internal_bake_pixels(Image *image,
/* margins */
if (margin > 0) {
RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer);
RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer, uv_offset);
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
@ -303,10 +309,8 @@ static void bake_targets_refresh(BakeTargets *targets)
if (ima) {
BKE_image_partial_update_mark_full_update(ima);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
}
BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
}
}
}
@ -321,7 +325,8 @@ static bool write_external_bake_pixels(const char *filepath,
ImageFormatData *im_format,
const bool is_noncolor,
Mesh const *mesh_eval,
char const *uv_layer)
char const *uv_layer,
const float uv_offset[2])
{
ImBuf *ibuf = NULL;
bool ok = false;
@ -378,7 +383,7 @@ static bool write_external_bake_pixels(const char *filepath,
mask_buffer = MEM_callocN(sizeof(char) * pixels_num, "Bake Mask");
RE_bake_mask_fill(pixel_array, pixels_num, mask_buffer);
RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer);
RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer, uv_offset);
if (mask_buffer) {
MEM_freeN(mask_buffer);
@ -467,7 +472,6 @@ static bool bake_object_check(ViewLayer *view_layer,
ED_object_get_active_image(ob, mat_nr, &image, NULL, &node, &ntree);
if (image) {
ImBuf *ibuf;
if (node) {
if (BKE_node_is_connected_to_output(ntree, node)) {
@ -482,21 +486,27 @@ static bool bake_object_check(ViewLayer *view_layer,
}
}
void *lock;
ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
if (ibuf) {
BKE_image_release_ibuf(image, ibuf, lock);
}
else {
BKE_reportf(reports,
RPT_ERROR,
"Uninitialized image \"%s\" from object \"%s\"",
image->id.name + 2,
ob->id.name + 2);
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
BKE_image_release_ibuf(image, ibuf, lock);
return false;
if (ibuf) {
BKE_image_release_ibuf(image, ibuf, lock);
}
else {
BKE_reportf(reports,
RPT_ERROR,
"Uninitialized image \"%s\" from object \"%s\"",
image->id.name + 2,
ob->id.name + 2);
BKE_image_release_ibuf(image, ibuf, lock);
return false;
}
}
}
else {
@ -687,36 +697,35 @@ static bool bake_targets_init_image_textures(const BakeAPIRender *bkr,
}
}
/* Over-allocate in case there is more materials than images. */
/* Allocate material mapping. */
targets->materials_num = materials_num;
targets->images = MEM_callocN(sizeof(BakeImage) * targets->materials_num, "BakeTargets.images");
targets->material_to_image = MEM_callocN(sizeof(int) * targets->materials_num,
targets->material_to_image = MEM_callocN(sizeof(Image *) * targets->materials_num,
"BakeTargets.material_to_image");
/* Error handling and tag (in case multiple materials share the same image). */
BKE_main_id_tag_idcode(bkr->main, ID_IM, LIB_TAG_DOIT, false);
targets->images = NULL;
for (int i = 0; i < materials_num; i++) {
Image *image;
ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
/* Some materials have no image, we just ignore those cases. */
if (image == NULL) {
targets->material_to_image[i] = -1;
}
else if (image->id.tag & LIB_TAG_DOIT) {
for (int j = 0; j < i; j++) {
if (targets->images[j].image == image) {
targets->material_to_image[i] = j;
break;
}
targets->material_to_image[i] = image;
/* Some materials have no image, we just ignore those cases.
* Also setup each image only once. */
if (image && !(image->id.tag & LIB_TAG_DOIT)) {
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
/* Add bake image. */
targets->images = MEM_recallocN(targets->images,
sizeof(BakeImage) * (targets->images_num + 1));
targets->images[targets->images_num].image = image;
targets->images[targets->images_num].tile_number = tile->tile_number;
targets->images_num++;
}
}
else {
targets->material_to_image[i] = targets->images_num;
targets->images[targets->images_num].image = image;
image->id.tag |= LIB_TAG_DOIT;
targets->images_num++;
}
}
@ -735,13 +744,19 @@ static bool bake_targets_init_internal(const BakeAPIRender *bkr,
/* Saving to image datablocks. */
for (int i = 0; i < targets->images_num; i++) {
BakeImage *bk_image = &targets->images[i];
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = bk_image->tile_number;
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(bk_image->image, NULL, &lock);
ImBuf *ibuf = BKE_image_acquire_ibuf(bk_image->image, &iuser, &lock);
if (ibuf) {
bk_image->width = ibuf->x;
bk_image->height = ibuf->y;
bk_image->offset = targets->pixels_num;
BKE_image_get_tile_uv(bk_image->image, bk_image->tile_number, bk_image->uv_offset);
targets->pixels_num += (size_t)ibuf->x * (size_t)ibuf->y;
}
@ -768,6 +783,7 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr,
for (int i = 0; i < targets->images_num; i++) {
BakeImage *bk_image = &targets->images[i];
const bool ok = write_internal_bake_pixels(bk_image->image,
bk_image->tile_number,
pixel_array + bk_image->offset,
targets->result +
bk_image->offset * targets->channels_num,
@ -778,7 +794,8 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr,
bkr->is_clear,
targets->is_noncolor,
mesh_eval,
bkr->uv_layer);
bkr->uv_layer,
bk_image->uv_offset);
/* might be read by UI to set active image for display */
bake_update_image(bkr->area, bk_image->image);
@ -815,7 +832,6 @@ static bool bake_targets_init_external(const BakeAPIRender *bkr,
bk_image->width = bkr->width;
bk_image->height = bkr->height;
bk_image->offset = targets->pixels_num;
bk_image->image = NULL;
targets->pixels_num += (size_t)bkr->width * (size_t)bkr->height;
@ -827,7 +843,7 @@ static bool bake_targets_init_external(const BakeAPIRender *bkr,
if (!bkr->is_split_materials) {
/* saving a single image */
for (int i = 0; i < targets->materials_num; i++) {
targets->material_to_image[i] = 0;
targets->material_to_image[i] = targets->images[0].image;
}
}
@ -865,25 +881,26 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
}
if (bkr->is_split_materials) {
if (bk_image->image) {
BLI_path_suffix(name, FILE_MAX, bk_image->image->id.name + 2, "_");
if (ob_eval->mat[i]) {
BLI_path_suffix(name, FILE_MAX, ob_eval->mat[i]->id.name + 2, "_");
}
else if (mesh_eval->mat[i]) {
BLI_path_suffix(name, FILE_MAX, mesh_eval->mat[i]->id.name + 2, "_");
}
else {
if (ob_eval->mat[i]) {
BLI_path_suffix(name, FILE_MAX, ob_eval->mat[i]->id.name + 2, "_");
}
else if (mesh_eval->mat[i]) {
BLI_path_suffix(name, FILE_MAX, mesh_eval->mat[i]->id.name + 2, "_");
}
else {
/* if everything else fails, use the material index */
char tmp[5];
sprintf(tmp, "%d", i % 1000);
BLI_path_suffix(name, FILE_MAX, tmp, "_");
}
/* if everything else fails, use the material index */
char tmp[5];
sprintf(tmp, "%d", i % 1000);
BLI_path_suffix(name, FILE_MAX, tmp, "_");
}
}
if (bk_image->tile_number) {
char tmp[FILE_MAX];
SNPRINTF(tmp, "%d", bk_image->tile_number);
BLI_path_suffix(name, FILE_MAX, tmp, "_");
}
/* save it externally */
const bool ok = write_external_bake_pixels(name,
pixel_array + bk_image->offset,
@ -896,7 +913,8 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
&bake->im_format,
targets->is_noncolor,
mesh_eval,
bkr->uv_layer);
bkr->uv_layer,
bk_image->uv_offset);
if (!ok) {
BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name);

View File

@ -19,6 +19,8 @@ extern "C" {
typedef struct BakeImage {
struct Image *image;
int tile_number;
float uv_offset[2];
int width;
int height;
size_t offset;
@ -30,7 +32,7 @@ typedef struct BakeTargets {
int images_num;
/* Lookup table from Material number to BakeImage. */
int *material_to_image;
struct Image **material_to_image;
int materials_num;
/* Pixel buffer to bake to. */
@ -104,7 +106,8 @@ void RE_bake_margin(struct ImBuf *ibuf,
int margin,
char margin_type,
struct Mesh const *me,
char const *uv_layer);
char const *uv_layer,
const float uv_offset[2]);
void RE_bake_normal_world_to_object(const BakePixel pixel_array[],
size_t pixels_num,

View File

@ -25,13 +25,18 @@ struct Mesh;
* \param me: the mesh to use the polygons of.
* \param uv_layer: The UV layer to use.
*/
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(struct ImBuf *ibuf,
char *mask,
const int margin,
struct Mesh const *me,
char const *uv_layer,
const float uv_offset[2]);
void RE_generate_texturemargin_adjacentfaces_dm(struct ImBuf *ibuf,
char *mask,
const int margin,
struct DerivedMesh *dm);
struct DerivedMesh *dm,
const float uv_offset[2]);
#ifdef __cplusplus
}

View File

@ -146,12 +146,13 @@ void RE_bake_margin(ImBuf *ibuf,
const int margin,
const char margin_type,
Mesh const *me,
char const *uv_layer)
char const *uv_layer,
const float uv_offset[2])
{
/* margin */
switch (margin_type) {
case R_BAKE_ADJACENT_FACES:
RE_generate_texturemargin_adjacentfaces(ibuf, mask, margin, me, uv_layer);
RE_generate_texturemargin_adjacentfaces(ibuf, mask, margin, me, uv_layer, uv_offset);
break;
default:
/* fall through */
@ -746,30 +747,36 @@ void RE_bake_pixels_populate(Mesh *me,
for (int i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
const MPoly *mp = &me->mpoly[lt->poly];
float vec[3][2];
int mat_nr = mp->mat_nr;
int image_id = targets->material_to_image[mat_nr];
if (image_id < 0) {
continue;
}
bd.bk_image = &targets->images[image_id];
bd.primitive_id = i;
for (int a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;
/* Find images matching this material. */
Image *image = targets->material_to_image[mp->mat_nr];
for (int image_id = 0; image_id < targets->images_num; image_id++) {
BakeImage *bk_image = &targets->images[image_id];
if (bk_image->image != image) {
continue;
}
/* 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)bd.bk_image->width - (0.5f + 0.001f);
vec[a][1] = uv[1] * (float)bd.bk_image->height - (0.5f + 0.002f);
/* Compute triangle vertex UV coordinates. */
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] - bk_image->uv_offset[0]) * (float)bk_image->width - (0.5f + 0.001f);
vec[a][1] = (uv[1] - bk_image->uv_offset[1]) * (float)bk_image->height - (0.5f + 0.002f);
}
/* Rasterize triangle. */
bd.bk_image = bk_image;
bake_differentials(&bd, vec[0], vec[1], vec[2]);
zspan_scanconvert(
&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel);
}
bake_differentials(&bd, vec[0], vec[1], vec[2]);
zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel);
}
for (int i = 0; i < targets->images_num; i++) {

View File

@ -51,7 +51,7 @@ typedef void (*MPassKnownData)(DerivedMesh *lores_dm,
const int x,
const int y);
typedef void *(*MInitBakeData)(MultiresBakeRender *bkr, Image *ima);
typedef void *(*MInitBakeData)(MultiresBakeRender *bkr, ImBuf *ibuf);
typedef void (*MFreeBakeData)(void *bake_data);
typedef struct MultiresBakeResult {
@ -64,6 +64,7 @@ typedef struct {
MPoly *mpoly;
MLoop *mloop;
MLoopUV *mloopuv;
float uv_offset[2];
const MLoopTri *mlooptri;
float *pvtangent;
const float (*precomputed_normals)[3];
@ -91,7 +92,6 @@ typedef struct {
typedef struct {
float *heights;
Image *ima;
DerivedMesh *ssdm;
const int *orig_index_mp_to_orig;
} MHeightBakeData;
@ -148,7 +148,8 @@ static void init_bake_rast(MBakeRast *bake_rast,
static void flush_pixel(const MResolvePixelData *data, const int x, const int y)
{
const float st[2] = {(x + 0.5f) / data->w, (y + 0.5f) / data->h};
const float st[2] = {(x + 0.5f) / data->w + data->uv_offset[0],
(y + 0.5f) / data->h + data->uv_offset[1]};
const float *st0, *st1, *st2;
const float *tang0, *tang1, *tang2;
float no0[3], no1[3], no2[3];
@ -395,8 +396,12 @@ static void *do_multires_bake_thread(void *data_v)
data->tri_index = tri_index;
bake_rasterize(
bake_rast, mloopuv[lt->tri[0]].uv, mloopuv[lt->tri[1]].uv, mloopuv[lt->tri[2]].uv);
float uv[3][2];
sub_v2_v2v2(uv[0], mloopuv[lt->tri[0]].uv, data->uv_offset);
sub_v2_v2v2(uv[1], mloopuv[lt->tri[1]].uv, data->uv_offset);
sub_v2_v2v2(uv[2], mloopuv[lt->tri[2]].uv, data->uv_offset);
bake_rasterize(bake_rast, uv[0], uv[1], uv[2]);
/* tag image buffer for refresh */
if (data->ibuf->rect_float) {
@ -447,6 +452,8 @@ static void init_ccgdm_arrays(DerivedMesh *dm)
static void do_multires_bake(MultiresBakeRender *bkr,
Image *ima,
ImageTile *tile,
ImBuf *ibuf,
bool require_tangent,
MPassKnownData passKnownData,
MInitBakeData initBakeData,
@ -462,7 +469,6 @@ static void do_multires_bake(MultiresBakeRender *bkr,
MultiresBakeThread *handles;
MultiresBakeQueue queue;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
MVert *mvert = dm->getVertArray(dm);
MPoly *mpoly = dm->getPolyArray(dm);
MLoop *mloop = dm->getLoopArray(dm);
@ -511,7 +517,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
/* all threads shares the same custom bake data */
if (initBakeData) {
bake_data = initBakeData(bkr, ima);
bake_data = initBakeData(bkr, ibuf);
}
if (tot_thread > 1) {
@ -539,6 +545,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
handle->data.mvert = mvert;
handle->data.vert_normals = vert_normals;
handle->data.mloopuv = mloopuv;
BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset);
handle->data.mlooptri = mlooptri;
handle->data.mloop = mloop;
handle->data.pvtangent = pvtangent;
@ -590,8 +597,6 @@ static void do_multires_bake(MultiresBakeRender *bkr,
MEM_freeN(handles);
BKE_id_free(NULL, temp_mesh);
BKE_image_release_ibuf(ima, ibuf, NULL);
}
}
@ -758,10 +763,9 @@ static void interp_barycentric_mlooptri(DerivedMesh *dm,
/* **************** Displacement Baker **************** */
static void *init_heights_data(MultiresBakeRender *bkr, Image *ima)
static void *init_heights_data(MultiresBakeRender *bkr, ImBuf *ibuf)
{
MHeightBakeData *height_data;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
DerivedMesh *lodm = bkr->lores_dm;
BakeImBufuserData *userdata = ibuf->userdata;
@ -772,7 +776,6 @@ static void *init_heights_data(MultiresBakeRender *bkr, Image *ima)
height_data = MEM_callocN(sizeof(MHeightBakeData), "MultiresBake heightData");
height_data->ima = ima;
height_data->heights = userdata->displacement_buffer;
if (!bkr->use_lores_mesh) {
@ -794,8 +797,6 @@ static void *init_heights_data(MultiresBakeRender *bkr, Image *ima)
height_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX);
BKE_image_release_ibuf(ima, ibuf, NULL);
return (void *)height_data;
}
@ -903,7 +904,7 @@ static void apply_heights_callback(DerivedMesh *lores_dm,
/* **************** Normal Maps Baker **************** */
static void *init_normal_data(MultiresBakeRender *bkr, Image *UNUSED(ima))
static void *init_normal_data(MultiresBakeRender *bkr, ImBuf *UNUSED(ibuf))
{
MNormalBakeData *normal_data;
DerivedMesh *lodm = bkr->lores_dm;
@ -1099,7 +1100,7 @@ static void create_ao_raytree(MultiresBakeRender *bkr, MAOBakeData *ao_data)
RE_rayobject_done(raytree);
}
static void *init_ao_data(MultiresBakeRender *bkr, Image *UNUSED(ima))
static void *init_ao_data(MultiresBakeRender *bkr, ImBuf *UNUSED(ibuf))
{
MAOBakeData *ao_data;
DerivedMesh *lodm = bkr->lores_dm;
@ -1313,8 +1314,12 @@ static void apply_ao_callback(DerivedMesh *lores_dm,
/* ******$***************** Post processing ************************* */
static void bake_ibuf_filter(
ImBuf *ibuf, char *mask, const int margin, const char margin_type, DerivedMesh *dm)
static void bake_ibuf_filter(ImBuf *ibuf,
char *mask,
const int margin,
const char margin_type,
DerivedMesh *dm,
const float uv_offset[2])
{
/* must check before filtering */
const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf);
@ -1322,7 +1327,7 @@ static void bake_ibuf_filter(
if (margin) {
switch (margin_type) {
case R_BAKE_ADJACENT_FACES:
RE_generate_texturemargin_adjacentfaces_dm(ibuf, mask, margin, dm);
RE_generate_texturemargin_adjacentfaces_dm(ibuf, mask, margin, dm, uv_offset);
break;
default:
/* fall through */
@ -1427,38 +1432,54 @@ static void bake_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
for (link = bkr->image.first; link; link = link->next) {
Image *ima = (Image *)link->data;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
if (ibuf->x > 0 && ibuf->y > 0) {
BakeImBufuserData *userdata = MEM_callocN(sizeof(BakeImBufuserData),
"MultiresBake userdata");
userdata->mask_buffer = MEM_callocN(ibuf->y * ibuf->x, "MultiresBake imbuf mask");
ibuf->userdata = userdata;
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
switch (bkr->mode) {
case RE_BAKE_NORMALS:
do_multires_bake(
bkr, ima, true, apply_tangmat_callback, init_normal_data, free_normal_data, result);
break;
case RE_BAKE_DISPLACEMENT:
do_multires_bake(bkr,
ima,
false,
apply_heights_callback,
init_heights_data,
free_heights_data,
result);
break;
/* TODO: restore ambient occlusion baking support. */
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
if (ibuf->x > 0 && ibuf->y > 0) {
BakeImBufuserData *userdata = MEM_callocN(sizeof(BakeImBufuserData),
"MultiresBake userdata");
userdata->mask_buffer = MEM_callocN(ibuf->y * ibuf->x, "MultiresBake imbuf mask");
ibuf->userdata = userdata;
switch (bkr->mode) {
case RE_BAKE_NORMALS:
do_multires_bake(bkr,
ima,
tile,
ibuf,
true,
apply_tangmat_callback,
init_normal_data,
free_normal_data,
result);
break;
case RE_BAKE_DISPLACEMENT:
do_multires_bake(bkr,
ima,
tile,
ibuf,
false,
apply_heights_callback,
init_heights_data,
free_heights_data,
result);
break;
/* TODO: restore ambient occlusion baking support. */
#if 0
case RE_BAKE_AO:
do_multires_bake(bkr, ima, false, apply_ao_callback, init_ao_data, free_ao_data, result);
break;
case RE_BAKE_AO:
do_multires_bake(bkr, ima, tile, ibuf, false, apply_ao_callback, init_ao_data, free_ao_data, result);
break;
#endif
}
}
}
BKE_image_release_ibuf(ima, ibuf, NULL);
BKE_image_release_ibuf(ima, ibuf, NULL);
}
ima->id.tag |= LIB_TAG_DOIT;
}
@ -1471,48 +1492,62 @@ static void finish_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
for (link = bkr->image.first; link; link = link->next) {
Image *ima = (Image *)link->data;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata;
if (ibuf->x <= 0 || ibuf->y <= 0) {
continue;
}
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
if (use_displacement_buffer) {
bake_ibuf_normalize_displacement(ibuf,
userdata->displacement_buffer,
userdata->mask_buffer,
result->height_min,
result->height_max);
}
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata;
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);
if (ibuf->rect_float) {
ibuf->userflags |= IB_RECT_INVALID;
}
if (ibuf->mipmap[0]) {
ibuf->userflags |= IB_MIPMAP_INVALID;
imb_freemipmapImBuf(ibuf);
}
if (ibuf->userdata) {
if (userdata->displacement_buffer) {
MEM_freeN(userdata->displacement_buffer);
if (ibuf->x <= 0 || ibuf->y <= 0) {
continue;
}
MEM_freeN(userdata->mask_buffer);
MEM_freeN(userdata);
ibuf->userdata = NULL;
}
if (use_displacement_buffer) {
bake_ibuf_normalize_displacement(ibuf,
userdata->displacement_buffer,
userdata->mask_buffer,
result->height_min,
result->height_max);
}
BKE_image_release_ibuf(ima, ibuf, NULL);
DEG_id_tag_update(&ima->id, 0);
float uv_offset[2];
BKE_image_get_tile_uv(ima, tile->tile_number, uv_offset);
bake_ibuf_filter(ibuf,
userdata->mask_buffer,
bkr->bake_margin,
bkr->bake_margin_type,
bkr->lores_dm,
uv_offset);
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
BKE_image_mark_dirty(ima, ibuf);
if (ibuf->rect_float) {
ibuf->userflags |= IB_RECT_INVALID;
}
if (ibuf->mipmap[0]) {
ibuf->userflags |= IB_MIPMAP_INVALID;
imb_freemipmapImBuf(ibuf);
}
if (ibuf->userdata) {
if (userdata->displacement_buffer) {
MEM_freeN(userdata->displacement_buffer);
}
MEM_freeN(userdata->mask_buffer);
MEM_freeN(userdata);
ibuf->userdata = NULL;
}
BKE_image_release_ibuf(ima, ibuf, NULL);
DEG_id_tag_update(&ima->id, 0);
}
}
}

View File

@ -46,6 +46,7 @@ class TextureMarginMap {
Vector<int> loop_to_poly_map_;
int w_, h_;
float uv_offset_[2];
Vector<uint32_t> pixel_data_;
ZSpan zspan_;
uint32_t value_to_store_;
@ -61,6 +62,7 @@ class TextureMarginMap {
public:
TextureMarginMap(size_t w,
size_t h,
const float uv_offset[2],
MPoly const *mpoly,
MLoop const *mloop,
MLoopUV const *mloopuv,
@ -76,6 +78,8 @@ class TextureMarginMap {
totloop_(totloop),
totedge_(totedge)
{
copy_v2_v2(uv_offset_, uv_offset);
pixel_data_.resize(w_ * h_, 0xFFFFFFFF);
zbuf_alloc_span(&zspan_, w_, h_);
@ -277,8 +281,8 @@ class TextureMarginMap {
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));
ret.x = (((mloopuv.uv[0] - uv_offset_[0]) * w_) - (0.5f + 0.001f));
ret.y = (((mloopuv.uv[1] - uv_offset_[1]) * h_) - (0.5f + 0.001f));
return ret;
}
@ -482,7 +486,8 @@ static void generate_margin(ImBuf *ibuf,
const int margin,
const Mesh *me,
DerivedMesh *dm,
char const *uv_layer)
char const *uv_layer,
const float uv_offset[2])
{
MPoly *mpoly;
@ -531,7 +536,8 @@ static void generate_margin(ImBuf *ibuf,
tottri = dm->getNumLoopTri(dm);
}
TextureMarginMap map(ibuf->x, ibuf->y, mpoly, mloop, mloopuv, totpoly, totloop, totedge);
TextureMarginMap map(
ibuf->x, ibuf->y, uv_offset, 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`
@ -555,8 +561,8 @@ static void generate_margin(ImBuf *ibuf,
* 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);
vec[a][0] = (uv[0] - uv_offset[0]) * (float)ibuf->x - (0.5f + 0.001f);
vec[a][1] = (uv[1] - uv_offset[1]) * (float)ibuf->y - (0.5f + 0.002f);
}
/* NOTE: we need the top bit for the dijkstra distance map. */
@ -592,16 +598,20 @@ static void generate_margin(ImBuf *ibuf,
} // namespace blender::render::texturemargin
void RE_generate_texturemargin_adjacentfaces(
ImBuf *ibuf, char *mask, const int margin, const Mesh *me, char const *uv_layer)
void RE_generate_texturemargin_adjacentfaces(ImBuf *ibuf,
char *mask,
const int margin,
const Mesh *me,
char const *uv_layer,
const float uv_offset[2])
{
blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, nullptr, uv_layer);
blender::render::texturemargin::generate_margin(
ibuf, mask, margin, me, nullptr, uv_layer, uv_offset);
}
void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf,
char *mask,
const int margin,
DerivedMesh *dm)
void RE_generate_texturemargin_adjacentfaces_dm(
ImBuf *ibuf, char *mask, const int margin, DerivedMesh *dm, const float uv_offset[2])
{
blender::render::texturemargin::generate_margin(ibuf, mask, margin, nullptr, dm, nullptr);
blender::render::texturemargin::generate_margin(
ibuf, mask, margin, nullptr, dm, nullptr, uv_offset);
}