Compare commits
166 Commits
temp-ui-cp
...
temp-T9735
Author | SHA1 | Date | |
---|---|---|---|
0e4a04a07f | |||
c4b20425bd | |||
f39e871490 | |||
c8f3ec797c | |||
2e046183a2 | |||
654e242d43 | |||
9ce9cf5a31 | |||
e80c6c48aa | |||
34ff126470 | |||
4ce7dab4c6 | |||
cd277c94e7 | |||
f7c0638258 | |||
0e5a73fe8a | |||
87ea1d1d63 | |||
9cbfcc7f3c | |||
0bd2f9dcff | |||
d96172376d | |||
f43a9e6c65 | |||
ca4de164a5 | |||
7b4bc72a49 | |||
ab26e4d6a7 | |||
44126f76ea | |||
cfed819761 | |||
![]() |
7c1556a497 | ||
2decd837f6 | |||
fb66386beb | |||
e0a1299aee | |||
7cc51a464c | |||
245fb6472c | |||
3a2fb294b8 | |||
b577f11a5e | |||
998d541568 | |||
c89e73209a | |||
86d9ec6df7 | |||
4b281ecd74 | |||
5c5d5b27a0 | |||
ab8c980365 | |||
11bcb614b5 | |||
77e47b7e01 | |||
aef482b72f | |||
0e4ec7d9d3 | |||
9852c6cf63 | |||
c627d7ede2 | |||
1f8e9ad6de | |||
5ba160ce96 | |||
9daf4a8040 | |||
f6bac97d16 | |||
ae13c11a95 | |||
c6da2a827f | |||
211ba8df0b | |||
248339a1cb | |||
ae027413f4 | |||
9b16782aef | |||
83d4cf9db1 | |||
809501289d | |||
d3918312d8 | |||
01f79f7c93 | |||
ea486bcd3d | |||
1ec0129cfa | |||
cf8b29950b | |||
66b03b7e3b | |||
0947875637 | |||
798db23477 | |||
21caf27fa9 | |||
b7e511373a | |||
f49adba9c1 | |||
7c181c8982 | |||
b57ea0330c | |||
167da68133 | |||
701c225675 | |||
9112d13e69 | |||
9a0180e3e7 | |||
10509846db | |||
ca7d7e02c3 | |||
67e831af28 | |||
386f0e47e4 | |||
83dbe62e71 | |||
b1b5113b99 | |||
5442e0720f | |||
4498e0ea6b | |||
7b4103eed1 | |||
73e0b48e41 | |||
4d2a4f8582 | |||
af1f0e166d | |||
6469dcfe41 | |||
da5662cd0f | |||
4fcf554088 | |||
9863f5359f | |||
5b7ed8bea0 | |||
2cd6dfc44f | |||
f79da200c5 | |||
220b10a689 | |||
ab6c400d14 | |||
5c25182aff | |||
bfb9faaded | |||
3bf7f06a26 | |||
067ffe96a4 | |||
cf9f997d80 | |||
64f1c223f9 | |||
84df064cfa | |||
66c4eee016 | |||
c8b78bfa8e | |||
175dfbb708 | |||
8acf269817 | |||
0867c2401b | |||
e5a8128575 | |||
81669eeb0c | |||
3f4c47e96e | |||
04cd5303fb | |||
![]() |
1be8a1260c | ||
28d1c86b49 | |||
520e80d9cc | |||
82c2e61fbd | |||
cd11b92f2d | |||
bf066f9393 | |||
fc461c1c25 | |||
e0abad2f6a | |||
9088eac81b | |||
1fc4efd966 | |||
16aed16ec0 | |||
238b442413 | |||
205b4c3742 | |||
0b77f7a542 | |||
a256d4bf36 | |||
0c8b59493d | |||
![]() |
ed305cb012 | ||
99a147d934 | |||
f737a254eb | |||
![]() |
467b1e2d72 | ||
![]() |
ae4894f537 | ||
![]() |
a2db5ee3e5 | ||
![]() |
cff0866223 | ||
![]() |
3e8a9160ba | ||
![]() |
07d11907a9 | ||
![]() |
2d7e3359c8 | ||
23de2dae5e | |||
8a59a65fd9 | |||
f873802493 | |||
57504d1b11 | |||
42ddd87fa5 | |||
![]() |
69efc30bd8 | ||
![]() |
596a9c6983 | ||
8e7066f949 | |||
631a49ce5f | |||
b6d5c010e6 | |||
4a6a65f214 | |||
8a6b74b325 | |||
2d6ca66b7f | |||
f3f77d2175 | |||
093ebb71ee | |||
90cdb12add | |||
d60bbfdc18 | |||
680f95ab4d | |||
eca2804a86 | |||
c1945a07c2 | |||
b8c0a6c999 | |||
ca30a541a0 | |||
7137951f9c | |||
27a2c1c261 | |||
560614c0dc | |||
843752d834 | |||
7fb08a4ff5 | |||
e2c545e1b4 | |||
81f6998d65 | |||
e3203434c3 | |||
9a2b06bad8 |
@@ -132,6 +132,28 @@ struct UDIMTilePixels {
|
||||
}
|
||||
};
|
||||
|
||||
struct SeamFixCopy {
|
||||
int2 src_pixel;
|
||||
int2 dst_pixel;
|
||||
};
|
||||
struct SeamFixBlend {
|
||||
float2 src_pixel;
|
||||
int2 dst_pixel;
|
||||
char src_bit_mask;
|
||||
};
|
||||
|
||||
struct UDIMSeamFixes {
|
||||
uint16_t src_tile_number;
|
||||
uint16_t dst_tile_number;
|
||||
Vector<SeamFixCopy> pixels_to_copy;
|
||||
Vector<SeamFixBlend> pixels_to_blend;
|
||||
|
||||
UDIMSeamFixes(uint16_t src_tile_number, uint16_t dst_tile_number)
|
||||
: src_tile_number(src_tile_number), dst_tile_number(dst_tile_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeData {
|
||||
struct {
|
||||
bool dirty : 1;
|
||||
@@ -139,6 +161,7 @@ struct NodeData {
|
||||
|
||||
Vector<UDIMTilePixels> tiles;
|
||||
Triangles triangles;
|
||||
Vector<UDIMSeamFixes> seams;
|
||||
|
||||
NodeData()
|
||||
{
|
||||
@@ -155,6 +178,17 @@ struct NodeData {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UDIMSeamFixes &ensure_seam_fixes(uint16_t src_tile_number, uint16_t dst_tile_number)
|
||||
{
|
||||
for (UDIMSeamFixes &fixes : seams) {
|
||||
if (fixes.src_tile_number == src_tile_number && fixes.dst_tile_number == dst_tile_number) {
|
||||
return fixes;
|
||||
}
|
||||
}
|
||||
seams.append(UDIMSeamFixes(src_tile_number, dst_tile_number));
|
||||
return seams.last();
|
||||
}
|
||||
|
||||
void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer)
|
||||
{
|
||||
UDIMTilePixels *tile = find_tile_data(image_tile);
|
||||
@@ -181,4 +215,8 @@ struct NodeData {
|
||||
NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node);
|
||||
void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user);
|
||||
|
||||
void BKE_pbvh_pixels_rebuild_seams(
|
||||
PBVH *pbvh, const Mesh *me, Image *image, ImageUser *image_user, const MLoopUV *ldata_uv);
|
||||
void BKE_pbvh_pixels_fix_seams(PBVHNode *node, Image *image, ImageUser *image_user);
|
||||
|
||||
} // namespace blender::bke::pbvh::pixels
|
||||
|
@@ -247,6 +247,7 @@ set(SRC
|
||||
intern/pbvh.cc
|
||||
intern/pbvh_bmesh.c
|
||||
intern/pbvh_pixels.cc
|
||||
intern/pbvh_pixels_seams.cc
|
||||
intern/pointcache.c
|
||||
intern/pointcloud.cc
|
||||
intern/preferences.c
|
||||
|
@@ -113,6 +113,7 @@ char *BKE_paint_canvas_key_get(struct PaintModeSettings *settings, struct Object
|
||||
Image *image;
|
||||
ImageUser *image_user;
|
||||
if (BKE_paint_canvas_image_get(settings, ob, &image, &image_user)) {
|
||||
ss << ",SEAM_DIST:" << image->seamfix_distance;
|
||||
ImageUser tile_user = *image_user;
|
||||
LISTBASE_FOREACH (ImageTile *, image_tile, &image->tiles) {
|
||||
tile_user.tile = image_tile->tile_number;
|
||||
|
@@ -259,16 +259,47 @@ static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_us
|
||||
pixel_row.start_image_coordinate.x;
|
||||
for (int x = 0; x < pixel_row.num_pixels; x++) {
|
||||
if (image_buffer->rect_float) {
|
||||
copy_v4_fl(&image_buffer->rect_float[pixel_offset * 4], 1.0);
|
||||
copy_v4_fl4(&image_buffer->rect_float[pixel_offset * 4], 0.0f, 0.5f, 0.0f, 1.0f);
|
||||
}
|
||||
if (image_buffer->rect) {
|
||||
uint8_t *dest = static_cast<uint8_t *>(
|
||||
static_cast<void *>(&image_buffer->rect[pixel_offset]));
|
||||
copy_v4_uchar(dest, 255);
|
||||
static uint8_t color[] = {0, 128, 0, 255};
|
||||
copy_v4_v4_uchar(dest, color);
|
||||
}
|
||||
pixel_offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (UDIMSeamFixes &fixes : node_data->seams) {
|
||||
if (fixes.dst_tile_number != image_tile.get_tile_number()) {
|
||||
continue;
|
||||
}
|
||||
for (SeamFixCopy &fix : fixes.pixels_to_copy) {
|
||||
int pixel_offset = fix.dst_pixel.y * image_buffer->x + fix.dst_pixel.x;
|
||||
if (image_buffer->rect_float != nullptr) {
|
||||
copy_v4_fl4(&image_buffer->rect_float[pixel_offset * 4], 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else if (image_buffer->rect != nullptr) {
|
||||
uint8_t *dest = static_cast<uint8_t *>(
|
||||
static_cast<void *>(&image_buffer->rect[pixel_offset]));
|
||||
static uint8_t seam_color[] = {255, 0, 0, 255};
|
||||
copy_v4_v4_uchar(dest, seam_color);
|
||||
}
|
||||
}
|
||||
for (SeamFixBlend &fix : fixes.pixels_to_blend) {
|
||||
int pixel_offset = fix.dst_pixel.y * image_buffer->x + fix.dst_pixel.x;
|
||||
if (image_buffer->rect_float != nullptr) {
|
||||
copy_v4_fl4(&image_buffer->rect_float[pixel_offset * 4], 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else if (image_buffer->rect != nullptr) {
|
||||
uint8_t *dest = static_cast<uint8_t *>(
|
||||
static_cast<void *>(&image_buffer->rect[pixel_offset]));
|
||||
static uint8_t seam_color[] = {255, 0, 0, 255};
|
||||
copy_v4_v4_uchar(dest, seam_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
||||
}
|
||||
@@ -303,6 +334,9 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, nodes_to_update.size());
|
||||
BLI_task_parallel_range(0, nodes_to_update.size(), &user_data, do_encode_pixels, &settings);
|
||||
|
||||
BKE_pbvh_pixels_rebuild_seams(pbvh, mesh, image, image_user, ldata_uv);
|
||||
|
||||
if (USE_WATERTIGHT_CHECK) {
|
||||
apply_watertight_check(pbvh, image, image_user);
|
||||
}
|
||||
|
814
source/blender/blenkernel/intern/pbvh_pixels_seams.cc
Normal file
814
source/blender/blenkernel/intern/pbvh_pixels_seams.cc
Normal file
@@ -0,0 +1,814 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_wrappers.hh"
|
||||
#include "BKE_pbvh.h"
|
||||
#include "BKE_pbvh_pixels.hh"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_edgehash.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "pbvh_intern.h"
|
||||
|
||||
namespace blender::bke::pbvh::pixels {
|
||||
|
||||
struct EdgeLoop {
|
||||
/** Loop indexes that form an edge. */
|
||||
int l[2];
|
||||
};
|
||||
|
||||
enum class EdgeCheckFlag {
|
||||
/** No connecting edge loop found. */
|
||||
Unconnected,
|
||||
/** A connecting edge loop found. */
|
||||
Connected,
|
||||
};
|
||||
|
||||
struct EdgeCheck {
|
||||
EdgeCheckFlag flag;
|
||||
EdgeLoop first;
|
||||
EdgeLoop second;
|
||||
/* First vertex index of the first edge loop to determine winding order switching. */
|
||||
int first_v;
|
||||
};
|
||||
|
||||
/** Do the two given EdgeLoops share the same uv coordinates. */
|
||||
bool share_uv(const MLoopUV *ldata_uv, EdgeLoop &edge1, EdgeLoop &edge2)
|
||||
{
|
||||
const float2 &uv_1_a = ldata_uv[edge1.l[0]].uv;
|
||||
const float2 &uv_1_b = ldata_uv[edge1.l[1]].uv;
|
||||
const float2 &uv_2_a = ldata_uv[edge2.l[0]].uv;
|
||||
const float2 &uv_2_b = ldata_uv[edge2.l[1]].uv;
|
||||
|
||||
return (equals_v2v2(uv_1_a, uv_2_a) && equals_v2v2(uv_1_b, uv_2_b)) ||
|
||||
(equals_v2v2(uv_1_a, uv_2_b) && equals_v2v2(uv_1_b, uv_2_a));
|
||||
}
|
||||
|
||||
/** Make a list of connected and unconnected edgeloops that require UV Seam fixes. */
|
||||
void find_edges_that_need_fixing(const Mesh *mesh,
|
||||
const MLoopUV *ldata_uv,
|
||||
Vector<std::pair<EdgeLoop, EdgeLoop>> &r_connected,
|
||||
Vector<EdgeLoop> &r_unconnected)
|
||||
{
|
||||
EdgeHash *eh = BLI_edgehash_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(mesh->totpoly));
|
||||
|
||||
for (int p = 0; p < mesh->totpoly; p++) {
|
||||
MPoly &mpoly = mesh->mpoly[p];
|
||||
int prev_l = mpoly.loopstart + mpoly.totloop - 1;
|
||||
for (int l = 0; l < mpoly.totloop; l++) {
|
||||
MLoop &prev_mloop = mesh->mloop[prev_l];
|
||||
int current_l = mpoly.loopstart + l;
|
||||
MLoop &mloop = mesh->mloop[current_l];
|
||||
|
||||
void **value_ptr;
|
||||
|
||||
if (!BLI_edgehash_ensure_p(eh, prev_mloop.v, mloop.v, &value_ptr)) {
|
||||
EdgeCheck *value = MEM_cnew<EdgeCheck>(__func__);
|
||||
value->flag = EdgeCheckFlag::Unconnected;
|
||||
value->first.l[0] = prev_l;
|
||||
value->first.l[1] = current_l;
|
||||
value->first_v = prev_mloop.v;
|
||||
*value_ptr = value;
|
||||
}
|
||||
else {
|
||||
EdgeCheck *value = static_cast<EdgeCheck *>(*value_ptr);
|
||||
if (value->flag == EdgeCheckFlag::Unconnected) {
|
||||
value->flag = EdgeCheckFlag::Connected;
|
||||
/* Switch winding order to match the first edge. */
|
||||
if (prev_mloop.v == value->first_v) {
|
||||
value->second.l[0] = prev_l;
|
||||
value->second.l[1] = current_l;
|
||||
}
|
||||
else {
|
||||
value->second.l[0] = current_l;
|
||||
value->second.l[1] = prev_l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prev_l = current_l;
|
||||
}
|
||||
}
|
||||
|
||||
EdgeHashIterator iter;
|
||||
BLI_edgehashIterator_init(&iter, eh);
|
||||
while (!BLI_edgehashIterator_isDone(&iter)) {
|
||||
EdgeCheck *value = static_cast<EdgeCheck *>(BLI_edgehashIterator_getValue(&iter));
|
||||
switch (value->flag) {
|
||||
case EdgeCheckFlag::Unconnected: {
|
||||
r_unconnected.append(value->first);
|
||||
break;
|
||||
}
|
||||
case EdgeCheckFlag::Connected: {
|
||||
if (!share_uv(ldata_uv, value->first, value->second)) {
|
||||
r_connected.append(std::pair<EdgeLoop, EdgeLoop>(value->first, value->second));
|
||||
r_connected.append(std::pair<EdgeLoop, EdgeLoop>(value->second, value->first));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_edgehashIterator_step(&iter);
|
||||
}
|
||||
|
||||
BLI_edgehash_free(eh, MEM_freeN);
|
||||
}
|
||||
|
||||
struct PixelInfo {
|
||||
const static uint32_t IS_EXTRACTED = 1 << 0;
|
||||
const static uint32_t IS_SEAM_FIX = 1 << 1;
|
||||
|
||||
uint32_t node = 0;
|
||||
PixelInfo() = default;
|
||||
PixelInfo(const PixelInfo &other) = default;
|
||||
|
||||
static PixelInfo from_node(int node_index)
|
||||
{
|
||||
PixelInfo result;
|
||||
result.node = node_index << 2 | PixelInfo::IS_EXTRACTED;
|
||||
return result;
|
||||
}
|
||||
|
||||
static PixelInfo seam_fix()
|
||||
{
|
||||
PixelInfo result;
|
||||
result.node = IS_SEAM_FIX;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t get_node_index() const
|
||||
{
|
||||
return node >> 2;
|
||||
}
|
||||
|
||||
bool is_extracted() const
|
||||
{
|
||||
return (node & PixelInfo::IS_EXTRACTED) != 0;
|
||||
}
|
||||
bool is_seam_fix() const
|
||||
{
|
||||
return (node & PixelInfo::IS_SEAM_FIX) != 0;
|
||||
}
|
||||
bool is_empty_space() const
|
||||
{
|
||||
return !(is_extracted() || is_seam_fix());
|
||||
}
|
||||
};
|
||||
|
||||
struct Bitmap {
|
||||
image::ImageTileWrapper image_tile;
|
||||
Vector<PixelInfo> bitmap;
|
||||
int2 resolution;
|
||||
|
||||
Bitmap(image::ImageTileWrapper &image_tile, Vector<PixelInfo> bitmap, int2 resolution)
|
||||
: image_tile(image_tile), bitmap(bitmap), resolution(resolution)
|
||||
{
|
||||
}
|
||||
|
||||
void mark_seam_fix(int2 image_coordinate)
|
||||
{
|
||||
int offset = image_coordinate.y * resolution.x + image_coordinate.x;
|
||||
bitmap[offset] = PixelInfo::seam_fix();
|
||||
}
|
||||
|
||||
const PixelInfo &get_pixel_info(int2 image_coordinate) const
|
||||
{
|
||||
int offset = image_coordinate.y * resolution.x + image_coordinate.x;
|
||||
return bitmap[offset];
|
||||
}
|
||||
|
||||
const PixelInfo &get_pixel_info_safe(int2 image_coordinate) const
|
||||
{
|
||||
static PixelInfo coordinate_out_of_bounds_result;
|
||||
if (image_coordinate.x < 0 || image_coordinate.y < 0 || image_coordinate.x >= resolution.x ||
|
||||
image_coordinate.y >= resolution.y) {
|
||||
return coordinate_out_of_bounds_result;
|
||||
}
|
||||
|
||||
int offset = image_coordinate.y * resolution.x + image_coordinate.x;
|
||||
return bitmap[offset];
|
||||
}
|
||||
|
||||
bool contains(const float2 &uv) const
|
||||
{
|
||||
int2 tile_offset = image_tile.get_tile_offset();
|
||||
float2 tile_uv(uv.x - tile_offset.x, uv.y - tile_offset.y);
|
||||
if (tile_uv.x < 0.0f || tile_uv.x >= 1.0f) {
|
||||
return false;
|
||||
}
|
||||
if (tile_uv.y < 0.0f || tile_uv.y >= 1.0f) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct Bitmaps {
|
||||
Vector<Bitmap> bitmaps;
|
||||
|
||||
const Bitmap *find_containing_uv(float2 uv) const
|
||||
{
|
||||
for (const Bitmap &bitmap : bitmaps) {
|
||||
if (bitmap.contains(uv)) {
|
||||
return &bitmap;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<PixelInfo> create_tile_bitmap(const PBVH &pbvh,
|
||||
image::ImageTileWrapper &image_tile,
|
||||
ImBuf &image_buffer)
|
||||
{
|
||||
Vector<PixelInfo> result(image_buffer.x * image_buffer.y);
|
||||
|
||||
for (int n = 0; n < pbvh.totnode; n++) {
|
||||
PBVHNode *node = &pbvh.nodes[n];
|
||||
if ((node->flag & PBVH_Leaf) == 0) {
|
||||
continue;
|
||||
}
|
||||
NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
|
||||
UDIMTilePixels *tile_node_data = node_data->find_tile_data(image_tile);
|
||||
if (tile_node_data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PackedPixelRow &pixel_row : tile_node_data->pixel_rows) {
|
||||
int pixel_offset = pixel_row.start_image_coordinate.y * image_buffer.x +
|
||||
pixel_row.start_image_coordinate.x;
|
||||
for (int x = 0; x < pixel_row.num_pixels; x++) {
|
||||
result[pixel_offset] = PixelInfo::from_node(n);
|
||||
pixel_offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Bitmaps create_tile_bitmap(const PBVH &pbvh, Image &image, ImageUser &image_user)
|
||||
{
|
||||
Bitmaps result;
|
||||
ImageUser image_tile_user = image_user;
|
||||
LISTBASE_FOREACH (ImageTile *, tile_data, &image.tiles) {
|
||||
image::ImageTileWrapper image_tile(tile_data);
|
||||
image_tile_user.tile = image_tile.get_tile_number();
|
||||
ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &image_tile_user, nullptr);
|
||||
if (image_buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<PixelInfo> bitmap = create_tile_bitmap(pbvh, image_tile, *image_buffer);
|
||||
result.bitmaps.append(Bitmap(image_tile, bitmap, int2(image_buffer->x, image_buffer->y)));
|
||||
|
||||
BKE_image_release_ibuf(&image, image_buffer, nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int2 find_source_pixel(const Bitmap &bitmap,
|
||||
float2 near_image_coord,
|
||||
const float seam_fix_distance)
|
||||
{
|
||||
const int SEARCH_RADIUS = std::ceil(seam_fix_distance);
|
||||
float min_distance = FLT_MAX;
|
||||
int2 result(0, 0);
|
||||
int2 image_coord(int(near_image_coord.x), int(near_image_coord.y));
|
||||
for (int v = image_coord.y - SEARCH_RADIUS; v <= image_coord.y + SEARCH_RADIUS; v++) {
|
||||
for (int u = image_coord.x - SEARCH_RADIUS; u <= image_coord.x + SEARCH_RADIUS; u++) {
|
||||
if (u < 0 || u >= bitmap.resolution.x || v < 0 || v >= bitmap.resolution.y) {
|
||||
/** Pixel not part of this tile. */
|
||||
continue;
|
||||
}
|
||||
|
||||
int2 uv(u, v);
|
||||
const PixelInfo &pixel_info = bitmap.get_pixel_info(uv);
|
||||
if (!pixel_info.is_extracted()) {
|
||||
continue;
|
||||
}
|
||||
float2 center_pixel_uv(u + 0.5f, v + 0.5f);
|
||||
|
||||
float distance = len_v2v2(center_pixel_uv, near_image_coord);
|
||||
if (distance < min_distance) {
|
||||
result = uv;
|
||||
min_distance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Clears all existing seam fixes in the given PBVH. */
|
||||
static void pbvh_pixels_clear_seams(PBVH *pbvh)
|
||||
{
|
||||
for (int n = 0; n < pbvh->totnode; n++) {
|
||||
PBVHNode &node = pbvh->nodes[n];
|
||||
if ((node.flag & PBVH_Leaf) == 0) {
|
||||
continue;
|
||||
}
|
||||
NodeData &node_data = BKE_pbvh_pixels_node_data_get(node);
|
||||
node_data.seams.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static void add_seam_fix(PBVHNode &node,
|
||||
uint16_t src_tile_number,
|
||||
int2 src_pixel,
|
||||
uint16_t dst_tile_number,
|
||||
int2 dst_pixel)
|
||||
{
|
||||
NodeData &node_data = BKE_pbvh_pixels_node_data_get(node);
|
||||
UDIMSeamFixes &seam_fixes = node_data.ensure_seam_fixes(src_tile_number, dst_tile_number);
|
||||
seam_fixes.pixels_to_copy.append(SeamFixCopy{src_pixel, dst_pixel});
|
||||
}
|
||||
|
||||
static void add_seam_fix(PBVHNode &node,
|
||||
uint16_t src_tile_number,
|
||||
float2 src_pixel,
|
||||
char src_bit_mask,
|
||||
uint16_t dst_tile_number,
|
||||
int2 dst_pixel)
|
||||
{
|
||||
NodeData &node_data = BKE_pbvh_pixels_node_data_get(node);
|
||||
UDIMSeamFixes &seam_fixes = node_data.ensure_seam_fixes(src_tile_number, dst_tile_number);
|
||||
seam_fixes.pixels_to_blend.append(SeamFixBlend{src_pixel, dst_pixel, src_bit_mask});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Build fixes for connected edges.
|
||||
* \{ */
|
||||
|
||||
struct Projection {
|
||||
const Bitmap *bitmap = nullptr;
|
||||
int node_index = -1;
|
||||
float2 pixel = float2(0.0f, 0.0f);
|
||||
char bit_mask = 0;
|
||||
float score = 0.0f;
|
||||
};
|
||||
|
||||
/*
|
||||
* Project the point over onto the connected UV space. Taking into account the scale
|
||||
* difference.
|
||||
*/
|
||||
static void find_projection_source(const Bitmaps &bitmaps,
|
||||
const float distance_to_edge,
|
||||
const float lambda,
|
||||
const MLoopUV &uv1,
|
||||
const MLoopUV &uv2,
|
||||
const float scale_factor,
|
||||
Projection &r_projection)
|
||||
{
|
||||
r_projection.bit_mask = 0;
|
||||
r_projection.score = 0;
|
||||
r_projection.node_index = -1;
|
||||
|
||||
float2 closest_point;
|
||||
interp_v2_v2v2(closest_point, uv1.uv, uv2.uv, lambda);
|
||||
|
||||
r_projection.bitmap = bitmaps.find_containing_uv(closest_point);
|
||||
if (r_projection.bitmap == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
closest_point.x -= r_projection.bitmap->image_tile.get_tile_x_offset();
|
||||
closest_point.y -= r_projection.bitmap->image_tile.get_tile_y_offset();
|
||||
|
||||
float2 direction;
|
||||
sub_v2_v2v2(direction, uv2.uv, uv1.uv);
|
||||
|
||||
float2 perpedicular(direction.y, -direction.x);
|
||||
normalize_v2(perpedicular);
|
||||
perpedicular.x /= r_projection.bitmap->resolution.x;
|
||||
perpedicular.y /= r_projection.bitmap->resolution.y;
|
||||
float2 projected_coord = closest_point + perpedicular * distance_to_edge * scale_factor;
|
||||
projected_coord.x *= r_projection.bitmap->resolution.x;
|
||||
projected_coord.y *= r_projection.bitmap->resolution.y;
|
||||
r_projection.pixel = projected_coord;
|
||||
|
||||
/* Check neighbouring pixels and extract mask. Pixels should be part of the same node. */
|
||||
int2 pixel(std::floor(projected_coord.x), std::floor(projected_coord.y));
|
||||
PixelInfo pixel_info = r_projection.bitmap->get_pixel_info_safe(pixel);
|
||||
int node_index = pixel_info.get_node_index();
|
||||
if (pixel_info.is_extracted() &&
|
||||
(r_projection.node_index == -1 || r_projection.node_index == node_index)) {
|
||||
r_projection.bit_mask |= 1;
|
||||
r_projection.score += 1.0;
|
||||
r_projection.node_index = node_index;
|
||||
}
|
||||
|
||||
pixel.x += 1;
|
||||
pixel_info = r_projection.bitmap->get_pixel_info_safe(pixel);
|
||||
node_index = pixel_info.get_node_index();
|
||||
if (pixel_info.is_extracted() &&
|
||||
(r_projection.node_index == -1 || r_projection.node_index == node_index)) {
|
||||
r_projection.bit_mask |= 2;
|
||||
r_projection.score += 1.0;
|
||||
r_projection.node_index = node_index;
|
||||
}
|
||||
|
||||
pixel.x -= 1;
|
||||
pixel.y += 1;
|
||||
pixel_info = r_projection.bitmap->get_pixel_info_safe(pixel);
|
||||
node_index = pixel_info.get_node_index();
|
||||
if (pixel_info.is_extracted() &&
|
||||
(r_projection.node_index == -1 || r_projection.node_index == node_index)) {
|
||||
r_projection.bit_mask |= 4;
|
||||
r_projection.score += 1.0;
|
||||
r_projection.node_index = node_index;
|
||||
}
|
||||
|
||||
pixel.x += 1;
|
||||
pixel_info = r_projection.bitmap->get_pixel_info_safe(pixel);
|
||||
node_index = pixel_info.get_node_index();
|
||||
if (pixel_info.is_extracted() &&
|
||||
(r_projection.node_index == -1 || r_projection.node_index == node_index)) {
|
||||
r_projection.bit_mask |= 8;
|
||||
r_projection.score += 1.0;
|
||||
r_projection.node_index = node_index;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_fixes(PBVH &pbvh,
|
||||
Bitmaps &bitmaps,
|
||||
Bitmap &bitmap,
|
||||
const rcti &uvbounds,
|
||||
const MLoopUV &luv_a_1,
|
||||
const MLoopUV &luv_a_2,
|
||||
const MLoopUV &luv_b_1,
|
||||
const MLoopUV &luv_b_2,
|
||||
const float scale_factor,
|
||||
const float seamfix_distance)
|
||||
{
|
||||
if (uvbounds.xmax < 0 || uvbounds.ymax < 0 || uvbounds.xmin > bitmap.resolution.x ||
|
||||
uvbounds.ymin > bitmap.resolution.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int v = uvbounds.ymin; v <= uvbounds.ymax; v++) {
|
||||
for (int u = uvbounds.xmin; u <= uvbounds.xmax; u++) {
|
||||
if (u < 0 || u >= bitmap.resolution.x || v < 0 || v >= bitmap.resolution.y) {
|
||||
/** Pixel not part of this tile. */
|
||||
continue;
|
||||
}
|
||||
|
||||
int pixel_offset = v * bitmap.resolution.x + u;
|
||||
PixelInfo &pixel_info = bitmap.bitmap[pixel_offset];
|
||||
if (pixel_info.is_extracted() || pixel_info.is_seam_fix()) {
|
||||
/* Skip this pixel as it already has a solution. */
|
||||
continue;
|
||||
}
|
||||
|
||||
float2 dst_uv_offset(bitmap.image_tile.get_tile_x_offset(),
|
||||
bitmap.image_tile.get_tile_y_offset());
|
||||
float2 uv_coord(u, v);
|
||||
float2 center_uv_coord(u + 0.5f, v + 0.5f);
|
||||
float2 center_uv(center_uv_coord.x / bitmap.resolution.x,
|
||||
center_uv_coord.y / bitmap.resolution.y);
|
||||
float2 closest_point;
|
||||
const float lambda = closest_to_line_v2(closest_point,
|
||||
center_uv,
|
||||
float2(luv_a_1.uv) - dst_uv_offset,
|
||||
float2(luv_a_2.uv) - dst_uv_offset);
|
||||
float2 closest_coord(closest_point.x * bitmap.resolution.x,
|
||||
closest_point.y * bitmap.resolution.y);
|
||||
|
||||
/* Distance to the edge in pixel space. */
|
||||
float distance_to_edge = len_v2v2(closest_coord, center_uv_coord);
|
||||
if (distance_to_edge > seamfix_distance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Projection solution1;
|
||||
Projection solution2;
|
||||
/* When distance to edge is small, it could happen that the solution is pointing to a pixel
|
||||
* that also needs to be fixed. In this case we increase the distance until we find a
|
||||
* solution or skip this pixel after some iterations. */
|
||||
while (solution1.score == 0.0f && solution2.score == 0.0f) {
|
||||
find_projection_source(
|
||||
bitmaps, distance_to_edge, lambda, luv_b_1, luv_b_2, scale_factor, solution1);
|
||||
find_projection_source(
|
||||
bitmaps, distance_to_edge, 1.0f - lambda, luv_b_2, luv_b_1, scale_factor, solution2);
|
||||
if (distance_to_edge > 1.4f) {
|
||||
break;
|
||||
}
|
||||
distance_to_edge += 0.25f;
|
||||
}
|
||||
if (solution1.score == 0.0 && solution2.score == 0.0) {
|
||||
/* No solution found skip this pixel. */
|
||||
continue;
|
||||
}
|
||||
Projection &best_solution = solution1.score > solution2.score ? solution1 : solution2;
|
||||
|
||||
int2 destination_pixel(u, v);
|
||||
int src_node = best_solution.node_index;
|
||||
PBVHNode &node = pbvh.nodes[src_node];
|
||||
add_seam_fix(node,
|
||||
best_solution.bitmap->image_tile.get_tile_number(),
|
||||
best_solution.pixel,
|
||||
best_solution.bit_mask,
|
||||
bitmap.image_tile.get_tile_number(),
|
||||
destination_pixel);
|
||||
bitmap.mark_seam_fix(destination_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void build_fixes(PBVH &pbvh,
|
||||
const Vector<std::pair<EdgeLoop, EdgeLoop>> &connected,
|
||||
Bitmaps &bitmaps,
|
||||
const MLoopUV *ldata_uv,
|
||||
const float seamfix_distance)
|
||||
{
|
||||
const int margin = std::ceil(seamfix_distance);
|
||||
for (const std::pair<EdgeLoop, EdgeLoop> &pair : connected) {
|
||||
// determine bounding rect in uv space + margin of 1;
|
||||
rctf uvbounds;
|
||||
BLI_rctf_init_minmax(&uvbounds);
|
||||
const MLoopUV &luv_a_1 = ldata_uv[pair.first.l[0]];
|
||||
const MLoopUV &luv_a_2 = ldata_uv[pair.first.l[1]];
|
||||
BLI_rctf_do_minmax_v(&uvbounds, luv_a_1.uv);
|
||||
BLI_rctf_do_minmax_v(&uvbounds, luv_a_2.uv);
|
||||
|
||||
const MLoopUV &luv_b_1 = ldata_uv[pair.second.l[0]];
|
||||
const MLoopUV &luv_b_2 = ldata_uv[pair.second.l[1]];
|
||||
|
||||
const float scale_factor = len_v2v2(luv_b_1.uv, luv_b_2.uv) / len_v2v2(luv_a_1.uv, luv_a_2.uv);
|
||||
|
||||
for (Bitmap &bitmap : bitmaps.bitmaps) {
|
||||
rcti uvbounds_i;
|
||||
uvbounds_i.xmin = (uvbounds.xmin - bitmap.image_tile.get_tile_x_offset()) *
|
||||
bitmap.resolution[0];
|
||||
uvbounds_i.ymin = (uvbounds.ymin - bitmap.image_tile.get_tile_y_offset()) *
|
||||
bitmap.resolution[1];
|
||||
uvbounds_i.xmax = (uvbounds.xmax - bitmap.image_tile.get_tile_x_offset()) *
|
||||
bitmap.resolution[0];
|
||||
uvbounds_i.ymax = (uvbounds.ymax - bitmap.image_tile.get_tile_y_offset()) *
|
||||
bitmap.resolution[1];
|
||||
|
||||
uvbounds_i.xmin -= margin;
|
||||
uvbounds_i.xmax += margin;
|
||||
uvbounds_i.ymin -= margin;
|
||||
uvbounds_i.ymax += margin;
|
||||
|
||||
build_fixes(pbvh,
|
||||
bitmaps,
|
||||
bitmap,
|
||||
uvbounds_i,
|
||||
luv_a_1,
|
||||
luv_a_2,
|
||||
luv_b_1,
|
||||
luv_b_2,
|
||||
scale_factor,
|
||||
seamfix_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Build fixes for unconnected edges.
|
||||
* \{ */
|
||||
|
||||
static void build_fixes(PBVH &pbvh,
|
||||
Bitmap &bitmap,
|
||||
const rcti &uvbounds,
|
||||
const MLoopUV &luv_1,
|
||||
const MLoopUV &luv_2,
|
||||
const float seamfix_distance)
|
||||
{
|
||||
if (uvbounds.xmax < 0 || uvbounds.ymax < 0 || uvbounds.xmin > bitmap.resolution.x ||
|
||||
uvbounds.ymin > bitmap.resolution.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int v = uvbounds.ymin; v <= uvbounds.ymax; v++) {
|
||||
for (int u = uvbounds.xmin; u <= uvbounds.xmax; u++) {
|
||||
if (u < 0 || u >= bitmap.resolution[0] || v < 0 || v >= bitmap.resolution[1]) {
|
||||
/** Pixel not part of this tile. */
|
||||
continue;
|
||||
}
|
||||
int pixel_offset = v * bitmap.resolution[0] + u;
|
||||
PixelInfo &pixel_info = bitmap.bitmap[pixel_offset];
|
||||
if (pixel_info.is_extracted() || pixel_info.is_seam_fix()) {
|
||||
/* Skip this pixel as it already has a solution. */
|
||||
continue;
|
||||
}
|
||||
|
||||
float2 dst_uv_offset(bitmap.image_tile.get_tile_x_offset(),
|
||||
bitmap.image_tile.get_tile_y_offset());
|
||||
float2 uv(float(u) / bitmap.resolution[0], float(v) / bitmap.resolution[1]);
|
||||
float2 closest_point;
|
||||
closest_to_line_v2(
|
||||
closest_point, uv, float2(luv_1.uv) - dst_uv_offset, float2(luv_2.uv) - dst_uv_offset);
|
||||
|
||||
/* Calculate the distance in pixel space. */
|
||||
float2 uv_coord(u, v);
|
||||
float2 closest_coord(closest_point.x * bitmap.resolution.x,
|
||||
closest_point.y * bitmap.resolution.y);
|
||||
float distance_to_edge = len_v2v2(uv_coord, closest_coord);
|
||||
if (distance_to_edge > seamfix_distance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int2 source_pixel = find_source_pixel(bitmap, closest_coord, seamfix_distance);
|
||||
int2 destination_pixel(u, v);
|
||||
|
||||
PixelInfo src_pixel_info = bitmap.get_pixel_info(source_pixel);
|
||||
if (!src_pixel_info.is_extracted()) {
|
||||
continue;
|
||||
}
|
||||
int src_node = src_pixel_info.get_node_index();
|
||||
|
||||
PBVHNode &node = pbvh.nodes[src_node];
|
||||
add_seam_fix(node,
|
||||
bitmap.image_tile.get_tile_number(),
|
||||
source_pixel,
|
||||
bitmap.image_tile.get_tile_number(),
|
||||
destination_pixel);
|
||||
bitmap.mark_seam_fix(destination_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void build_fixes(PBVH &pbvh,
|
||||
const Vector<EdgeLoop> &unconnected,
|
||||
Bitmaps &bitmaps,
|
||||
const MLoopUV *ldata_uv,
|
||||
const float seamfix_distance)
|
||||
{
|
||||
for (const EdgeLoop &unconnected_loop : unconnected) {
|
||||
// determine bounding rect in uv space + margin of 1;
|
||||
rctf uvbounds;
|
||||
BLI_rctf_init_minmax(&uvbounds);
|
||||
const MLoopUV &luv_1 = ldata_uv[unconnected_loop.l[0]];
|
||||
const MLoopUV &luv_2 = ldata_uv[unconnected_loop.l[1]];
|
||||
BLI_rctf_do_minmax_v(&uvbounds, luv_1.uv);
|
||||
BLI_rctf_do_minmax_v(&uvbounds, luv_2.uv);
|
||||
|
||||
for (Bitmap &bitmap : bitmaps.bitmaps) {
|
||||
rcti uvbounds_i;
|
||||
const int MARGIN = 1;
|
||||
uvbounds_i.xmin = (uvbounds.xmin - bitmap.image_tile.get_tile_x_offset()) *
|
||||
bitmap.resolution[0] -
|
||||
MARGIN;
|
||||
uvbounds_i.ymin = (uvbounds.ymin - bitmap.image_tile.get_tile_y_offset()) *
|
||||
bitmap.resolution[1] -
|
||||
MARGIN;
|
||||
uvbounds_i.xmax = (uvbounds.xmax - bitmap.image_tile.get_tile_x_offset()) *
|
||||
bitmap.resolution[0] +
|
||||
MARGIN;
|
||||
uvbounds_i.ymax = (uvbounds.ymax - bitmap.image_tile.get_tile_y_offset()) *
|
||||
bitmap.resolution[1] +
|
||||
MARGIN;
|
||||
|
||||
build_fixes(pbvh, bitmap, uvbounds_i, luv_1, luv_2, seamfix_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void BKE_pbvh_pixels_rebuild_seams(
|
||||
PBVH *pbvh, const Mesh *mesh, Image *image, ImageUser *image_user, const MLoopUV *ldata_uv)
|
||||
{
|
||||
|
||||
// find seams.
|
||||
// for each edge
|
||||
Vector<std::pair<EdgeLoop, EdgeLoop>> connected;
|
||||
Vector<EdgeLoop> unconnected;
|
||||
find_edges_that_need_fixing(mesh, ldata_uv, connected, unconnected);
|
||||
|
||||
// Make a bitmap per tile indicating pixels that have already been assigned to a PBVHNode.
|
||||
Bitmaps bitmaps = create_tile_bitmap(*pbvh, *image, *image_user);
|
||||
|
||||
pbvh_pixels_clear_seams(pbvh);
|
||||
/* Fix connected edges before unconnected to improve quality. */
|
||||
build_fixes(*pbvh, connected, bitmaps, ldata_uv, image->seamfix_distance);
|
||||
build_fixes(*pbvh, unconnected, bitmaps, ldata_uv, image->seamfix_distance);
|
||||
}
|
||||
|
||||
static float4 blend_source(ImBuf *src_image_buffer, float2 src_coordinate, char src_bit_mask)
|
||||
{
|
||||
int2 src_pixel(std::floor(src_coordinate.x), std::floor(src_coordinate.y));
|
||||
int src_offset = src_pixel.y * src_image_buffer->x + src_pixel.x;
|
||||
float2 frac(src_coordinate.x - src_pixel.x, src_coordinate.y - src_pixel.y);
|
||||
float4 color(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
float sum_factor = 0.0f;
|
||||
if (src_bit_mask & 1) {
|
||||
float4 c1(src_image_buffer->rect_float[src_offset * 4]);
|
||||
float f1 = std::sqrt(std::pow(1.0f - frac.x, 2)) + std::pow(1.0f - frac.y, 2);
|
||||
color = color + c1 * f1;
|
||||
sum_factor += f1;
|
||||
}
|
||||
if (src_bit_mask & 2) {
|
||||
float4 c2(src_image_buffer->rect_float[(src_offset + 1) * 4]);
|
||||
float f2 = std::sqrt(std::pow(frac.x, 2)) + std::pow(1.0f - frac.y, 2);
|
||||
color = color + c2 * f2;
|
||||
sum_factor += f2;
|
||||
}
|
||||
if (src_bit_mask & 4) {
|
||||
float4 c3(src_image_buffer->rect_float[(src_offset + src_image_buffer->x) * 4]);
|
||||
float f3 = std::sqrt(std::pow(1.0 - frac.x, 2)) + std::pow(frac.y, 2);
|
||||
color = color + c3 * f3;
|
||||
sum_factor += f3;
|
||||
}
|
||||
if (src_bit_mask & 8) {
|
||||
float4 c4(src_image_buffer->rect_float[(src_offset + src_image_buffer->x + 1) * 4]);
|
||||
float f4 = std::sqrt(std::pow(frac.x, 2)) + std::pow(frac.y, 2);
|
||||
color = color + c4 * f4;
|
||||
sum_factor += f4;
|
||||
}
|
||||
color.x /= sum_factor;
|
||||
color.y /= sum_factor;
|
||||
color.z /= sum_factor;
|
||||
color.w = 1.0f;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void BKE_pbvh_pixels_fix_seams(PBVHNode *node, Image *image, ImageUser *image_user)
|
||||
{
|
||||
NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node);
|
||||
ImageUser iuser = *image_user;
|
||||
|
||||
for (UDIMSeamFixes &fixes : node_data.seams) {
|
||||
iuser.tile = fixes.dst_tile_number;
|
||||
ImBuf *dst_image_buffer = BKE_image_acquire_ibuf(image, &iuser, nullptr);
|
||||
if (dst_image_buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iuser.tile = fixes.src_tile_number;
|
||||
ImBuf *src_image_buffer = BKE_image_acquire_ibuf(image, &iuser, nullptr);
|
||||
if (src_image_buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** Determine the region to update by checking actual changes. */
|
||||
rcti region_to_update;
|
||||
BLI_rcti_init_minmax(®ion_to_update);
|
||||
|
||||
if (src_image_buffer->rect_float != nullptr && dst_image_buffer->rect_float != nullptr) {
|
||||
for (SeamFixCopy &fix : fixes.pixels_to_copy) {
|
||||
int src_offset = fix.src_pixel.y * src_image_buffer->x + fix.src_pixel.x;
|
||||
int dst_offset = fix.dst_pixel.y * dst_image_buffer->x + fix.dst_pixel.x;
|
||||
|
||||
if (equals_v4v4(&dst_image_buffer->rect_float[dst_offset * 4],
|
||||
&src_image_buffer->rect_float[src_offset * 4])) {
|
||||
continue;
|
||||
}
|
||||
BLI_rcti_do_minmax_v(®ion_to_update, fix.dst_pixel);
|
||||
copy_v4_v4(&dst_image_buffer->rect_float[dst_offset * 4],
|
||||
&src_image_buffer->rect_float[src_offset * 4]);
|
||||
}
|
||||
|
||||
for (SeamFixBlend &fix : fixes.pixels_to_blend) {
|
||||
float4 color = blend_source(src_image_buffer, fix.src_pixel, fix.src_bit_mask);
|
||||
int dst_offset = fix.dst_pixel.y * dst_image_buffer->x + fix.dst_pixel.x;
|
||||
if (equals_v4v4(&dst_image_buffer->rect_float[dst_offset * 4], color)) {
|
||||
continue;
|
||||
}
|
||||
BLI_rcti_do_minmax_v(®ion_to_update, fix.dst_pixel);
|
||||
copy_v4_v4(&dst_image_buffer->rect_float[dst_offset * 4], color);
|
||||
}
|
||||
}
|
||||
else if (src_image_buffer->rect != nullptr && dst_image_buffer->rect != nullptr) {
|
||||
for (SeamFixCopy &fix : fixes.pixels_to_copy) {
|
||||
int src_offset = fix.src_pixel.y * src_image_buffer->x + fix.src_pixel.x;
|
||||
int dst_offset = fix.dst_pixel.y * dst_image_buffer->x + fix.dst_pixel.x;
|
||||
if (dst_image_buffer->rect[dst_offset] == src_image_buffer->rect[src_offset]) {
|
||||
continue;
|
||||
}
|
||||
BLI_rcti_do_minmax_v(®ion_to_update, fix.dst_pixel);
|
||||
dst_image_buffer->rect[dst_offset] = src_image_buffer->rect[src_offset];
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark dst_image_buffer region dirty covering each dst_pixel. */
|
||||
if (BLI_rcti_is_valid(®ion_to_update)) {
|
||||
LISTBASE_FOREACH (ImageTile *, image_tile, &image->tiles) {
|
||||
if (image_tile->tile_number != fixes.dst_tile_number) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BKE_image_partial_update_mark_region(
|
||||
image, image_tile, dst_image_buffer, ®ion_to_update);
|
||||
break;
|
||||
}
|
||||
}
|
||||
BKE_image_release_ibuf(image, src_image_buffer, nullptr);
|
||||
BKE_image_release_ibuf(image, dst_image_buffer, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bke::pbvh::pixels
|
@@ -2780,6 +2780,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Image", "float", "seamfix_distance")) {
|
||||
LISTBASE_FOREACH (Image *, image, &bmain->images) {
|
||||
image->seamfix_distance = 2.5f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace legacy combine/separate color nodes */
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
/* In geometry nodes, replace shader combine/separate color nodes with function nodes */
|
||||
|
@@ -366,6 +366,7 @@ static void do_mark_dirty_regions(void *__restrict userdata,
|
||||
{
|
||||
TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata);
|
||||
PBVHNode *node = data->nodes[n];
|
||||
BKE_pbvh_pixels_fix_seams(node, data->image_data.image, data->image_data.image_user);
|
||||
BKE_pbvh_pixels_mark_image_dirty(*node, *data->image_data.image, *data->image_data.image_user);
|
||||
}
|
||||
|
||||
|
@@ -948,6 +948,7 @@ void uiTemplateImage(uiLayout *layout,
|
||||
}
|
||||
|
||||
uiItemR(col, &imaptr, "use_view_as_render", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, &imaptr, "seamfix_distance", 0, NULL, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
.gpuframenr = INT_MAX, \
|
||||
.gpu_pass = SHRT_MAX, \
|
||||
.gpu_layer = SHRT_MAX, \
|
||||
.seamfix_distance = 2.5f, \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -175,7 +175,9 @@ typedef struct Image {
|
||||
short gpu_pass;
|
||||
short gpu_layer;
|
||||
short gpu_view;
|
||||
char _pad2[4];
|
||||
|
||||
/* Thickness of the UV seam fix area in distance between pixels and edges in pixel space. */
|
||||
float seamfix_distance;
|
||||
|
||||
/** Deprecated. */
|
||||
struct PackedFile *packedfile DNA_DEPRECATED;
|
||||
|
@@ -1160,6 +1160,11 @@ static void rna_def_image(BlenderRNA *brna)
|
||||
"Use 16 bits per channel to lower the memory usage during rendering");
|
||||
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_gpu_texture_update");
|
||||
|
||||
prop = RNA_def_property(srna, "seamfix_distance", PROP_FLOAT, PROP_PIXEL);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Seam-fix Distance", "Pixels closer to edge will be considered during UV seam fixing");
|
||||
RNA_def_property_ui_range(prop, 0.0, FLT_MAX, 1, 1);
|
||||
|
||||
/* multiview */
|
||||
prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
|
Reference in New Issue
Block a user