Compare commits

...

71 Commits

Author SHA1 Message Date
8ea7063a71 Merge remote-tracking branch 'origin' into temp-pbvh-split 2023-01-23 09:14:43 -08:00
495094d65b temp-pbvh-split: fix compile error 2023-01-23 09:11:57 -08:00
3f923b0d7e temp-pbvh-split: Attempt to fix submodules 2023-01-23 08:47:46 -08:00
365bd78cba Merge branch 'master' into temp-pbvh-split 2023-01-23 08:45:43 -08:00
14fa20d797 Merge branch 'master' into temp-pbvh-split 2023-01-12 19:33:03 -08:00
afbfa1352b temp-pbvh-split: make requested patch changes 2023-01-03 10:57:29 -08:00
7cb31b649f Merge branch 'master' into temp-pbvh-split 2023-01-03 10:37:06 -08:00
98bdd08507 Merge branch 'master' into temp-pbvh-split 2022-12-30 21:38:01 -08:00
78b008d20d temp-pbvh-split: Fix various crashes and memory corruption 2022-10-15 00:55:45 -07:00
aa1f2f243d Merge branch 'master' into temp-pbvh-split 2022-10-14 23:22:01 -07:00
278a2137f9 Merge remote-tracking branch 'origin' into temp-pbvh-split 2022-10-07 12:28:55 -07:00
92964a29b5 temp-pbvh-split: fix improver use of mesh->mvert 2022-10-07 12:28:25 -07:00
adb0e5e054 temp-pbvh-split: fix merge error 2022-10-07 02:32:49 -07:00
6a0a92c587 Merge branch 'master' into temp-pbvh-split 2022-10-06 14:14:55 -07:00
35bff5f4e0 Merge branch 'master' into temp-pbvh-split 2022-06-22 08:34:35 -07:00
f90aeb0152 temp-pbvh-split: Remove debug macro 2022-06-02 16:47:03 -07:00
6851971501 temp-pbvh-split: Use task api correctly.
BLI_task_pool_push could use a comment clarify that
you can call it within threads, and also that there's
no overhead to doing so.
2022-06-02 16:43:37 -07:00
d55dc9a3b1 temp-pbvh-split: Remove debug printf 2022-06-02 16:15:49 -07:00
6047cb7708 Merge remote-tracking branch 'origin' into temp-pbvh-split 2022-06-02 16:13:51 -07:00
f82036b6e2 temp-pbvh-split: Finish merge and fix paint undo 2022-06-02 16:13:19 -07:00
f967fbea65 Merge branch 'master' into temp-pbvh-split 2022-06-02 03:22:27 -07:00
f2d39b810b Enable inlining on Apple Silicon. Use new process-wide ShaderCache in order to safely re-enable binary archives
This patch is the same as D14763, but with a fix for unit test failures caused by ShaderCache fetch logic not working in the non-MetalRT case:

```
diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm
index ad268ae7057..6aa1a56056e 100644
--- a/intern/cycles/device/metal/kernel.mm
+++ b/intern/cycles/device/metal/kernel.mm
@@ -203,9 +203,12 @@ bool kernel_has_intersection(DeviceKernel device_kernel)

   /* metalrt options */
   request.pipeline->use_metalrt = device->use_metalrt;
-  request.pipeline->metalrt_hair = device->kernel_features & KERNEL_FEATURE_HAIR;
-  request.pipeline->metalrt_hair_thick = device->kernel_features & KERNEL_FEATURE_HAIR_THICK;
-  request.pipeline->metalrt_pointcloud = device->kernel_features & KERNEL_FEATURE_POINTCLOUD;
+  request.pipeline->metalrt_hair = device->use_metalrt &&
+                                   (device->kernel_features & KERNEL_FEATURE_HAIR);
+  request.pipeline->metalrt_hair_thick = device->use_metalrt &&
+                                         (device->kernel_features & KERNEL_FEATURE_HAIR_THICK);
+  request.pipeline->metalrt_pointcloud = device->use_metalrt &&
+                                         (device->kernel_features & KERNEL_FEATURE_POINTCLOUD);

   {
     thread_scoped_lock lock(cache_mutex);
@@ -225,9 +228,9 @@ bool kernel_has_intersection(DeviceKernel device_kernel)

   /* metalrt options */
   bool use_metalrt = device->use_metalrt;
-  bool metalrt_hair = device->kernel_features & KERNEL_FEATURE_HAIR;
-  bool metalrt_hair_thick = device->kernel_features & KERNEL_FEATURE_HAIR_THICK;
-  bool metalrt_pointcloud = device->kernel_features & KERNEL_FEATURE_POINTCLOUD;
+  bool metalrt_hair = use_metalrt && (device->kernel_features & KERNEL_FEATURE_HAIR);
+  bool metalrt_hair_thick = use_metalrt && (device->kernel_features & KERNEL_FEATURE_HAIR_THICK);
+  bool metalrt_pointcloud = use_metalrt && (device->kernel_features & KERNEL_FEATURE_POINTCLOUD);

   MetalKernelPipeline *best_pipeline = nullptr;
   for (auto &pipeline : collection) {

```

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D14923
2022-06-02 02:56:36 -07:00
9549df341c UI: Update rest of UI code for increased button flag bitfield
Needed after 98a04ed452.
2022-06-02 02:56:36 -07:00
afabb990ee Fix T95710: Make Single User > Object Data Animation broken
The operator now not only checks `ob->data` for Actions to duplicate,
but also passes `ob->data` to the duplication function (instead of `ob`).
2022-06-02 02:56:36 -07:00
052073e9e4 Fix T97947: USD will fail to export without file extension
Now add a default ".usdc" file extension if no (or the wrong) extension
is given instead of presenting the user with the error that "no suitable
USD plugin to write is found".

This is in line with how other exporters do this.

Maniphest Tasks: T97947

Differential Revision: https://developer.blender.org/D14895
2022-06-02 02:56:36 -07:00
1ebfd4294e Curves: Adjust sculpt mode UI layouts
This patch adjusts the UI layouts for the tool header and the tool
properties in sculpt mode in a few ways. The goals are to better group
related settings, keep fundamental settings easily accessible, fix the
availability of some options, and make better use of space.

1. Remove ID template in tool header
2. Rename "Add Amount" to "Count" for add brush
3. Add "use pressure" toggles to radius and strength sliders
4. Move strength falloff to a popover
5. Move many "Add" brush settings to popover called "Curve Shape"
6. Move two "Grow/Shrink" options to a popover called "Scaling"
7. Don't display "Falloff" panel in properties when it has no effect

See the differential revision for screenshots and more reasoning.

Differential Revision: https://developer.blender.org/D14922
2022-06-02 02:56:35 -07:00
8414e79b71 Fix: Crash with empty curves add interpolate points
The neighbors for an added curve can be empty.
In that case use the constant value instead of interpolating.
2022-06-02 02:56:35 -07:00
73f1f51627 Fix T97330: GPU Subdiv compiler error.
GLSL has different max number of ssbo per glsl stage.
This patch checks if the number of compute ssbo blocks matches
our requirements for the GPU Subdiv, before enabling it.

Some platforms allow more ssbo bindings then blocks per stage.
2022-06-02 02:56:35 -07:00
6bd0176ebc Fix: Build error on windows.
Issue introduced by rBeef98e66cf9e

BLI_math_rotation.h uses M_PI which
gets defined inside BLI_math_base.h
2022-06-02 02:56:35 -07:00
91a488ad18 Fix (unreported) crash in Outliner Overrides Properties view in invalid cases.
We cannot try to get RNA info when the rna path of an override property
is invalid.
2022-06-02 02:56:35 -07:00
c6445efe72 Cleanup: rename BLI_str_format_attribute_domain_size
This is useful without any functionality specific to attribute domains,
rename to `BLI_str_format_decimal_unit` to follow naming of a similar
function `BLI_str_format_byte_unit`.
2022-06-02 02:56:35 -07:00
d98e2b20d4 Fix T97895: Eevee support for Geometry Nodes Color Attributes.
Geometry nodes can generate color attributes that aren't on point or corner domain.
When not found in these domains it will be processed as a common attribute.
2022-06-02 02:56:35 -07:00
Pablo Vazquez
5fa64f6565 Mesh: Add Auto Smooth option to Shade Smooth operator
Add a property to the **Shade Smooth** operator to quickly enable the Mesh `use_auto_smooth` option.

The `Angle` property is exposed in the **Adjust Last Operation** panel to make it easy to tweak on multiple objects without having to go to the Properties editor.

The operator is exposed in the `Object` menu and `Object Context Menu`.

=== Demo ===

{F13066173, size=full}

Regarding the implementation, there are multiple ways to go about this (like making a whole new operator altogether), but I think a property is the cleanest/simplest.

I imagine there are simpler ways to achieve this without duplicating the `use_auto_smooth` property in the operator itself (getting it from the Mesh props?), but I couldn't find other operators doing something similar.

Reviewed By: #modeling, mont29

Differential Revision: https://developer.blender.org/D14894
2022-06-02 02:56:35 -07:00
1cc6e754f3 Fix "Open Clip" operator in Clip Editor broken
Steps to reproduce were:
- Open Clip Editor
- Call "Open Clip" (e.g. Alt+O)
- Select video file

The file wouldn't be loaded into the Clip Editor.

Caused by 7849b56c3c.
2022-06-02 02:56:35 -07:00
a92413b0db Revert "Gizmo: optimize intersection tests, fix selection bias"
Manually revert commit [0] as it caused problems macOS (reported T96435).

- Includes fixes from [1] & [2].
- T98037 TODO has been created to keep track of this feature.

Thanks to @jbakker & @sergey for investigating this issue as I wasn't
able to reproduce the bug.

[0]: 0cb5eae9d0
[1]: cb986446e2
[2]: cc8fe1a1cb
2022-06-02 02:56:35 -07:00
83a3443ea5 Fix T97173: Color Attributes shading turns black after switching mode.
Sculpt colors tagged the custom data as already created (cd_used), but
should have been tagged as being requested (cd_needed).
2022-06-02 02:56:35 -07:00
3915e57a47 Cleanup: Use single quotes for Python enum string 2022-06-02 02:56:35 -07:00
a74cd17030 Fix: Hide empty panel in curves sculpt mode tool settings
This panel is empty after rB5b24291be1e0
2022-06-02 02:56:35 -07:00
d1c080b737 Fix: Spline parameter node broken for Catmull Rom curves
Subtracting one from the evaluated index could make the index -1.
That was only necessary for Bezier curves due to the specifics of
the "bezier_evaluated_offsets".
2022-06-02 02:56:35 -07:00
16fa4c5347 Outliner: Remove the 'Remap data-block usages' operation.
This feature is very advanced, and the way it was exposed in the
Outliner was very confusing at best.

It remains available through the Python API (`ID.user_remap`) e.g.
2022-06-02 02:56:35 -07:00
1b4cc8c5f3 Outliner: Remove 'rename library' feature.
This was historically the only way to change/fix paths of library files
in Blender. However, only changing the path then required a manual
reload of the library, which could be skipped by user, or a save/reload
of the working .blend file, which could lead to corruption of advanced
library usages like overrides.

Prefferred, modern way to change path of a library is to use the
Relocate operation instead. Direct path modification remains possible
through RNA (python console or the Data API view in the Outliner.
2022-06-02 02:56:34 -07:00
9184beada7 Update Ceres to latest upstream version 2.1.0
This release deprecated the Parameterization API and the new Manifolds
API is to be used instead. This is what was done in the Libmv as part
of this change.

Additionally, remove the bundling scripts. Nowadays those are only
leading to a duplicated work to maintain.

No measurable changes on user side is expected.
2022-06-02 02:56:34 -07:00
331cc4b66e Cleanup: spelling in comments/strings
D14918 from @linux_dr with some other changes included.
2022-06-02 02:56:34 -07:00
Jun Mizutani
07ac2445b9 Fix: Curves interpolate point count option missing from panels
Added in 8852191b77

Differential Revision: https://developer.blender.org/D14919
2022-06-02 02:56:34 -07:00
e34bfbc13b Fix T97153: Knife project crashes
Knife projection BVH-tree lookup could use invalid indices since the
mesh being cut is also used for BVH intersection tests.

Solve by storing triangle indices when knife project is used so a
triangle index can always be used to look up original coordinates of a
triangle.
2022-06-02 02:56:34 -07:00
86214740ca Fix knife tool use-after free on completion
Regression in [0] accessed knife data after it had been freed.

[0]: f87029f7b1
2022-06-02 02:56:34 -07:00
9f76a10260 Fix T96892 Overlay: Hiding all of a mesh in edit mode causes visual glitch
This is caused by the geometry shader used by the edit mode line drawing.
If the drawcall uses indexed drawing and if the index buffer only contains
restart indices, it seems the result is 1 glitchy invocation of the
geometry shader.

Workaround by tagging these special case index buffers and bypassing
their drawcall.
2022-06-02 02:56:34 -07:00
166ef650cb Fix T97945: Cycles baking max distance is wrong
It was effectively sqrt(max_distance) before this fix.

Thanks to Omar Emara for identifying the solution.
2022-06-02 02:56:34 -07:00
ac1ffa2420 Fix T97908: Cycles missing motion from on pointcloud generated by geometry nodes
Assume geometry is always potentially animated, since we can't use our heuristic
to detect if the object is potentially animated by looking at modifiers on the
object.

The main original reason for this check was to avoid evaluating subdivision
surfaces for many static objects, which is not happening here anyway.
2022-06-02 02:56:34 -07:00
Mikhail Matrosov
f8db581a22 Fix T97966: Cycles shadow terminator offset wrong for scaled object instances
Differential Revision: https://developer.blender.org/D14893
2022-06-02 02:56:34 -07:00
Olivier Maury
2a570c1f4b Fix T97056: Cycles MNEE not working with glass and pure refraction BSDFs
Differential Revision: https://developer.blender.org/D14901
2022-06-02 02:56:33 -07:00
9f3fe0583a Fix part of T97895: Cycles not rendering edge domain attributes
These aren't really ideal for rendering, but better to show something. Edge
values are averaged at vertices.
2022-06-02 02:56:33 -07:00
5e338f88d5 Cleanup: use 'num' / 'size' suffix instead of 'sz'
GPU code used `sz` as an abbreviation for size, as well as a few other
places. Use size where this represents a size in bytes, see: T85728.
2022-06-02 02:56:33 -07:00
18159bd6c4 Cleanup: use '_num' / '_count' suffix instead of '_ct'
Use num & count (for counters), in drawing code, see: T85728.
2022-06-02 02:56:33 -07:00
17d5450eaf Cleanup: use '_num' suffix, mostly for curves & spline code
Replace tot/amount & size with num, in keeping with T85728.
2022-06-02 02:56:33 -07:00
01d33feed5 WM: clear wmEvent.flag for file-select events
Harmless but could cause file-select events to have WM_EVENT_IS_REPEAT
set which logged a warning as this is only intended for keyboard events.
2022-06-02 02:56:33 -07:00
d46039a8b2 Cleanup: spelling in comments
Revert change from [0] that assumed UNORM was a mis-spelling of UNIFORM.

[0]: 2c75857f9f
2022-06-02 02:56:33 -07:00
844bed2ec6 Cleanup: use '_num' suffix instead of '_size' for CurveGeometry
Follow conventions from T85728.
2022-06-02 02:56:33 -07:00
1d8f2aeb74 Cleanup: format 2022-06-02 02:56:33 -07:00
db23f908ad Cleanup: use doxy sections for node_edit.cc 2022-06-02 02:56:33 -07:00
7e63c035b8 Fix cursor snap not acting on selected UVs
Regression in rBd2271cf939.
2022-06-02 02:56:32 -07:00
09e05193c7 temp-pbvh-split: Use TaskPool API for texture node splitting 2022-05-10 18:39:22 -07:00
0d24e8af62 temp-pbvh-split: reuse triangles array. 2022-05-10 18:05:54 -07:00
b617e66e36 Merge branch 'master' into temp-pbvh-split 2022-05-10 17:10:37 -07:00
d6da131c40 Merge remote-tracking branch 'origin/master' into temp-pbvh-split 2022-05-09 18:31:18 -07:00
6e4daa7d3c temp-pbvh-split: Multithread pbvh texture building
I had to use the original threading API for this and
ThreadQueue.  Threads pull nodes to split from a
thread queue and push any new nodes onto the queue
for other threads to further split.

I'm thinking of trying this approach out for PBVH building in
general.  It cut the build time for texture leaves in half.
2022-05-09 18:11:22 -07:00
27fd506501 temp-pbvh-split: Fix pixel row splitting 2022-05-09 16:35:39 -07:00
867fe19770 Merge remote-tracking branch 'origin/master' into temp-pbvh-split 2022-05-09 11:05:32 -07:00
17c7f2e41b Merge remote-tracking branch 'origin/master' into temp-pbvh-split 2022-05-06 16:12:59 -07:00
d2fa1daea6 temp-pbvh-split: PBVH texpaint node splitting
* Texture paint now has its own special
  PBVH nodes leaf node flag, PBVH_TexLeaf.
* There is a new version of BKE_pbvh_search_gather
  (BKE_pbvh_search_gather_ex) that takes the leaf
  test flag as an extra argument.
2022-05-06 16:08:20 -07:00
1c77f259fd temp-pbvh-split: Support pbvh debug node box drawing outside of pbvh
draw mode
2022-05-04 20:58:20 -07:00
17 changed files with 542 additions and 68 deletions

View File

@@ -163,7 +163,8 @@ typedef enum {
PBVH_UpdateTopology = 1 << 13,
PBVH_UpdateColor = 1 << 14,
PBVH_RebuildPixels = 1 << 15,
PBVH_TopologyUpdated = 1 << 16, /* Used internally by pbvh_bmesh.c */
PBVH_TexLeaf = 1 << 16,
PBVH_TopologyUpdated = 1 << 17, /* Used internally by pbvh_bmesh.c */
} PBVHNodeFlags;
@@ -337,7 +338,12 @@ void BKE_pbvh_search_callback(PBVH *pbvh,
void BKE_pbvh_search_gather(
PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot);
void BKE_pbvh_search_gather_ex(PBVH *pbvh,
BKE_pbvh_SearchCallback scb,
void *search_data,
PBVHNode ***r_array,
int *r_tot,
PBVHNodeFlags leaf_flag);
/* Ray-cast
* the hit callback is called for all leaf nodes intersecting the ray;
* it's up to the callback to find the primitive within the leaves that is

View File

@@ -200,6 +200,10 @@ struct NodeData {
{
undo_regions.clear();
for (UDIMTilePixels &tile : tiles) {
if (tile.pixel_rows.size() == 0) {
continue;
}
rcti region;
BLI_rcti_init_minmax(&region);
for (PackedPixelRow &pixel_row : tile.pixel_rows) {

View File

@@ -1020,7 +1020,9 @@ void BKE_pbvh_free(PBVH *pbvh)
if (node->bm_other_verts) {
BLI_gset_free(node->bm_other_verts, NULL);
}
}
if (node->flag & (PBVH_Leaf | PBVH_TexLeaf)) {
pbvh_node_pixels_free(node);
}
}
@@ -1094,7 +1096,7 @@ static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, bool revisiting)
iter->stacksize++;
}
static PBVHNode *pbvh_iter_next(PBVHIter *iter)
static PBVHNode *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag)
{
/* purpose here is to traverse tree, visiting child nodes before their
* parents, this order is necessary for e.g. computing bounding boxes */
@@ -1121,7 +1123,7 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter)
continue; /* don't traverse, outside of search zone */
}
if (node->flag & PBVH_Leaf) {
if (node->flag & leaf_flag) {
/* immediately hit leaf node */
return node;
}
@@ -1166,8 +1168,12 @@ static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter)
return NULL;
}
void BKE_pbvh_search_gather(
PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot)
void BKE_pbvh_search_gather_ex(PBVH *pbvh,
BKE_pbvh_SearchCallback scb,
void *search_data,
PBVHNode ***r_array,
int *r_tot,
PBVHNodeFlags leaf_flag)
{
PBVHIter iter;
PBVHNode **array = NULL, *node;
@@ -1175,8 +1181,8 @@ void BKE_pbvh_search_gather(
pbvh_iter_begin(&iter, pbvh, scb, search_data);
while ((node = pbvh_iter_next(&iter))) {
if (node->flag & PBVH_Leaf) {
while ((node = pbvh_iter_next(&iter, leaf_flag))) {
if (node->flag & leaf_flag) {
if (UNLIKELY(tot == space)) {
/* resize array if needed */
space = (tot == 0) ? 32 : space * 2;
@@ -1199,6 +1205,12 @@ void BKE_pbvh_search_gather(
*r_tot = tot;
}
void BKE_pbvh_search_gather(
PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot)
{
BKE_pbvh_search_gather_ex(pbvh, scb, search_data, r_array, r_tot, PBVH_Leaf);
}
void BKE_pbvh_search_callback(PBVH *pbvh,
BKE_pbvh_SearchCallback scb,
void *search_data,
@@ -1210,7 +1222,7 @@ void BKE_pbvh_search_callback(PBVH *pbvh,
pbvh_iter_begin(&iter, pbvh, scb, search_data);
while ((node = pbvh_iter_next(&iter))) {
while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) {
if (node->flag & PBVH_Leaf) {
hcb(node, hit_data);
}
@@ -1946,7 +1958,7 @@ void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3])
pbvh_iter_begin(&iter, pbvh, NULL, NULL);
while ((node = pbvh_iter_next(&iter))) {
while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) {
if (node->flag & PBVH_UpdateRedraw) {
BB_expand_with_bb(&bb, &node->vb);
}
@@ -1966,7 +1978,7 @@ void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int
pbvh_iter_begin(&iter, pbvh, NULL, NULL);
while ((node = pbvh_iter_next(&iter))) {
while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) {
if (node->flag & PBVH_UpdateNormals) {
for (uint i = 0; i < node->totprim; i++) {
void *face = pbvh->gridfaces[node->prim_indices[i]];
@@ -3147,9 +3159,24 @@ void BKE_pbvh_draw_debug_cb(PBVH *pbvh,
PBVHNodeFlags flag),
void *user_data)
{
PBVHNodeFlags flag = PBVH_Leaf;
for (int a = 0; a < pbvh->totnode; a++) {
PBVHNode *node = &pbvh->nodes[a];
if (node->flag & PBVH_TexLeaf) {
flag = PBVH_TexLeaf;
break;
}
}
for (int a = 0; a < pbvh->totnode; a++) {
PBVHNode *node = &pbvh->nodes[a];
if (!(node->flag & flag)) {
continue;
}
draw_fn(node, user_data, node->vb.bmin, node->vb.bmax, node->flag);
}
}

View File

@@ -150,6 +150,8 @@ struct PBVH {
int faces_num; /* Do not use directly, use BKE_pbvh_num_faces. */
int leaf_limit;
int pixel_leaf_limit;
int depth_limit;
/* Mesh data */
struct Mesh *mesh;

View File

@@ -15,7 +15,9 @@
#include "BLI_math.h"
#include "BLI_task.h"
#include "PIL_time.h"
#include "BKE_global.h"
#include "BKE_image_wrappers.hh"
#include "bmesh.h"
@@ -25,12 +27,6 @@
namespace blender::bke::pbvh::pixels {
/**
* During debugging this check could be enabled.
* It will write to each image pixel that is covered by the PBVH.
*/
constexpr bool USE_WATERTIGHT_CHECK = false;
/**
* Calculate the delta of two neighbor UV coordinates in the given image buffer.
*/
@@ -57,6 +53,315 @@ static float2 calc_barycentric_delta_x(const ImBuf *image_buffer,
return calc_barycentric_delta(uvs, start_uv, end_uv);
}
static int count_node_pixels(PBVHNode &node)
{
if (!node.pixels.node_data) {
return 0;
}
NodeData &data = BKE_pbvh_pixels_node_data_get(node);
int totpixel = 0;
for (UDIMTilePixels &tile : data.tiles) {
for (PackedPixelRow &row : tile.pixel_rows) {
totpixel += row.num_pixels;
}
}
return totpixel;
}
struct SplitQueueData {
ThreadQueue *new_nodes;
TaskPool *pool;
PBVH *pbvh;
Mesh *mesh;
Image *image;
ImageUser *image_user;
};
struct SplitNodePair {
SplitNodePair *parent;
PBVHNode node;
int children_offset = 0;
int depth = 0;
int source_index = -1;
bool is_old = false;
SplitQueueData *tdata;
SplitNodePair(SplitNodePair *node_parent = nullptr) : parent(node_parent)
{
memset(static_cast<void *>(&node), 0, sizeof(PBVHNode));
}
};
static void split_thread_job(TaskPool *__restrict pool, void *taskdata);
static void split_pixel_node(PBVH *pbvh,
SplitNodePair *split,
Mesh *mesh,
Image *image,
ImageUser *image_user,
SplitQueueData *tdata)
{
BB cb;
PBVHNode *node = &split->node;
cb = node->vb;
if (count_node_pixels(*node) <= pbvh->pixel_leaf_limit || split->depth >= pbvh->depth_limit) {
BKE_pbvh_pixels_node_data_get(split->node).rebuild_undo_regions();
return;
}
/* Find widest axis and its midpoint */
const int axis = BB_widest_axis(&cb);
const float mid = (cb.bmax[axis] + cb.bmin[axis]) * 0.5f;
node->flag = (PBVHNodeFlags)((int)node->flag & (int)~PBVH_TexLeaf);
SplitNodePair *split1 = MEM_new<SplitNodePair>("split_pixel_node split1", split);
SplitNodePair *split2 = MEM_new<SplitNodePair>("split_pixel_node split1", split);
split1->depth = split->depth + 1;
split2->depth = split->depth + 1;
PBVHNode *child1 = &split1->node;
PBVHNode *child2 = &split2->node;
child1->flag = PBVH_TexLeaf;
child2->flag = PBVH_TexLeaf;
child1->vb = cb;
child1->vb.bmax[axis] = mid;
child2->vb = cb;
child2->vb.bmin[axis] = mid;
NodeData &data = BKE_pbvh_pixels_node_data_get(split->node);
NodeData *data1 = MEM_new<NodeData>(__func__);
NodeData *data2 = MEM_new<NodeData>(__func__);
child1->pixels.node_data = static_cast<void *>(data1);
child2->pixels.node_data = static_cast<void *>(data2);
data1->uv_primitives = data.uv_primitives;
data2->uv_primitives = data.uv_primitives;
data1->tiles.resize(data.tiles.size());
data2->tiles.resize(data.tiles.size());
for (int i : IndexRange(data.tiles.size())) {
UDIMTilePixels &tile = data.tiles[i];
UDIMTilePixels &tile1 = data1->tiles[i];
UDIMTilePixels &tile2 = data2->tiles[i];
tile1.tile_number = tile2.tile_number = tile.tile_number;
tile1.flags.dirty = tile2.flags.dirty = 0;
}
ImageUser image_user2 = *image_user;
for (int i : IndexRange(data.tiles.size())) {
const UDIMTilePixels &tile = data.tiles[i];
image_user2.tile = tile.tile_number;
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user2, nullptr);
if (image_buffer == nullptr) {
continue;
}
const float(*vert_cos)[3] = BKE_pbvh_get_vert_positions(pbvh);
PBVHData &pbvh_data = BKE_pbvh_pixels_data_get(*pbvh);
for (const PackedPixelRow &row : tile.pixel_rows) {
UDIMTilePixels *tile1 = &data1->tiles[i];
UDIMTilePixels *tile2 = &data2->tiles[i];
UVPrimitivePaintInput &uv_prim = data.uv_primitives.paint_input[row.uv_primitive_index];
int3 tri = pbvh_data.geom_primitives.vert_indices[uv_prim.geometry_primitive_index];
float verts[3][3];
copy_v3_v3(verts[0], vert_cos[tri[0]]);
copy_v3_v3(verts[1], vert_cos[tri[1]]);
copy_v3_v3(verts[2], vert_cos[tri[2]]);
float2 delta = uv_prim.delta_barycentric_coord_u;
float2 uv1 = row.start_barycentric_coord;
float2 uv2 = row.start_barycentric_coord + delta * (float)row.num_pixels;
float co1[3];
float co2[3];
interp_barycentric_tri_v3(verts, uv1[0], uv1[1], co1);
interp_barycentric_tri_v3(verts, uv2[0], uv2[1], co2);
/* Are we spanning the midpoint? */
if ((co1[axis] <= mid) != (co2[axis] <= mid)) {
PackedPixelRow row1 = row;
float t;
if (mid < co1[axis]) {
t = 1.0f - (mid - co2[axis]) / (co1[axis] - co2[axis]);
SWAP(UDIMTilePixels *, tile1, tile2);
}
else {
t = (mid - co1[axis]) / (co2[axis] - co1[axis]);
}
int num_pixels = (int)floorf((float)row.num_pixels * t);
if (num_pixels) {
row1.num_pixels = num_pixels;
tile1->pixel_rows.append(row1);
}
if (num_pixels != row.num_pixels) {
PackedPixelRow row2 = row;
row2.num_pixels = row.num_pixels - num_pixels;
row2.start_barycentric_coord = row.start_barycentric_coord +
uv_prim.delta_barycentric_coord_u * (float)num_pixels;
row2.start_image_coordinate = row.start_image_coordinate;
row2.start_image_coordinate[0] += num_pixels;
tile2->pixel_rows.append(row2);
}
}
else if (co1[axis] <= mid && co2[axis] <= mid) {
tile1->pixel_rows.append(row);
}
else {
tile2->pixel_rows.append(row);
}
}
BKE_image_release_ibuf(image, image_buffer, nullptr);
}
data.undo_regions.clear();
if (node->flag & PBVH_Leaf) {
data.clear_data();
}
else {
pbvh_node_pixels_free(node);
}
BLI_thread_queue_push(tdata->new_nodes, static_cast<void *>(split1));
BLI_thread_queue_push(tdata->new_nodes, static_cast<void *>(split2));
BLI_task_pool_push(tdata->pool, split_thread_job, static_cast<void *>(split1), false, nullptr);
BLI_task_pool_push(tdata->pool, split_thread_job, static_cast<void *>(split2), false, nullptr);
}
static void split_flush_final_nodes(SplitQueueData *tdata)
{
PBVH *pbvh = tdata->pbvh;
Vector<SplitNodePair *> splits;
while (!BLI_thread_queue_is_empty(tdata->new_nodes)) {
SplitNodePair *newsplit = static_cast<SplitNodePair *>(BLI_thread_queue_pop(tdata->new_nodes));
splits.append(newsplit);
if (newsplit->is_old) {
continue;
}
if (!newsplit->parent->children_offset) {
newsplit->parent->children_offset = pbvh->totnode;
pbvh_grow_nodes(pbvh, pbvh->totnode + 2);
newsplit->source_index = newsplit->parent->children_offset;
}
else {
newsplit->source_index = newsplit->parent->children_offset + 1;
}
}
for (SplitNodePair *split : splits) {
BLI_assert(split->source_index != -1);
split->node.children_offset = split->children_offset;
pbvh->nodes[split->source_index] = split->node;
}
for (SplitNodePair *split : splits) {
MEM_delete<SplitNodePair>(split);
}
}
static void split_thread_job(TaskPool *__restrict pool, void *taskdata)
{
SplitQueueData *tdata = static_cast<SplitQueueData *>(BLI_task_pool_user_data(pool));
SplitNodePair *split = static_cast<SplitNodePair *>(taskdata);
split_pixel_node(tdata->pbvh, split, tdata->mesh, tdata->image, tdata->image_user, tdata);
}
static void split_pixel_nodes(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
{
if (G.debug_value == 891) {
return;
}
if (!pbvh->depth_limit) {
pbvh->depth_limit = 40; /* TODO: move into a constant */
}
if (!pbvh->pixel_leaf_limit) {
pbvh->pixel_leaf_limit = 256 * 256; /* TODO: move into a constant */
}
SplitQueueData tdata;
TaskPool *pool = BLI_task_pool_create_suspended(&tdata, TASK_PRIORITY_HIGH);
tdata.pool = pool;
tdata.pbvh = pbvh;
tdata.mesh = mesh;
tdata.image = image;
tdata.image_user = image_user;
tdata.new_nodes = BLI_thread_queue_init();
/* Set up initial jobs before initializing threads. */
for (int i : IndexRange(pbvh->totnode)) {
if (pbvh->nodes[i].flag & PBVH_TexLeaf) {
SplitNodePair *split = MEM_new<SplitNodePair>("split_pixel_nodes split");
split->source_index = i;
split->is_old = true;
split->node = pbvh->nodes[i];
split->tdata = &tdata;
BLI_task_pool_push(pool, split_thread_job, static_cast<void *>(split), false, nullptr);
BLI_thread_queue_push(tdata.new_nodes, static_cast<void *>(split));
}
}
BLI_task_pool_work_and_wait(pool);
BLI_task_pool_free(pool);
split_flush_final_nodes(&tdata);
BLI_thread_queue_free(tdata.new_nodes);
}
/**
* During debugging this check could be enabled.
* It will write to each image pixel that is covered by the PBVH.
*/
constexpr bool USE_WATERTIGHT_CHECK = false;
static void extract_barycentric_pixels(UDIMTilePixels &tile_data,
const ImBuf *image_buffer,
const uv_islands::UVIslandsMask &uv_mask,
@@ -233,7 +538,10 @@ static void do_encode_pixels(void *__restrict userdata,
static bool should_pixels_be_updated(PBVHNode *node)
{
if ((node->flag & PBVH_Leaf) == 0) {
if ((node->flag & (PBVH_Leaf | PBVH_TexLeaf)) == 0) {
return false;
}
if (node->children_offset != 0) {
return false;
}
if ((node->flag & PBVH_RebuildPixels) != 0) {
@@ -349,17 +657,17 @@ static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_us
BKE_image_partial_update_mark_full_update(image);
}
static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
static bool update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
{
Vector<PBVHNode *> nodes_to_update;
if (!find_nodes_to_update(pbvh, nodes_to_update)) {
return;
return false;
}
const StringRef active_uv_name = CustomData_get_active_layer_name(&mesh->ldata, CD_PROP_FLOAT2);
if (active_uv_name.is_empty()) {
return;
return false;
}
const AttributeAccessor attributes = mesh->attributes();
@@ -422,6 +730,15 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels);
}
/* Add PBVH_TexLeaf flag */
for (int i : IndexRange(pbvh->totnode)) {
PBVHNode &node = pbvh->nodes[i];
if (node.flag & PBVH_Leaf) {
node.flag = (PBVHNodeFlags)((int)node.flag | (int)PBVH_TexLeaf);
}
}
//#define DO_PRINT_STATISTICS
#ifdef DO_PRINT_STATISTICS
/* Print some statistics about compression ratio. */
@@ -434,7 +751,6 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
continue;
}
NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
compressed_data_len += node_data->triangles.mem_size();
for (const UDIMTilePixels &tile_data : node_data->tiles) {
compressed_data_len += tile_data.encoded_pixels.size() * sizeof(PackedPixelRow);
for (const PackedPixelRow &encoded_pixels : tile_data.encoded_pixels) {
@@ -448,6 +764,8 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
float(compressed_data_len) / num_pixels);
}
#endif
return true;
}
NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node)
@@ -484,7 +802,6 @@ void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &i
node_data->flags.dirty = false;
}
}
} // namespace blender::bke::pbvh::pixels
extern "C" {
@@ -492,12 +809,19 @@ using namespace blender::bke::pbvh::pixels;
void BKE_pbvh_build_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
{
update_pixels(pbvh, mesh, image, image_user);
if (update_pixels(pbvh, mesh, image, image_user)) {
split_pixel_nodes(pbvh, mesh, image, image_user);
}
}
void pbvh_node_pixels_free(PBVHNode *node)
{
NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
if (!node_data) {
return;
}
MEM_delete(node_data);
node->pixels.node_data = nullptr;
}

View File

@@ -10,9 +10,11 @@
#include "DRW_render.h"
#include "BKE_global.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_pbvh.h"
#include "BLI_alloca.h"
@@ -219,6 +221,16 @@ static void basic_cache_populate(void *vedata, Object *ob)
DRW_shgroup_call(shgrp, geom, ob);
}
}
if (G.debug_value == 889 && ob->sculpt && ob->sculpt->pbvh) {
int debug_node_nr = 0;
DRW_debug_modelmat(ob->object_to_world);
BKE_pbvh_draw_debug_cb(
ob->sculpt->pbvh,
(void (*)(void *d, const float min[3], const float max[3], PBVHNodeFlags f))
DRW_sculpt_debug_cb,
&debug_node_nr);
}
}
}

View File

@@ -15,6 +15,7 @@
#include "BLI_rand.h"
#include "BLI_string_utils.h"
#include "BKE_global.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
@@ -883,6 +884,16 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
*cast_shadow = *cast_shadow || (matcache[i].shadow_grp != NULL);
}
}
if (G.debug_value == 889 && ob->sculpt && ob->sculpt->pbvh) {
int debug_node_nr = 0;
DRW_debug_modelmat(ob->object_to_world);
BKE_pbvh_draw_debug_cb(
ob->sculpt->pbvh,
(void (*)(void *d, const float min[3], const float max[3], PBVHNodeFlags f))
DRW_sculpt_debug_cb,
&debug_node_nr);
}
}
/* Motion Blur Vectors. */

View File

@@ -17,6 +17,7 @@
#include "BKE_context.h"
#include "BKE_layer.h"
#include "BKE_material.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "BLT_translation.h"
@@ -1007,6 +1008,9 @@ void DRW_mesh_batch_cache_get_attributes(struct Object *object,
struct DRW_Attributes **r_attrs,
struct DRW_MeshCDMask **r_cd_needed);
void DRW_sculpt_debug_cb(
PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag);
#ifdef __cplusplus
}
#endif

View File

@@ -1255,7 +1255,7 @@ static void sculpt_draw_cb(DRWSculptCallbackData *scd,
}
}
static void sculpt_debug_cb(
void DRW_sculpt_debug_cb(
PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag)
{
int *debug_node_nr = (int *)user_data;
@@ -1270,7 +1270,8 @@ static void sculpt_debug_cb(
DRW_debug_bbox(&bb, (float[4]){0.5f, 0.5f, 0.5f, 0.6f});
}
#else /* Color coded leaf bounds. */
if (flag & PBVH_Leaf) {
if (flag & (PBVH_Leaf | PBVH_TexLeaf)) {
DRW_debug_bbox(&bb, SCULPT_DEBUG_COLOR((*debug_node_nr)++));
int color = (*debug_node_nr)++;
color += BKE_pbvh_debug_draw_gen_get(node);
@@ -1370,7 +1371,7 @@ static void drw_sculpt_generate_calls(DRWSculptCallbackData *scd)
BKE_pbvh_draw_debug_cb(
pbvh,
(void (*)(PBVHNode * n, void *d, const float min[3], const float max[3], PBVHNodeFlags f))
sculpt_debug_cb,
DRW_sculpt_debug_cb,
&debug_node_nr);
}
}

View File

@@ -333,7 +333,7 @@ struct DRWDebugVert {
BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16)
/* Take the header (DrawCommand) into account. */
#define DRW_DEBUG_DRAW_VERT_MAX (64 * 1024) - 1
#define DRW_DEBUG_DRAW_VERT_MAX (64 * 8192) - 1
/* The debug draw buffer is laid-out as the following struct.
* But we use plain array in shader code instead because of driver issues. */

View File

@@ -2775,15 +2775,21 @@ static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob,
return nodes;
}
static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
Sculpt *sd,
const Brush *brush,
bool use_original,
float radius_scale,
int *r_totnode)
static PBVHNode **sculpt_pbvh_gather_generic_intern(Object *ob,
Sculpt *sd,
const Brush *brush,
bool use_original,
float radius_scale,
int *r_totnode,
PBVHNodeFlags flag)
{
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = nullptr;
PBVHNodeFlags leaf_flag = PBVH_Leaf;
if (flag & PBVH_TexLeaf) {
leaf_flag = PBVH_TexLeaf;
}
/* Build a list of all nodes that are potentially within the cursor or brush's area of influence.
*/
@@ -2795,7 +2801,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
data.original = use_original;
data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK;
data.center = nullptr;
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
BKE_pbvh_search_gather_ex(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode, leaf_flag);
}
else {
DistRayAABB_Precalc dist_ray_to_aabb_precalc;
@@ -2809,11 +2815,33 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
data.original = use_original;
data.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc;
data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK;
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode);
BKE_pbvh_search_gather_ex(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode, leaf_flag);
}
return nodes;
}
static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
Sculpt *sd,
const Brush *brush,
bool use_original,
float radius_scale,
int *r_totnode)
{
return sculpt_pbvh_gather_generic_intern(
ob, sd, brush, use_original, radius_scale, r_totnode, PBVH_Leaf);
}
static PBVHNode **sculpt_pbvh_gather_texpaint(Object *ob,
Sculpt *sd,
const Brush *brush,
bool use_original,
float radius_scale,
int *r_totnode)
{
return sculpt_pbvh_gather_generic_intern(
ob, sd, brush, use_original, radius_scale, r_totnode, PBVH_TexLeaf);
}
/* Calculate primary direction of movement for many brushes. */
static void calc_sculpt_normal(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3])
@@ -3440,8 +3468,8 @@ static void do_brush_action(Sculpt *sd,
PaintModeSettings *paint_mode_settings)
{
SculptSession *ss = ob->sculpt;
int totnode;
PBVHNode **nodes;
int totnode, texnodes_num = 0;
PBVHNode **nodes, **texnodes = NULL;
/* Check for unsupported features. */
PBVHType type = BKE_pbvh_type(ss->pbvh);
@@ -3454,6 +3482,20 @@ static void do_brush_action(Sculpt *sd,
BKE_pbvh_ensure_node_loops(ss->pbvh);
}
const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
ss->cache->original;
const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob);
if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) {
sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob);
texnodes = sculpt_pbvh_gather_texpaint(ob, sd, brush, use_original, 1.0f, &texnodes_num);
if (!texnodes_num) {
return;
}
}
/* Build a list of all nodes that are potentially within the brush's area of influence */
if (SCULPT_tool_needs_all_pbvh_nodes(brush)) {
@@ -3464,8 +3506,6 @@ static void do_brush_action(Sculpt *sd,
nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode);
}
else {
const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
ss->cache->original;
float radius_scale = 1.0f;
/* Corners of square brushes can go outside the brush radius. */
@@ -3480,10 +3520,6 @@ static void do_brush_action(Sculpt *sd,
}
nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
}
const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob);
if (use_pixels) {
sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob);
}
/* Draw Face Sets in draw mode makes a single undo push, in alt-smooth mode deforms the
* vertices and uses regular coords undo. */
@@ -3524,6 +3560,7 @@ static void do_brush_action(Sculpt *sd,
/* Only act if some verts are inside the brush area. */
if (totnode == 0) {
MEM_SAFE_FREE(texnodes);
return;
}
float location[3];
@@ -3671,7 +3708,7 @@ static void do_brush_action(Sculpt *sd,
SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_PAINT:
SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode);
SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode, texnodes, texnodes_num);
break;
case SCULPT_TOOL_SMEAR:
SCULPT_do_smear_brush(sd, ob, nodes, totnode);
@@ -3715,6 +3752,7 @@ static void do_brush_action(Sculpt *sd,
}
MEM_SAFE_FREE(nodes);
MEM_SAFE_FREE(texnodes);
/* Update average stroke position. */
copy_v3_v3(location, ss->cache->true_location);

View File

@@ -1789,7 +1789,9 @@ void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings,
Sculpt *sd,
Object *ob,
PBVHNode **nodes,
int totnode) ATTR_NONNULL();
int totnode,
PBVHNode **texnodes,
int texnodes_num) ATTR_NONNULL();
/**
* \brief Get the image canvas for painting on the given object.
@@ -1806,7 +1808,9 @@ void SCULPT_do_paint_brush_image(struct PaintModeSettings *paint_mode_settings,
Sculpt *sd,
Object *ob,
PBVHNode **nodes,
int totnode) ATTR_NONNULL();
int totnode,
PBVHNode **texnodes,
int texnode_num) ATTR_NONNULL();
bool SCULPT_use_image_paint_brush(struct PaintModeSettings *settings, Object *ob) ATTR_NONNULL();
/* Smear Brush. */

View File

@@ -249,11 +249,17 @@ static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata),
add_v4_v4(join->color, swptd->color);
}
void SCULPT_do_paint_brush(
PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
Sculpt *sd,
Object *ob,
PBVHNode **nodes,
int totnode,
PBVHNode **texnodes,
int texnodes_num)
{
if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) {
SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, nodes, totnode);
SCULPT_do_paint_brush_image(
paint_mode_settings, sd, ob, nodes, totnode, texnodes, texnodes_num);
return;
}

View File

@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/* Paint a color made from hash of node pointer. */
//#define DEBUG_PIXEL_NODES
#include "DNA_image_types.h"
#include "DNA_object_types.h"
@@ -9,6 +12,9 @@
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_task.h"
#ifdef DEBUG_PIXEL_NODES
# include "BLI_hash.h"
#endif
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
@@ -187,6 +193,15 @@ template<typename ImageBuffer> class PaintingKernel {
automask_data);
float4 paint_color = brush_color * falloff_strength * brush_strength;
float4 buffer_color;
#ifdef DEBUG_PIXEL_NODES
if ((pixel_row.start_image_coordinate.y >> 3) & 1) {
paint_color[0] *= 0.5f;
paint_color[1] *= 0.5f;
paint_color[2] *= 0.5f;
}
#endif
blend_color_mix_float(buffer_color, color, paint_color);
buffer_color *= brush->alpha;
IMB_blend_color_float(color, color, buffer_color, static_cast<IMB_BlendMode>(brush->blend));
@@ -199,20 +214,18 @@ template<typename ImageBuffer> class PaintingKernel {
return pixels_painted;
}
void init_brush_color(ImBuf *image_buffer)
void init_brush_color(ImBuf *image_buffer, float in_brush_color[3])
{
const char *to_colorspace = image_accessor.get_colorspace_name(image_buffer);
if (last_used_color_space == to_colorspace) {
return;
}
copy_v3_v3(brush_color,
ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) :
BKE_brush_color_get(ss->scene, brush));
/* NOTE: Brush colors are stored in sRGB. We use math color to follow other areas that
* use brush colors. From there on we use IMB_colormanagement to convert the brush color to the
* colorspace of the texture. This isn't ideal, but would need more refactoring to make sure
* that brush colors are stored in scene linear by default. */
srgb_to_linearrgb_v3_v3(brush_color, brush_color);
srgb_to_linearrgb_v3_v3(brush_color, in_brush_color);
brush_color[3] = 1.0f;
const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(
@@ -336,6 +349,22 @@ static void do_paint_pixels(void *__restrict userdata,
PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, positions);
PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, positions);
float brush_color[4];
#ifdef DEBUG_PIXEL_NODES
uint hash = BLI_hash_int(POINTER_AS_UINT(node));
brush_color[0] = (float)(hash & 255) / 255.0f;
brush_color[1] = (float)((hash >> 8) & 255) / 255.0f;
brush_color[2] = (float)((hash >> 16) & 255) / 255.0f;
#else
copy_v3_v3(brush_color,
ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) :
BKE_brush_color_get(ss->scene, brush));
#endif
brush_color[3] = 1.0f;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
@@ -353,10 +382,10 @@ static void do_paint_pixels(void *__restrict userdata,
}
if (image_buffer->rect_float != nullptr) {
kernel_float4.init_brush_color(image_buffer);
kernel_float4.init_brush_color(image_buffer, brush_color);
}
else {
kernel_byte4.init_brush_color(image_buffer);
kernel_byte4.init_brush_color(image_buffer, brush_color);
}
for (const PackedPixelRow &pixel_row : tile_data.pixel_rows) {
@@ -520,27 +549,33 @@ bool SCULPT_use_image_paint_brush(PaintModeSettings *settings, Object *ob)
return BKE_paint_canvas_image_get(settings, ob, &image, &image_user);
}
void SCULPT_do_paint_brush_image(
PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
void SCULPT_do_paint_brush_image(PaintModeSettings *paint_mode_settings,
Sculpt *sd,
Object *ob,
PBVHNode **nodes,
int totnode,
PBVHNode **texnodes,
int texnodes_num)
{
Brush *brush = BKE_paint_brush(&sd->paint);
TexturePaintingUserData data = {nullptr};
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.nodes = texnodes;
if (!ImageData::init_active_image(ob, &data.image_data, paint_mode_settings)) {
return;
}
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings);
BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings);
BKE_pbvh_parallel_range_settings(&settings, true, texnodes_num);
BLI_task_parallel_range(0, texnodes_num, &data, do_push_undo_tile, &settings);
BLI_task_parallel_range(0, texnodes_num, &data, do_paint_pixels, &settings);
TaskParallelSettings settings_flush;
BKE_pbvh_parallel_range_settings(&settings_flush, false, totnode);
BLI_task_parallel_range(0, totnode, &data, do_mark_dirty_regions, &settings_flush);
BKE_pbvh_parallel_range_settings(&settings_flush, false, texnodes_num);
BLI_task_parallel_range(0, texnodes_num, &data, do_mark_dirty_regions, &settings_flush);
}
}

View File

@@ -2090,7 +2090,7 @@ static UndoSculpt *sculpt_undo_get_nodes(void)
{
UndoStack *ustack = ED_undo_stack_get();
UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT);
return sculpt_undosys_step_get_nodes(us);
return us ? sculpt_undosys_step_get_nodes(us) : NULL;
}
/** \} */