1
1

Compare commits

...

154 Commits

Author SHA1 Message Date
206f3352dd fix missing type conversion 2021-06-28 15:46:59 +02:00
dd92220f0b Merge branch 'master' into temp-attribute-processor 2021-06-28 13:32:16 +02:00
5639dcef62 Fix after merge 2021-06-23 18:54:05 +02:00
a20e0fd1a5 fix 2021-06-23 14:23:06 +02:00
448710996b Merge branch 'master' into temp-attribute-processor 2021-06-23 14:00:05 +02:00
3b41749245 initial support for voronoi node
Only 3D in F1 mode with euclidean distance is support right now.
2021-06-23 08:34:54 +02:00
00944353be cleanup menu 2021-06-22 09:47:28 +02:00
623818f2d5 initial support for noise texture 2021-06-22 09:44:25 +02:00
4b7eae5cd3 Merge branch 'master' into temp-attribute-processor 2021-06-16 15:16:04 +02:00
1172c0ae44 Merge branch 'master' into temp-attribute-processor 2021-06-15 15:22:10 +02:00
7f17d15125 fix bugs 2021-06-10 14:56:30 +02:00
b33fd7e89e add Position Input/Output and Attribute Input node 2021-06-10 12:52:45 +02:00
10b78013eb support default mode again and have separate default attribute name 2021-06-10 12:07:37 +02:00
3d057de976 Merge branch 'master' into temp-attribute-processor 2021-06-10 09:35:41 +02:00
3817b408e0 fix crash when creating group 2021-06-09 14:01:03 +02:00
b8d3ad515a fix copying default value from node group inputs 2021-06-09 12:50:44 +02:00
022b541cb3 node group fixes 2021-06-09 12:44:33 +02:00
e294e7a99c fixes 2021-06-09 12:26:29 +02:00
7928578f18 support Set Attribute node 2021-06-09 12:13:44 +02:00
369aa3a8a0 improve Set Attribute node 2021-06-09 11:41:25 +02:00
243b5ac98c add initial Set Attribute node 2021-06-09 11:19:05 +02:00
b63483baec cleanup 2021-06-09 11:08:07 +02:00
4ee0e62986 cleanup 2021-06-09 10:54:59 +02:00
b2184402ae support selection 2021-06-09 10:52:13 +02:00
028777bdba cleanup 2021-06-09 10:41:50 +02:00
e43c171455 Cleanup 2021-06-09 10:37:02 +02:00
e433e4ae42 cleanup 2021-06-09 10:18:47 +02:00
f22aa19845 cleanup 2021-06-09 10:17:56 +02:00
3391649edd cleanup 2021-06-09 10:17:21 +02:00
9e95222cb7 Geometry Nodes: New Attribute Processor.
Differential Revision: https://developer.blender.org/D11547
2021-06-09 10:14:55 +02:00
ef07d5a297 fix 2021-06-08 16:42:41 +02:00
09ec88413b fix 2021-06-08 16:36:11 +02:00
b9c8bb4254 Merge branch 'master' into temp-attribute-processor 2021-06-08 16:32:51 +02:00
64eb1df4b3 move Index node to input category 2021-06-07 16:59:08 +02:00
9a9b87d9c5 support attribute node 2021-06-07 16:59:08 +02:00
257b6bcc61 move operator from python to c++ 2021-06-07 16:59:08 +02:00
b53babaae7 update socket names 2021-06-07 16:59:08 +02:00
aa9efbedf1 remove default modes 2021-06-07 16:59:08 +02:00
93a5fe3a37 use enum for socket types 2021-06-07 16:59:08 +02:00
90de4edc06 improve add node group interface operator 2021-06-07 16:59:08 +02:00
138a0c8109 make attribute processor more prominent in the menu 2021-06-07 16:59:08 +02:00
3a06d098da Fix T88899: __file__ not set for text.as_module() 2021-06-07 16:59:08 +02:00
10636857f8 Event Simulate: and a --keep-open command line argument
It can be useful to investigate the state of the file
after event simulation runs.
2021-06-07 16:59:08 +02:00
e69f9fe0ca Cleanup: unused argument 2021-06-07 16:59:08 +02:00
3b39482e2c LineArt: Fix edge clipping index error.
Small bug that's causing edge count to be incorrect in
final culled list, just being offset exactly 1 entry.

Reviewed By: Sebastian Parborg (zeddb)

Differential Revision: https://developer.blender.org/D11513
2021-06-07 16:59:08 +02:00
82c72ccdbc VSE: Remove seq->tmp usage from transform code
This field was used for extend feature to get handle position of
metastrip children. Since D9972 extend feature works only on meta
strip itself, not it's children.
So `SEQ_transform_get_left_handle_frame()` second argument is always
false and can be removed.

Another instance of `seq->tmp usage` is hack to distinguish strips to be
shuffled, which is not covered by this patch.

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D10321
2021-06-07 16:59:08 +02:00
6ea2f106a0 Exact Boolean: fix last commit: pass an arg by reference instead of value. 2021-06-07 16:59:08 +02:00
c9d4f5d625 Exact Boolean: speed up when there are many separate components.
Use bounding box tests quickly tell that two components cannot
have a containment relation between each other. This change
cut about 0.6s off a test with 25 big icospheres.
2021-06-07 16:59:08 +02:00
22c7967205 Limit Rotation: explicitly orthogonalize the matrix before processing.
Add a call to orthogonalize the matrix before processing for the
same reasons as D8915, and an early exit in case no limits are
enabled for a bit of extra efficiency.

Since the constraint goes through Euler decomposition, it would
in fact remove shear even before this change, but the resulting
rotation won't make much sense.

This change allows using the constraint without any enabled limits
purely for the purpose of efficiently removing shear.

Differential Revision: https://developer.blender.org/D9626
2021-06-07 16:59:08 +02:00
56088650be Limit Rotation: add an Euler Order option.
Since Limit Rotation is based on Euler decomposition, it should allow
specifying the order to use for the same reasons as Copy Rotation does,
namely, if the bone uses Quaternion rotation for its animation channels,
there is no way to choose the order for the constraint.

Ref D9626
2021-06-07 16:59:08 +02:00
14ec1f4e13 Fix T88859: Assert when changing view modes
The `loose_lines`' ibo was not being initialized.
2021-06-07 16:59:08 +02:00
33d262a7c8 BMesh: avoid extra faces-of-edges loop building partial update data 2021-06-07 16:59:08 +02:00
ec04ff8a33 Fix assert check in BLI_polyfill_beautify 2021-06-07 16:59:08 +02:00
c5c607bdaa Fix assert in gpencil_batches_ensure 2021-06-07 16:59:08 +02:00
f4392d1423 Cleanup: use ternary operator for icon argument 2021-06-07 16:59:08 +02:00
005f7318b2 Fix T88828: View/Navigation(Walk/Fly) disappeared from keymap
This was unintentionally removed in
f92f5d1ac6.
2021-06-07 16:59:08 +02:00
93e08fdf90 Cleanup: indentation 2021-06-07 16:59:08 +02:00
92e16e35dc Cleanup: spelling in comments
Also remove reference to function that never existed for adding `bNode`.
2021-06-07 16:59:08 +02:00
2abb09aa2b FFmpeg: Fix seeking not returning the correct frame when not using TC index
Fixed the logic for seeking in ffmpeg video files.
The main fix is that we now apply a small offset in ffmpeg_get_seek_pos
to make sure we don't get the frame in front of the seek position when
seeking backward.

The rest of the changes is general cleanup and untangling code.

Reviewed By: Richard Antalik

Differential Revision: http://developer.blender.org/D11492
2021-06-07 16:59:07 +02:00
a371d4bb2b Fix T88813: Scalable allocator not used on win10
Due to the way we ship the CRT on windows TBB's
malloc proxy was unable to attach it self to
the memory management functions on windows 10.

This change moves ucrtbase.dll out of the blender.crt
folder and back into the main blender folder to side
step some undesirable behaviour on win10 making TBB
once more able to attach it self.

Having this work again, should give a speed
boost in memory allocation heavy workloads
such as mantaflow.

For details on how this only failed on Win10
see T88813
2021-06-07 16:59:07 +02:00
03f9dba616 Edit Mesh: partial updates for normal and face tessellation
This patch exposes functionality for performing partial mesh updates
for normal calculation and face tessellation while transforming a mesh.

The partial update data only needs to be generated once,
afterwards the cached connectivity information can be reused
(with the exception of changing proportional editing radius).

Currently this is only used for transform, in the future it could be
used for other operators as well as the transform panel.

The best-case overall speedup while transforming geometry is about
1.45x since the time to update a small number of normals and faces is
negligible.

For an additional speedup partial face tessellation is multi-threaded,
this gives ~15x speedup on my system (timing tessellation alone).
Exact results depend on the number of CPU cores available.

Ref D11494

Reviewed By: mano-wii
2021-06-07 16:59:07 +02:00
Charlie Jolly
5f2ad6751c Nodes: Add Multiply Add to Vector Math nodes
Cycles, Eevee, OSL, Geo, Attribute

This operator provides consistency with the standard math node. Allows users to use a single node instead of two nodes for this common operation.

Reviewed By: HooglyBoogly, brecht

Differential Revision: https://developer.blender.org/D10808
2021-06-07 16:59:07 +02:00
04e6656ff1 windows/deps: Fix TBB build issues.
rB847579b42250 updated the TBB build script
which had some unintended consequences for
windows as the directory layout slightly
changed.

This change adjusts the builder to the new
structure, there are no version/functional
changes.
2021-06-07 16:59:07 +02:00
Philipp Oeser
0ed005d48a Texture Paint: changing paint slots and viewport could go out of sync
When changing to another texture paint slot, the texture displayed in
the viewport should change accordingly (as well as the image displayed
in the Image Editor).

The procedure to find the texture to display in the viewport
(BKE_texpaint_slot_material_find_node) could fail
though because it assumed iterating nodes would always happen in the
same order (it was index based). This is not the case though, nodes can
get sorted differently based on selection (see ED_node_sort).

Now check the actual image being referenced in the paint slot for
comparison.

ref T88788 (probably enough to call this a fix, the other issue(s)
mentioned in the report are more likely a feature request)

Reviewed By: mano-wii

Maniphest Tasks: T88788

Differential Revision: https://developer.blender.org/D11496
2021-06-07 16:59:07 +02:00
1f33e20f55 LibOverride: Fix early break in some of the resync code.
This `break` moved out of its braces at some point in the previous
fixes/refctors... :(
2021-06-07 16:59:07 +02:00
946f53d86e GPencil: Change Fill Boundary icon
The icon has been changed to `eye` because is more consistent with other areas.
2021-06-07 16:59:07 +02:00
7ab79ac6ba GPencil: Change Fill extend lines icon
The icon has been changed to `eye` because is more consistent with other areas.
2021-06-07 16:59:07 +02:00
Fynn Grotehans
049924c240 Update Camera presets
The (tracking) camera presets have not been updated in the last 7 or
more years, so they are very outdated. I found it pointless to have a
few specific camera models in the list and instead add the most commonly
used sensor sizes/film sizes. This way the list is shorter, easier to
maintain/becomes later outdated, and is more user friendly for most people
who don't own any of the specific cameras. I added the Crop Factor to the
Beginning of the name, so it gets sortet in the correct order and presets
are easier to find based on the size.

Reviewed By: #render_cycles, #motion_tracking, brecht, sergey

Differential Revision: https://developer.blender.org/D10739
2021-06-07 16:59:07 +02:00
6118cbb78e Greasepencil: show pressure curve widgets in the sidebar
These were only showing in the Properties Editor, but there is no reason
to have the panels be different in the sidebar (they should not show in
the top bar though).

agreed upon by both @anoniov and @mendio

ref T88787
2021-06-07 16:59:07 +02:00
bb2e2ad3ff BlenLoad: Ensure linked IDs are properly sorted.
So far, linked IDs were not properly sorted at all, only the ones
explicitely linked from WM code would be, but any indirectly linked
data-blocks would end up in some random order in their lists.

While not ideal, this is not a huge issue in itself, but it had bad
side-effects, e.g. causing (recursive) resync of overrides to happen in
random order, leading to mismatches between name indices of newly-generated
override IDs and the one existings e.g.

And in general, it is much better to be consistent here.

Note that the file sub-version is bumped for this commit, since some
sorting (the directly linked IDs which we keep a reference to) should
never need to be re-done after relevant doversion process.
2021-06-07 16:59:07 +02:00
467f97fb57 GPencil: Cleanup unneeded variable assign
The variable is assigned below again and the initial value is not used.
2021-06-07 16:59:07 +02:00
236ff239bb Fix T88803: GPencil Thickness modifier produces thicker lines
There was a double apply of the thickness due a bug in the fading new parameter.

Differential Revision: https://developer.blender.org/D11483
2021-06-07 16:59:07 +02:00
5e0068f3f7 Math: Added max_uu/min_uu variations. 2021-06-07 16:59:07 +02:00
Johnny Matthews
dfcb4abd4c Geometry Nodes: Curve Length Node
This commit adds a node that outputs the total length of all
evalauted curve splines in a geometry set as a float value.

Differential Revision: https://developer.blender.org/D11459
2021-06-07 16:59:04 +02:00
60bd05f920 Geometry Nodes: Support curve data in the geometry delete node
This commit implements support for deleting curve data in the geometry
delete node. Spline domain and point domain attributes are supported.

Differential Revision: https://developer.blender.org/D11464
2021-06-07 16:58:32 +02:00
688671b9d7 Cleanup: Add comment explaining assert
This triggers fairly often during development, so it might save some
frustration at some point to have a comment here.
2021-06-07 16:58:32 +02:00
07961e5358 Overlay: Flash on Mode Transfer overlay
This implements T87633

This overlay renders a flash animation on the target object when
transfering the mode to it using the mode transfer operator.
This provides visual feedback when switching between objects without
extra overlays that affect the general color and lighting in the scene.

Differences with the design task:

- This uses just a fade out animation instead of a fade in/out animation.
The code is ready for fade in/out, but as the rest of the overlays
(face sets, masks...) change instantly without animation, having a fade
in/out effect gives the impression that the object flashes twice (once
for the face sets, twice for the peak alpha of the flash animation).

- The rendering uses a flat color without fresnel for now, but this can
be improved in the future to make it look more like the shader in the
prototype.

- Not enabled by default (can be enabled in the overlays panel), maybe
the defaults can change for 3.0 to disable fade inactive and enable this
instead.

Reviewed By: jbakker, JulienKaspar

Differential Revision: https://developer.blender.org/D11055
2021-06-07 16:58:32 +02:00
01f2dad596 VSE: Add refresh_all operator to all sequencer regions
This operator is needed in some cases to update image preview.
In workspaces with smaller timelines this is limiting, because users
need to first check that mouse cursor is in correct place, then press
CTRL+R shortcut.
2021-06-07 16:58:32 +02:00
ffa4c53d10 VSE: Remove JPEG reference from proxy panel
Proxies doesn't use MJPEG codec anymore, but text still referenced it.
2021-06-07 16:58:32 +02:00
88c16c4e7a GHOST/wayland: fix restoring hidden cursor 2021-06-07 16:58:32 +02:00
fed3c58f92 GHOST/wayland: fix non-moving normal cursor 2021-06-07 16:58:32 +02:00
77f3b9ea9f GHOST/wayland: add 'xdg-decoration' support 2021-06-07 16:58:32 +02:00
1dd65919e5 GHOST/wayland: adapt window and cursor surface scale to support HiDPI screens 2021-06-07 16:58:32 +02:00
bdada925e3 GHOST/wayland: handle return values for data sources 2021-06-07 16:58:32 +02:00
89602a8366 GHOST/wayland: set parent relation only for dialogs to mimic X11 behaviour 2021-06-07 16:58:32 +02:00
96a8189d74 GHOST/wayland: handle missing relative-pointer and pointer-constraints support 2021-06-07 16:58:32 +02:00
8f18d076e9 GHOST/wayland: get cursor settings via D-Bus 2021-06-07 16:58:32 +02:00
1077c37f1b GHOST/wayland: inhibit warning 2021-06-07 16:58:32 +02:00
f54fc8ac6e cmake: use absolute path for Wayland libraries 2021-06-07 16:58:32 +02:00
a7e337957d GHOST/wayland: set swap interval to 0
The Wayland server will not update hidden surfaces. This will block the
main event loop and thus also block updates to visible windows in a multi-
window setup.
2021-06-07 16:58:32 +02:00
d6a773345b WM: only use the tablet drag threshold for mouse button events
Keyboard click-drag events now use the "Drag Threshold".
This resolves a problem where keyboard click drag events
used a much smaller threshold when using a tablet.
2021-06-07 16:58:32 +02:00
ff0259de92 LibOverride: fix previous commit (rB826bed4349fa). 2021-06-07 16:58:32 +02:00
6dac1211b1 LibOverride: Fix some fail cases with auto-resync.
In some cases e.g. only objects would actually need resync, so
collections on the override character would not be resynced, and if some
objects were sharing relationships with others those could be
lost/destroyed.
2021-06-07 16:58:32 +02:00
7568501934 Fix missing updates in RNA override create functions. 2021-06-07 16:58:32 +02:00
2200c5f761 LibOverride: Add override_hierarchy_createto ID's RNA API. 2021-06-07 16:58:32 +02:00
f11182b1be Fix T88762: UI using tab to enter next button could clamp the hard min/
max unneccessarily

Since rB298d5eb66916 [which was needed to update buttons with custom
property range functions correctly], using tab would always clamp
(hardmin/hardmax) properties which were using FLT_MAX / INT_MAX as range
in their property definitions.

The clamping of rB298d5eb66916 was copied over from rB9b7f44ceb56c
[where it was used for the softmin/softmax], and while the re-evaluation
of hardmin/hardmax is needed for custom property range functions, the
clamping should actually not take place.

There are many properties using FLT_MAX / INT_MAX etc. and while it
probably would be good to update these with ranges that make more sense
-- not using FLT_MAX / INT_MAX would not have done the clamping here --
there should not be an arbitrary limit to these and they should stay as
they are.

Maniphest Tasks: T88762

Differential Revision: https://developer.blender.org/D11473
2021-06-07 16:58:32 +02:00
a069bd219c Cleanup: make format 2021-06-07 16:58:32 +02:00
3d1a0f97f7 LibOverride: ensure proper indirect tag for 'virtual' linked IDs.
Ensure 'virtual' linked override IDs generated by the recursive resync
process are tagged as indirectly linked data.

This is needed to avoid the 'missing data' messages on those virtual
data-blocks after saving and reloading.
2021-06-07 16:58:32 +02:00
2d37e469a2 IDManagement: Collection: Fix several issues in relationships building code.
`BKE_main_collections_parent_relations_rebuild`,
`BKE_collection_parent_relations_rebuild` anf their internal
dependencies had two issues fixed by this commit:

* Main one was that a same collection could be processed several times,
  sometimes even in an infinite loop (in some rare corner cases), by
  `collection_parents_rebuild_recursive`.
* More exotic, code here would not ensure that the collections it was
  processing were actually in Main (or a master one from a scene in
  Main), which became an issue with some advanced ID management
  processes involving partially out-of-main remapping, like liboverride
  resync.
2021-06-07 16:58:32 +02:00
20b0de7db1 Cleanup: use ascii characters instead of unicode where possible
Follow own code style docs.
2021-06-07 16:58:32 +02:00
1b5288b87f Cleanup: spelling in comments, correct outdated comments 2021-06-07 16:58:32 +02:00
e83d96753c Cleanup: use doxy sections for node_relationships.cc 2021-06-07 16:58:32 +02:00
de5e3ef8e5 Fix invalid return values from file_execute
Error in 6c8c30d865
2021-06-07 16:58:32 +02:00
91544db137 Draw Mesh Extractor: Fix used thread count
Some threads were always idle because of this.
2021-06-07 16:58:32 +02:00
2cc7f32f18 Cleanup: Remove unused 'ExtractTaskData's members 2021-06-07 16:58:32 +02:00
9e16740ca1 Cleanup: Fix build warnings 2021-06-07 16:58:32 +02:00
fc94932596 VSE: Add strip-time intersection test function
Use SEQ_time_strip_intersects_frame function to test if strip intersects with frame.

Note: There are cases where this function should not be used. For example splitting
strips require at least 1 frame "inside" strip. Another example is drawing, where
playhead technically doesn't intersect strip, but it is rendered, because current
frame has "duration" or "thickness" of 1 frame.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11320
2021-06-07 16:58:32 +02:00
97e363a06a Cleanup: Strip duplication code
Remove unused flag `SEQ_DUPE_ANIM` and code used by this flag.
Remove flag `SEQ_DUPE_CONTEXT` and refactor code, to split operator
logic from duplication code.
Reduce indentation level in for loop.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11318
2021-06-07 16:58:32 +02:00
7d47eea06d Fix T57397: Movies are blurred after sws_scale
Images with 4:2:2 and 4:4:4 chroma subsampling were blurred when
`SWS_FAST_BILINEAR` interpolation is set for `anim->img_convert_ctx`.

Use `SWS_BILINEAR` interpolation for all movies, as performance is
not impacted by this change.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11457
2021-06-07 16:58:32 +02:00
5dbe82f129 FFmpeg: Update proxy settings
Changes in rBce649c73446e, affected established proxy codec preset.
Presets were not working and all presets were similar to `veryfast`.
Tunes are now working too, so `fastdecode` tune can be used. I have
measured little improvement, but I tested this only on 2 machines and
I have been informed that `fastdecode` tune does influence decoding
performance for some users.

Change preset from `slow` to `veryfast` and add tune `fastdecode`

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11454
2021-06-07 16:58:32 +02:00
9bd5dff790 FFmpeg: Fix H264 lossless render not lossless
While encoder parameters for lossless encoding are set correctly,
output is not lossless due to pixel format being set to
`AV_PIX_FMT_YUV420P` which is inherently lossy due to chroma subsampling.

This was reported in T61569 and was merged to T57397, but there were
2 bugs - one for encoding and one for decoding.

Set pixel format to `AV_PIX_FMT_YUV444P` when rendering lossless H264
files. This format isn't available in `codec->pix_fmts[0]` and it looks,
that it has to be hard-coded.

Reviewed By: sergey

Differential Revision: D11458
2021-06-07 16:58:32 +02:00
c3427196c2 Boolean exact: speedup by parallelizing a plane calculation.
This patch is from erik85, who says:
This patch makes populate_plane inside polymesh_from_trimesh_with_dissolve run in parallel.
On a test file with a boolean between two subdivided cubes (~6 million verts) this gives a 10% speed increase (49.5s to 45s) on my 6 core CPU.

Also there is an optimization of other_tri_if_manifold to skip the contains-call and get the pointer directly.
This reduces CPU time for find_patches from 5s to 2.2s on the same test file.
2021-06-07 16:58:32 +02:00
21c61092f0 Boolean exact: speedup when there are many components.
When there are many components (separate pieces of connected mesh),
a part of the algorithm to determine component containment was slow.
Using a float version of finding the nearest point on a triangle
as a prefilter sped this up enormously. A case of 25 icospheres
subdivided twice goes 11 seconds faster on my Macbook pro with this
change.
2021-06-07 16:58:32 +02:00
59ffc4945a Nodes: move some files to C++
This just moves a couple of files in `space_node` to C++ and fixes
related errors.

The goal is to be able to use C++ data structures to simplify the code.

Differential Revision: https://developer.blender.org/D11451
2021-06-07 16:58:32 +02:00
07b7158c29 Cleanup: unused variable 2021-06-07 16:58:32 +02:00
1b29d18790 Fix swapped x/y in event simulation script
Incorrect area center calculation, also correct comments.
2021-06-07 16:58:31 +02:00
f68eac3945 Fix T88732: Curve to mesh node crash with empty input curve
The mesh to curve node generated an empty curve because no edges were
selected. This commit changes that node to not add a curve in that case.

This also changes the curve to mesh node to not add a material when
no mesh was created. Even though we don't expect null curves or meshes
in this case, the change is harmless.
2021-06-07 16:58:31 +02:00
e99d523901 EEVEE: AOVs not same as cycles.
EEVEE uses hashing to sync aov names and types with the gpu.
For the type a hashed value was overridden making `decalA`
and `decalB` choose the same hash. This patches fixes this
by removing the most significant bit.
2021-06-07 16:58:31 +02:00
aa46b36a3f Revert "EEVEE: AOVs not same as cycles."
This reverts commit 730a46e87d.
2021-06-07 16:58:31 +02:00
cf84056954 EEVEE: AOVs not same as cycles.
EEVEE uses hashing to sync aov names and types with the gpu. For the type a hashed value was overridden making `decalA` and `decalB` choose the same hash. This patches fixes this by removing the most significant bit.
2021-06-07 16:58:31 +02:00
47d90d9a96 GPencil: New operator to Normalize strokes
Sometimes is required to reset the thickness or the opacity of the strokes. Actually this was done using a modifier, but this operators solves this.

Reviewed By: mendio, filedescriptor

Maniphest Tasks: T87427

Differential Revision: https://developer.blender.org/D11453
2021-06-07 16:58:31 +02:00
bbf78af3c5 Merge branch 'master' into temp-attribute-processor 2021-06-02 10:11:28 -04:00
3da0ad6bb2 add node to access iteration index 2021-05-31 11:41:51 +02:00
002e75fa24 Merge branch 'master' into temp-attribute-processor 2021-05-31 11:13:42 +02:00
427ea6e4ec create attribute node group in attribute tree 2021-05-28 12:16:59 +02:00
c1e587bc46 add initial attribute group 2021-05-28 11:28:05 +02:00
0a082f9ccd Merge branch 'master' into temp-attribute-processor 2021-05-28 10:55:42 +02:00
01321c7825 support other component types 2021-05-27 13:30:13 +02:00
abbd01aff0 support other input modes 2021-05-27 12:39:40 +02:00
40cbdf56fd fix socket recreation 2021-05-27 12:03:34 +02:00
29e5a8b200 cleanup 2021-05-27 11:27:46 +02:00
aa363a71cd improve naming 2021-05-27 11:27:30 +02:00
d42a2cfef4 Merge branch 'master' into temp-attribute-processor 2021-05-27 10:47:05 +02:00
1564743475 Merge branch 'master' into temp-attribute-processor 2021-05-27 10:42:04 +02:00
d932728ac1 initial working version 2021-05-25 18:41:45 +02:00
dcc7786f14 Merge branch 'master' into temp-attribute-processor 2021-05-25 16:58:15 +02:00
27fc7ca66c start creating network 2021-05-25 16:40:37 +02:00
9807cb0eef Merge branch 'master' into temp-attribute-processor 2021-05-25 16:30:02 +02:00
1f25ab4bb9 Merge branch 'master' into temp-attribute-processor 2021-05-25 11:38:01 +02:00
cadfba7aad support changing sockets 2021-05-25 11:13:20 +02:00
be5f911e2a add rna for group inputs 2021-05-25 09:52:57 +02:00
c37de30fdb add initial info for inputs and outputs 2021-05-24 17:53:58 +02:00
95d64038f4 show domain 2021-05-24 17:22:10 +02:00
7f32a3ddd3 start creating correct sockets 2021-05-24 17:13:20 +02:00
fcba670314 create nodes in new group 2021-05-24 17:07:18 +02:00
4fe0ef5eab fix showing menu items of wrong tree type 2021-05-24 16:57:31 +02:00
4a6a755afd support tab on attribute processor 2021-05-24 16:50:32 +02:00
64b4e66afb cleanup 2021-05-24 16:39:15 +02:00
ec77f2d11d support creating attribute processor 2021-05-24 16:36:00 +02:00
e36fe7b78f initial sockets on attribute processor 2021-05-24 16:13:50 +02:00
a795d243cb reference group in attribute processor 2021-05-24 16:07:24 +02:00
4c322f3fba initial attribute processor geometry node 2021-05-24 15:51:26 +02:00
03d31e568f add initial nodes to menu 2021-05-24 15:37:16 +02:00
1291e369f9 initial attribute nodes 2021-05-24 15:29:27 +02:00
39 changed files with 2114 additions and 121 deletions

View File

@@ -367,6 +367,39 @@ class NODE_OT_active_preview_toggle(Operator):
return spreadsheets
class NODE_OT_new_attribute_processor_group(Operator):
bl_idname = "node.new_attribute_processor_group"
bl_label = "New Attribute Processor Group"
bl_options = {'REGISTER', 'UNDO'}
node_name: StringProperty()
@classmethod
def poll(cls, context):
space = context.space_data
if space is None:
return False
if space.type != 'NODE_EDITOR':
return False
return space.edit_tree is not None
def execute(self, context):
node = context.space_data.edit_tree.nodes[self.node_name]
if node.bl_idname != "GeometryNodeAttributeProcessor":
return {'CANCELLED'}
group = bpy.data.node_groups.new("Attribute Group", "AttributeNodeTree")
input_node = group.nodes.new('NodeGroupInput')
output_node = group.nodes.new('NodeGroupOutput')
input_node.location.x = -200 - input_node.width
output_node.location.x = 200
node.node_tree = group
return {'FINISHED'}
classes = (
NodeSetting,
@@ -376,4 +409,5 @@ classes = (
NODE_OT_collapse_hide_unused_toggle,
NODE_OT_tree_path_parent,
NODE_OT_active_preview_toggle,
NODE_OT_new_attribute_processor_group,
)

View File

@@ -30,39 +30,60 @@ from nodeitems_utils import (
class SortedNodeCategory(NodeCategory):
def __init__(self, identifier, name, description="", items=None):
# for builtin nodes the convention is to sort by name
if isinstance(items, list):
items = sorted(items, key=lambda item: item.label.lower())
# Sort node items by name but leave custom node items where they are.
sort_key = lambda value: value.label.lower()
new_items = []
section = []
for item in items:
if isinstance(item, NodeItemCustom):
new_items.extend(sorted(section, key=sort_key))
new_items.append(item)
section.clear()
else:
section.append(item)
new_items.extend(sorted(section, key=sort_key))
items = new_items
super().__init__(identifier, name, description=description, items=items)
def edit_tree_is_type(context, idname):
space = context.space_data
if space.type != 'NODE_EDITOR':
return False
tree = space.edit_tree
if tree is None:
return space.tree_type == idname
return tree.bl_idname == idname
class CompositorNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'CompositorNodeTree')
return edit_tree_is_type(context, 'CompositorNodeTree')
class ShaderNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'ShaderNodeTree')
return edit_tree_is_type(context, 'ShaderNodeTree')
class TextureNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'TextureNodeTree')
return edit_tree_is_type(context, 'TextureNodeTree')
class GeometryNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'GeometryNodeTree')
return edit_tree_is_type(context, 'GeometryNodeTree')
class AttributeNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return edit_tree_is_type(context, 'AttributeNodeTree')
# menu entry for node group tools
@@ -78,6 +99,7 @@ node_tree_group_type = {
'ShaderNodeTree': 'ShaderNodeGroup',
'TextureNodeTree': 'TextureNodeGroup',
'GeometryNodeTree': 'GeometryNodeGroup',
'AttributeNodeTree': 'AttributeNodeGroup',
}
@@ -475,6 +497,8 @@ texture_node_categories = [
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeAttributeProcessor"),
NodeItemCustom(draw=lambda self, layout, context: layout.separator()),
NodeItem("GeometryNodeAttributeRandomize"),
NodeItem("GeometryNodeAttributeMath"),
NodeItem("GeometryNodeAttributeClamp"),
@@ -585,12 +609,57 @@ geometry_node_categories = [
]),
]
attribute_node_categories = [
# Attribute Processor Nodes
AttributeNodeCategory("ATTR_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
AttributeNodeCategory("ATTR_INPUT", "Input", items=[
NodeItem("FunctionNodeRandomFloat"),
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputString"),
NodeItem("FunctionNodeInputVector"),
NodeItem("AttributeNodeIndex"),
NodeItem("AttributeNodeAttributeInput"),
NodeItem("AttributeNodePositionInput"),
]),
AttributeNodeCategory("ATTR_OUTPUT", "Output", items=[
NodeItem("AttributeNodeSetAttribute"),
NodeItem("AttributeNodePositionOutput"),
]),
AttributeNodeCategory("ATTR_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexNoise"),
NodeItem("ShaderNodeTexVoronoi"),
]),
AttributeNodeCategory("ATTR_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
]),
AttributeNodeCategory("ATTR_VECTOR", "Vector", items=[
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
]),
AttributeNodeCategory("ATTR_GROUP", "Group", items=node_group_items),
AttributeNodeCategory("ATTR_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
]),
]
def register():
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
nodeitems_utils.register_node_categories('GEOMETRY', geometry_node_categories)
nodeitems_utils.register_node_categories('ATTRIBUTE', attribute_node_categories)
def unregister():
@@ -598,6 +667,7 @@ def unregister():
nodeitems_utils.unregister_node_categories('COMPOSITING')
nodeitems_utils.unregister_node_categories('TEXTURE')
nodeitems_utils.unregister_node_categories('GEOMETRY')
nodeitems_utils.unregister_node_categories('ATTRIBUTE')
if __name__ == "__main__":

View File

@@ -1439,6 +1439,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_SEPARATE_COMPONENTS 1059
#define GEO_NODE_CURVE_SUBDIVIDE 1060
#define GEO_NODE_RAYCAST 1061
#define GEO_NODE_ATTRIBUTE_PROCESSOR 1062
/** \} */
@@ -1454,6 +1455,18 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Nodes
* \{ */
#define ATTR_NODE_INDEX 1400
#define ATTR_NODE_SET_ATTRIBUTE 1401
#define ATTR_NODE_POSITION_INPUT 1402
#define ATTR_NODE_POSITION_OUTPUT 1403
#define ATTR_NODE_ATTRIBUTE_INPUT 1404
/** \} */
void BKE_node_system_init(void);
void BKE_node_system_exit(void);

View File

@@ -480,6 +480,7 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
}
write_node_socket_default_value(writer, sock);
BLO_write_string(writer, sock->default_attribute_name);
}
/* this is only direct data, tree itself should have been written */
@@ -579,6 +580,21 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
}
else if (node->type == GEO_NODE_ATTRIBUTE_PROCESSOR) {
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
BLO_write_struct(writer, NodeGeometryAttributeProcessor, storage);
BLO_write_struct_list(writer, AttributeProcessorInputSettings, &storage->inputs_settings);
BLO_write_struct_list(
writer, AttributeProcessorOutputSettings, &storage->outputs_settings);
LISTBASE_FOREACH (
AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
BLO_write_string(writer, input_settings->identifier);
}
LISTBASE_FOREACH (
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
BLO_write_string(writer, output_settings->identifier);
}
}
else if (node->typeinfo != &NodeTypeUndefined) {
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
}
@@ -638,6 +654,7 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
sock->typeinfo = nullptr;
BLO_read_data_address(reader, &sock->storage);
BLO_read_data_address(reader, &sock->default_value);
BLO_read_data_address(reader, &sock->default_attribute_name);
sock->total_inputs = 0; /* Clear runtime data set before drawing. */
sock->cache = nullptr;
}
@@ -763,6 +780,21 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
default:
break;
}
/* TODO: Handling this separately now so that it doesn't break when node->type changes when
* merging master. */
if (STREQ(node->idname, "GeometryNodeAttributeProcessor")) {
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
BLO_read_list(reader, &storage->inputs_settings);
BLO_read_list(reader, &storage->outputs_settings);
LISTBASE_FOREACH (
AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
BLO_read_data_address(reader, &input_settings->identifier);
}
LISTBASE_FOREACH (
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
BLO_read_data_address(reader, &output_settings->identifier);
}
}
}
}
BLO_read_list(reader, &ntree->links);
@@ -2124,6 +2156,9 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
socket_id_user_increment(sock_dst);
}
}
if (sock_src->default_attribute_name) {
sock_dst->default_attribute_name = (char *)MEM_dupallocN(sock_src->default_attribute_name);
}
sock_dst->stack_index = 0;
/* XXX some compositor node (e.g. image, render layers) still store
@@ -3076,6 +3111,9 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
}
MEM_freeN(sock->default_value);
}
if (sock->default_attribute_name) {
MEM_freeN(sock->default_attribute_name);
}
}
static void free_localized_node_groups(bNodeTree *ntree)
@@ -3610,7 +3648,8 @@ bool ntreeHasTree(const bNodeTree *ntree, const bNodeTree *lookup)
return true;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) {
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP, GEO_NODE_ATTRIBUTE_PROCESSOR) &&
node->id) {
if (ntreeHasTree((bNodeTree *)node->id, lookup)) {
return true;
}
@@ -5042,6 +5081,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
register_node_type_geo_attribute_processor();
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
@@ -5093,6 +5133,14 @@ static void registerGeometryNodes()
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();
register_node_type_attr_group();
register_node_type_attr_attribute_input();
register_node_type_attr_index();
register_node_type_attr_set_attribute();
register_node_type_attr_position_input();
register_node_type_attr_position_output();
}
static void registerFunctionNodes()
@@ -5118,6 +5166,7 @@ void BKE_node_system_init(void)
register_node_tree_type_sh();
register_node_tree_type_tex();
register_node_tree_type_geo();
register_node_tree_type_attr();
register_node_type_frame();
register_node_type_reroute();

View File

@@ -1764,7 +1764,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
else if (id_type == ID_MC) {
build_movieclip((MovieClip *)id);
}
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP, GEO_NODE_ATTRIBUTE_PROCESSOR)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
}

View File

@@ -2490,7 +2490,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL);
add_relation(clip_key, shading_key, "Clip -> Node");
}
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP, GEO_NODE_ATTRIBUTE_PROCESSOR)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
ComponentKey group_shading_key(&group_ntree->id, NodeType::SHADING);

View File

@@ -618,6 +618,44 @@ static int node_tweak_area_reroute(bNode *node, int x, int y)
return (dx * dx + dy * dy <= tweak_radius_sq);
}
static void node_draw_buttons_group_input(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNodeTree *ngroup = (bNodeTree *)ptr->owner_id;
if (ngroup->type != NTREE_ATTRIBUTE) {
return;
}
PointerRNA props;
uiItemFullO(layout,
"node.group_interface_add",
"New",
ICON_ADD,
nullptr,
WM_OP_INVOKE_DEFAULT,
0,
&props);
RNA_enum_set(&props, "in_out", SOCK_IN);
}
static void node_draw_buttons_group_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNodeTree *ngroup = (bNodeTree *)ptr->owner_id;
if (ngroup->type != NTREE_ATTRIBUTE) {
return;
}
PointerRNA props;
uiItemFullO(layout,
"node.group_interface_add",
"New",
ICON_ADD,
nullptr,
WM_OP_INVOKE_DEFAULT,
0,
&props);
RNA_enum_set(&props, "in_out", SOCK_OUT);
}
static void node_common_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@@ -635,6 +673,12 @@ static void node_common_set_butfunc(bNodeType *ntype)
ntype->draw_nodetype_prepare = node_draw_reroute_prepare;
ntype->tweak_area_func = node_tweak_area_reroute;
break;
case NODE_GROUP_INPUT:
ntype->draw_buttons = node_draw_buttons_group_input;
break;
case NODE_GROUP_OUTPUT:
ntype->draw_buttons = node_draw_buttons_group_output;
break;
}
}
@@ -724,7 +768,10 @@ static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C
static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "attribute_type", DEFAULT_FLAGS, IFACE_("Type"), ICON_NONE);
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
if (ntree->type == NTREE_SHADER) {
uiItemR(layout, ptr, "attribute_type", DEFAULT_FLAGS, IFACE_("Type"), ICON_NONE);
}
uiItemR(layout, ptr, "attribute_name", DEFAULT_FLAGS, IFACE_("Name"), ICON_NONE);
}
@@ -3419,6 +3466,7 @@ void ED_node_init_butfuncs(void)
ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL;
ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE;
ntreeType_Geometry->ui_icon = ICON_NODETREE;
ntreeType_Attribute->ui_icon = ICON_NODETREE;
}
void ED_init_custom_node_type(bNodeType *ntype)

View File

@@ -165,6 +165,10 @@ static void draw_socket_list(const bContext *C,
if (socket->typeinfo->interface_draw) {
socket->typeinfo->interface_draw((bContext *)C, layout, &socket_ptr);
}
if (ntree->type == NTREE_ATTRIBUTE) {
uiItemR(layout, &socket_ptr, "default_attribute_name", 0, "Attribute", ICON_NONE);
}
}
}

View File

@@ -50,6 +50,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -76,7 +77,8 @@ static bool node_group_operator_active_poll(bContext *C)
"ShaderNodeTree",
"CompositorNodeTree",
"TextureNodeTree",
"GeometryNodeTree")) {
"GeometryNodeTree",
"AttributeNodeTree")) {
return true;
}
}
@@ -102,25 +104,33 @@ static bool node_group_operator_editable(bContext *C)
static const char *group_ntree_idname(bContext *C)
{
SpaceNode *snode = CTX_wm_space_node(C);
return snode->tree_idname;
bNodeTree *ntree = snode->edittree;
return ntree ? ntree->idname : "";
}
const char *node_group_idname(bContext *C)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
if (ntree == NULL) {
return "";
}
if (ED_node_is_shader(snode)) {
if (STREQ(ntree->idname, "ShaderNodeTree")) {
return "ShaderNodeGroup";
}
if (ED_node_is_compositor(snode)) {
if (STREQ(ntree->idname, "CompositorNodeTree")) {
return "CompositorNodeGroup";
}
if (ED_node_is_texture(snode)) {
if (STREQ(ntree->idname, "TextureNodeTree")) {
return "TextureNodeGroup";
}
if (ED_node_is_geometry(snode)) {
if (STREQ(ntree->idname, "GeometryNodeTree")) {
return "GeometryNodeGroup";
}
if (STREQ(ntree->idname, "AttributeNodeTree")) {
return "AttributeNodeGroup";
}
return "";
}
@@ -145,18 +155,15 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname)
static int node_group_edit_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
const char *node_idname = node_group_idname(C);
const bool exit = RNA_boolean_get(op->ptr, "exit");
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *gnode = node_group_get_active(C, node_idname);
if (gnode && !exit) {
bNodeTree *ngroup = (bNodeTree *)gnode->id;
bNode *node = nodeGetActive(snode->edittree);
if (!exit && node != NULL && node->id != NULL && GS(node->id->name) == ID_NT) {
bNodeTree *ngroup = (bNodeTree *)node->id;
if (ngroup) {
ED_node_tree_push(snode, ngroup, gnode);
ED_node_tree_push(snode, ngroup, node);
}
}
else {
@@ -1130,3 +1137,110 @@ void NODE_OT_group_insert(wmOperatorType *ot)
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Group Interface Add Operator
* \{ */
static int node_group_interface_add_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
return WM_operator_props_dialog_popup(C, op, 300);
}
static int node_group_interface_add_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
const eNodeSocketDatatype type = (eNodeSocketDatatype)RNA_enum_get(op->ptr, "type");
char *name = RNA_string_get_alloc(op->ptr, "name", nullptr, 0);
const char *socket_idname = nullptr;
NODE_SOCKET_TYPES_BEGIN (st) {
if (st->type == type && st->subtype == PROP_NONE) {
socket_idname = st->idname;
break;
}
}
NODE_SOCKET_TYPES_END;
BLI_assert(socket_idname != nullptr);
bNodeSocket *sock = ntreeAddSocketInterface(ntree, in_out, socket_idname, name);
sock->default_attribute_name = RNA_string_get_alloc(op->ptr, "attribute_name", nullptr, 0);
if (name != nullptr) {
MEM_freeN(name);
}
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
snode_dag_update(C, snode);
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *node_group_interface_add_type_items(bContext *C,
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
EnumPropertyItem *items = nullptr;
int totitem = 0;
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
for (const EnumPropertyItem *item = rna_enum_node_socket_type_items; item->identifier; item++) {
if (ntree->typeinfo->valid_socket_type((eNodeSocketDatatype)item->value, ntree->typeinfo)) {
RNA_enum_item_add(&items, &totitem, item);
}
}
RNA_enum_item_end(&items, &totitem);
*r_free = true;
return items;
}
static void node_group_interface_add_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayoutSetActivateInit(layout, true);
uiItemR(layout, op->ptr, "name", 0, "Name", ICON_NONE);
uiItemR(layout, op->ptr, "attribute_name", 0, "Default Attribute", ICON_NONE);
uiItemR(layout, op->ptr, "type", 0, "Type", ICON_NONE);
}
void NODE_OT_group_interface_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Group Interface Add";
ot->description = "Add interface to node group";
ot->idname = "NODE_OT_group_interface_add";
/* api callbacks */
ot->invoke = node_group_interface_add_invoke;
ot->exec = node_group_interface_add_exec;
ot->poll = node_group_operator_editable;
ot->ui = node_group_interface_add_ui;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", "");
RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name of the new interface socket");
RNA_def_string(ot->srna, "attribute_name", nullptr, MAX_NAME, "Attribute Name", "");
prop = RNA_def_property(ot->srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, node_group_interface_add_type_items);
}
/** \} */

View File

@@ -228,6 +228,7 @@ void NODE_OT_group_insert(struct wmOperatorType *ot);
void NODE_OT_group_ungroup(struct wmOperatorType *ot);
void NODE_OT_group_separate(struct wmOperatorType *ot);
void NODE_OT_group_edit(struct wmOperatorType *ot);
void NODE_OT_group_interface_add(struct wmOperatorType *ot);
/* node_relationships.c */
void sort_multi_input_socket_links(struct SpaceNode *snode,

View File

@@ -76,6 +76,7 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_group_ungroup);
WM_operatortype_append(NODE_OT_group_separate);
WM_operatortype_append(NODE_OT_group_edit);
WM_operatortype_append(NODE_OT_group_interface_add);
WM_operatortype_append(NODE_OT_link_viewer);

View File

@@ -1027,7 +1027,14 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
{
bool free;
const EnumPropertyItem *item_src = RNA_enum_node_tree_types_itemf_impl(C, &free);
RNA_enum_items_add(item, totitem, item_src);
for (const EnumPropertyItem *item_iter = item_src; item_iter->identifier; item_iter++) {
/* Attribute node trees don't have their own space subtype, they can be accessed through
* geometry nodes. */
if (STREQ(item_iter->identifier, "AttributeNodeTree")) {
continue;
}
RNA_enum_item_add(item, totitem, item_iter);
}
if (free) {
MEM_freeN((void *)item_src);
}

View File

@@ -114,6 +114,7 @@ typedef struct bNodeSocket {
/** Default input value used for unlinked sockets. */
void *default_value;
char *default_attribute_name;
/* execution data */
/** Local stack index. */
@@ -525,6 +526,7 @@ typedef struct bNodeTree {
#define NTREE_COMPOSIT 1
#define NTREE_TEXTURE 2
#define NTREE_GEOMETRY 3
#define NTREE_ATTRIBUTE 4
/* ntree->init, flag */
#define NTREE_TYPE_INIT 1
@@ -1388,6 +1390,50 @@ typedef struct NodeGeometryRaycast {
char _pad[1];
} NodeGeometryRaycast;
typedef struct AttributeProcessorInputSettings {
struct AttributeProcessorInputSettings *next, *prev;
char *identifier;
/* GeometryNodeAttributeProcessorInputMode. */
uint8_t input_mode;
char _pad[7];
} AttributeProcessorInputSettings;
typedef struct AttributeProcessorOutputSettings {
struct AttributeProcessorOutputSettings *next, *prev;
char *identifier;
/* GeometryNodeAttributeProcessorOutputMode. */
uint8_t output_mode;
char _pad[7];
} AttributeProcessorOutputSettings;
typedef struct NodeGeometryAttributeProcessor {
/* AttributeDomain. */
int8_t domain;
char _pad[7];
/* List of AttributeProcessorInputSettings. */
ListBase inputs_settings;
/* List of AttributeProcessorOutputSettings. */
ListBase outputs_settings;
} NodeGeometryAttributeProcessor;
typedef struct NodeAttributeSetAttribute {
char attribute_name[64];
/* eNodeSocketDatatype */
uint8_t type;
char _pad[7];
} NodeAttributeSetAttribute;
typedef struct NodeAttributeAttributeInput {
char attribute_name[64];
/* eNodeSocketDatatype */
uint8_t type;
char _pad[7];
} NodeAttributeAttributeInput;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1905,6 +1951,17 @@ typedef enum GeometryNodeRaycastMapMode {
GEO_NODE_RAYCAST_NEAREST = 1,
} GeometryNodeRaycastMapMode;
typedef enum GeometryNodeAttributeProcessorInputMode {
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE = 0,
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE = 1,
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT = 2,
} GeometryNodeAttributeProcessorInputMode;
typedef enum GeometryNodeAttributeProcessorOutputMode {
GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE = 0,
GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT = 1,
} GeometryNodeAttributeProcessorOutputMode;
#ifdef __cplusplus
}
#endif

View File

@@ -71,6 +71,9 @@ extern StructRNA RNA_ArrayGpencilModifier;
extern StructRNA RNA_ArrayModifier;
extern StructRNA RNA_Attribute;
extern StructRNA RNA_AttributeGroup;
extern StructRNA RNA_AttributeNodeTree;
extern StructRNA RNA_AttributeProcessorInputSettings;
extern StructRNA RNA_AttributeProcessorOutputSettings;
extern StructRNA RNA_AssetMetaData;
extern StructRNA RNA_AssetTag;
extern StructRNA RNA_BackgroundImage;

View File

@@ -197,6 +197,7 @@ extern const EnumPropertyItem rna_enum_shading_type_items[];
extern const EnumPropertyItem rna_enum_navigation_mode_items[];
extern const EnumPropertyItem rna_enum_node_socket_in_out_items[];
extern const EnumPropertyItem rna_enum_node_socket_type_items[];
extern const EnumPropertyItem rna_enum_node_math_items[];
extern const EnumPropertyItem rna_enum_mapping_type_items[];

View File

@@ -81,17 +81,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
{SOCK_DISPLAY_SHAPE_SQUARE, "SQUARE", 0, "Square", ""},
{SOCK_DISPLAY_SHAPE_DIAMOND, "DIAMOND", 0, "Diamond", ""},
{SOCK_DISPLAY_SHAPE_CIRCLE_DOT, "CIRCLE_DOT", 0, "Circle with inner dot", ""},
{SOCK_DISPLAY_SHAPE_SQUARE_DOT, "SQUARE_DOT", 0, "Square with inner dot", ""},
{SOCK_DISPLAY_SHAPE_DIAMOND_DOT, "DIAMOND_DOT", 0, "Diamond with inner dot", ""},
{0, NULL, 0, NULL, NULL}};
static const EnumPropertyItem node_socket_type_items[] = {
const EnumPropertyItem rna_enum_node_socket_type_items[] = {
{SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""},
{SOCK_FLOAT, "VALUE", 0, "Value", ""},
{SOCK_INT, "INT", 0, "Integer", ""},
@@ -109,6 +99,16 @@ static const EnumPropertyItem node_socket_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
{SOCK_DISPLAY_SHAPE_SQUARE, "SQUARE", 0, "Square", ""},
{SOCK_DISPLAY_SHAPE_DIAMOND, "DIAMOND", 0, "Diamond", ""},
{SOCK_DISPLAY_SHAPE_CIRCLE_DOT, "CIRCLE_DOT", 0, "Circle with inner dot", ""},
{SOCK_DISPLAY_SHAPE_SQUARE_DOT, "SQUARE_DOT", 0, "Square with inner dot", ""},
{SOCK_DISPLAY_SHAPE_DIAMOND_DOT, "DIAMOND_DOT", 0, "Diamond with inner dot", ""},
{0, NULL, 0, NULL, NULL}};
static const EnumPropertyItem node_quality_items[] = {
{NTREE_QUALITY_HIGH, "HIGH", 0, "High", "High quality"},
{NTREE_QUALITY_MEDIUM, "MEDIUM", 0, "Medium", "Medium quality"},
@@ -896,6 +896,20 @@ static const EnumPropertyItem *rna_node_static_type_itemf(bContext *UNUSED(C),
# undef DefNode
}
if (RNA_struct_is_a(ptr->type, &RNA_AttributeNode)) {
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
if (STREQ(#Category, "AttributeNode")) { \
tmp.value = ID; \
tmp.identifier = EnumName; \
tmp.name = UIName; \
tmp.description = UIDesc; \
tmp.icon = ICON_NONE; \
RNA_enum_item_add(&item, &totitem, &tmp); \
}
# include "../../nodes/NOD_static_types.h"
# undef DefNode
}
if (RNA_struct_is_a(ptr->type, &RNA_FunctionNode)) {
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
if (STREQ(#Category, "FunctionNode")) { \
@@ -2016,6 +2030,20 @@ static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSE
return itemf_function_check(node_socket_data_type_items, switch_type_supported);
}
static bool set_attribute_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
}
static const EnumPropertyItem *rna_AttributeNodeSetAttribute_type_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
*r_free = true;
return itemf_function_check(node_socket_data_type_items, set_attribute_type_supported);
}
static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
@@ -2320,6 +2348,28 @@ static StructRNA *rna_GeometryNode_register(Main *bmain,
return nt->rna_ext.srna;
}
static StructRNA *rna_AttributeNode_register(Main *bmain,
ReportList *reports,
void *data,
const char *identifier,
StructValidateFunc validate,
StructCallbackFunc call,
StructFreeFunc free)
{
bNodeType *nt = rna_Node_register_base(
bmain, reports, &RNA_AttributeNode, data, identifier, validate, call, free);
if (!nt) {
return NULL;
}
nodeRegisterType(nt);
/* update while blender is running */
WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
return nt->rna_ext.srna;
}
static StructRNA *rna_FunctionNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3452,7 +3502,32 @@ static StructRNA *rna_GeometryNodeCustomGroup_register(Main *bmain,
return nt->rna_ext.srna;
}
void register_node_type_geo_custom_group(bNodeType *ntype);
static StructRNA *rna_AttributeNodeCustomGroup_register(Main *bmain,
ReportList *reports,
void *data,
const char *identifier,
StructValidateFunc validate,
StructCallbackFunc call,
StructFreeFunc free)
{
bNodeType *nt = rna_Node_register_base(
bmain, reports, &RNA_AttributeNodeCustomGroup, data, identifier, validate, call, free);
if (!nt) {
return NULL;
}
nt->group_update_func = node_group_update;
nt->type = NODE_CUSTOM_GROUP;
register_node_type_attribute_custom_group(nt);
nodeRegisterType(nt);
WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
return nt->rna_ext.srna;
}
static StructRNA *rna_ShaderNodeCustomGroup_register(Main *bmain,
ReportList *reports,
@@ -4497,6 +4572,29 @@ bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerR
return ma->gp_style == NULL;
}
static bool rna_GeometryNodeAttributeProcessor_node_tree_poll(PointerRNA *UNUSED(ptr),
const PointerRNA value)
{
bNodeTree *ngroup = value.data;
return STREQ(ngroup->idname, "AttributeNodeTree");
}
static void rna_GeometryNodeAttributeProcessor_mode_update(Main *bmain,
Scene *scene,
PointerRNA *ptr)
{
/* Unfortunately, `ptr->data` points to data within the node storage. We can't get the node
* without iterating over all nodes. */
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_ATTRIBUTE_PROCESSOR) {
PointerRNA node_ptr;
RNA_pointer_create(ptr->owner_id, &RNA_Node, node, &node_ptr);
rna_Node_socket_update(bmain, scene, &node_ptr);
}
}
}
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@@ -6219,7 +6317,7 @@ static void def_sh_script(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
parm = RNA_def_string(func, "name", NULL, 0, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(func, "type", node_socket_type_items, SOCK_FLOAT, "Type", "");
parm = RNA_def_enum(func, "type", rna_enum_node_socket_type_items, SOCK_FLOAT, "Type", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/*parm =*/RNA_def_boolean(func, "is_output", false, "Output", "Whether the socket is an output");
parm = RNA_def_pointer(func, "result", "NodeSocket", "", "");
@@ -9986,6 +10084,142 @@ static void def_geo_raycast(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_attribute_processor_group_input(BlenderRNA *brna)
{
static EnumPropertyItem input_mode_items[] = {
{GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE,
"VALUE",
0,
"Value",
"Pass a value into the group"},
{GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE,
"ATTRIBUTE",
0,
"Attribute",
"Pass a attribute name into the group"},
{GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT,
"DEFAULT",
0,
"Default",
"Use the default attribute name or default value if the attribute does not exist"},
{0, NULL, 0, NULL, NULL},
};
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "AttributeProcessorInputSettings", NULL);
RNA_def_struct_ui_text(srna, "Attribute Processor Input", "");
prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "identifier", "Identifier of the matching socket in the group");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "input_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_ui_text(prop, "Input Mode", "How the group input is provided");
RNA_def_property_enum_items(prop, input_mode_items);
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeProcessor_mode_update");
}
static void def_geo_attribute_processor_group_output(BlenderRNA *brna)
{
static EnumPropertyItem input_mode_items[] = {
{GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE,
"ATTRIBUTE",
0,
"Attribute",
"Specify the attribute the result should be stored in"},
{GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT,
"DEFAULT",
0,
"Default",
"Use the default attribute name for the output"},
{0, NULL, 0, NULL, NULL},
};
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "AttributeProcessorOutputSettings", NULL);
RNA_def_struct_ui_text(srna, "Attribute Processor Output", "");
prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "identifier", "Identifier of the matching socket in the group");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "output_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_ui_text(prop, "Output Mode", "Where the group output is stored");
RNA_def_property_enum_items(prop, input_mode_items);
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeProcessor_mode_update");
}
static void def_geo_attribute_processor(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "NodeTree");
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_GeometryNodeAttributeProcessor_node_tree_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeGroup_update");
RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeProcessor", "storage");
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "The geometry domain to process attributes in");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "inputs_settings", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "AttributeProcessorInputSettings");
RNA_def_property_ui_text(prop, "Group Inputs", "");
prop = RNA_def_property(srna, "outputs_settings", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "AttributeProcessorOutputSettings");
RNA_def_property_ui_text(prop, "Group Outputs", "");
}
static void def_attr_set_attribute(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeAttributeSetAttribute", "storage");
prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Attribute Name", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_AttributeNodeSetAttribute_type_itemf");
RNA_def_property_ui_text(prop, "Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_attr_attribute_input(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeAttributeAttributeInput", "storage");
prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Attribute Name", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_AttributeNodeSetAttribute_type_itemf");
RNA_def_property_ui_text(prop, "Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -10035,6 +10269,16 @@ static void rna_def_geometry_node(BlenderRNA *brna)
RNA_def_struct_register_funcs(srna, "rna_GeometryNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_attribute_node(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "AttributeNode", "NodeInternal");
RNA_def_struct_ui_text(srna, "Attribute Node", "");
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_register_funcs(srna, "rna_AttributeNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_function_node(BlenderRNA *brna)
{
StructRNA *srna;
@@ -10146,7 +10390,7 @@ static void rna_def_node_socket(BlenderRNA *brna)
*/
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, node_socket_type_items);
RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items);
RNA_def_property_enum_default(prop, SOCK_FLOAT);
RNA_def_property_enum_funcs(prop, NULL, "rna_NodeSocket_type_set", NULL);
RNA_def_property_ui_text(prop, "Type", "Data type");
@@ -10235,6 +10479,10 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
prop = RNA_def_property(srna, "default_attribute_name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Default Attribute Name", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -10889,7 +11137,7 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
/* for easier type comparison in python */
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "typeinfo->type");
RNA_def_property_enum_items(prop, node_socket_type_items);
RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items);
RNA_def_property_enum_default(prop, SOCK_FLOAT);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Type", "Data type");
@@ -11013,7 +11261,7 @@ static void rna_def_internal_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_funcs(prop, "rna_NodeInternalSocketTemplate_type_get", NULL, NULL);
RNA_def_property_enum_items(prop, node_socket_type_items);
RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items);
RNA_def_property_ui_text(prop, "Type", "Data type of the socket");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -11633,6 +11881,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
{NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"},
{NTREE_ATTRIBUTE, "ATTRIBUTE", ICON_NODETREE, "Attribute", "Attribute Nodes"},
{0, NULL, 0, NULL, NULL},
};
@@ -11773,7 +12022,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
func = RNA_def_function(srna, "valid_socket_type", NULL);
RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree");
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", "");
parm = RNA_def_enum(func, "type", rna_enum_node_socket_type_items, 0, "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", ""));
}
@@ -11881,6 +12130,18 @@ static void rna_def_geometry_nodetree(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_NODETREE);
}
static void rna_def_attribute_nodetree(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "AttributeNodeTree", "NodeTree");
RNA_def_struct_ui_text(srna,
"Attribute Node Tree",
"Node tree consisting of linked nodes used for attribute processing");
RNA_def_struct_sdna(srna, "bNodeTree");
RNA_def_struct_ui_icon(srna, ICON_NODETREE);
}
static StructRNA *define_specific_node(BlenderRNA *brna,
const char *struct_name,
const char *base_name,
@@ -11968,6 +12229,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_compositor_node(brna);
rna_def_texture_node(brna);
rna_def_geometry_node(brna);
rna_def_attribute_node(brna);
rna_def_function_node(brna);
rna_def_nodetree(brna);
@@ -11978,6 +12240,10 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_shader_nodetree(brna);
rna_def_texture_nodetree(brna);
rna_def_geometry_nodetree(brna);
rna_def_attribute_nodetree(brna);
def_geo_attribute_processor_group_input(brna);
def_geo_attribute_processor_group_output(brna);
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
{ \
@@ -12001,6 +12267,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
define_specific_node(brna, "CompositorNodeGroup", "CompositorNode", "Group", "", def_group);
define_specific_node(brna, "TextureNodeGroup", "TextureNode", "Group", "", def_group);
define_specific_node(brna, "GeometryNodeGroup", "GeometryNode", "Group", "", def_group);
define_specific_node(brna, "AttributeNodeGroup", "AttributeNode", "Group", "", def_group);
def_custom_group(brna,
"ShaderNodeCustomGroup",
"ShaderNode",
@@ -12025,6 +12292,12 @@ void RNA_def_nodetree(BlenderRNA *brna)
"Geometry Custom Group",
"Custom Geometry Group Node for Python nodes",
"rna_GeometryNodeCustomGroup_register");
def_custom_group(brna,
"AttributeNodeCustomGroup",
"AttributeNode",
"Attribute Custom Group",
"Custom Attribute Group for Python nodes",
"rna_AttributeNodeCustomGroup_register");
/* special socket types */
rna_def_cmp_output_file_slot_file(brna);

View File

@@ -139,6 +139,12 @@ set(SRC
function/nodes/node_fn_random_float.cc
function/node_function_util.cc
geometry/nodes/node_attr_attribute_input.cc
geometry/nodes/node_attr_group.cc
geometry/nodes/node_attr_index.cc
geometry/nodes/node_attr_position_input.cc
geometry/nodes/node_attr_position_output.cc
geometry/nodes/node_attr_set_attribute.cc
geometry/nodes/node_geo_align_rotation_to_vector.cc
geometry/nodes/node_geo_attribute_clamp.cc
geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -150,6 +156,7 @@ set(SRC
geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_processor.cc
geometry/nodes/node_geo_attribute_proximity.cc
geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_attribute_remove.cc
@@ -163,7 +170,7 @@ set(SRC
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_convex_hull.cc
geometry/nodes/node_geo_curve_length.cc
geometry/nodes/node_geo_curve_length.cc
geometry/nodes/node_geo_curve_to_mesh.cc
geometry/nodes/node_geo_curve_to_points.cc
geometry/nodes/node_geo_curve_resample.cc
@@ -204,6 +211,7 @@ set(SRC
geometry/nodes/node_geo_volume_to_mesh.cc
geometry/node_geometry_exec.cc
geometry/node_geometry_tree.cc
geometry/node_attribute_tree.cc
geometry/node_geometry_util.cc
shader/nodes/node_shader_add_shader.c
@@ -275,10 +283,10 @@ set(SRC
shader/nodes/node_shader_tex_image.c
shader/nodes/node_shader_tex_magic.c
shader/nodes/node_shader_tex_musgrave.c
shader/nodes/node_shader_tex_noise.c
shader/nodes/node_shader_tex_noise.cc
shader/nodes/node_shader_tex_pointdensity.c
shader/nodes/node_shader_tex_sky.c
shader/nodes/node_shader_tex_voronoi.c
shader/nodes/node_shader_tex_voronoi.cc
shader/nodes/node_shader_tex_wave.c
shader/nodes/node_shader_tex_white_noise.c
shader/nodes/node_shader_uvAlongStroke.c

View File

@@ -178,6 +178,7 @@ class DerivedNodeTree {
bool has_link_cycles() const;
bool has_undefined_nodes_or_sockets() const;
void foreach_node(FunctionRef<void(DNode)> callback) const;
void foreach_node_with_type(StringRef idname, FunctionRef<void(DNode)> callback) const;
std::string to_dot() const;
@@ -373,7 +374,7 @@ inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocket
inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_socket)
{
BLI_assert(base_socket->is_input());
BLI_assert(!base_socket || base_socket->is_input());
}
inline const InputSocketRef *DInputSocket::socket_ref() const
@@ -397,7 +398,7 @@ inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSoc
inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_socket)
{
BLI_assert(base_socket->is_output());
BLI_assert(!base_socket || base_socket->is_output());
}
inline const OutputSocketRef *DOutputSocket::socket_ref() const

View File

@@ -23,6 +23,7 @@ extern "C" {
#include "BKE_node.h"
extern struct bNodeTreeType *ntreeType_Geometry;
extern struct bNodeTreeType *ntreeType_Attribute;
void register_node_tree_type_geo(void);
@@ -40,6 +41,7 @@ void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_processor(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
@@ -92,6 +94,17 @@ void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_volume_to_mesh(void);
void register_node_tree_type_attr(void);
void register_node_type_attr_group(void);
void register_node_type_attribute_custom_group(bNodeType *ntype);
void register_node_type_attr_attribute_input(void);
void register_node_type_attr_index(void);
void register_node_type_attr_set_attribute(void);
void register_node_type_attr_position_input(void);
void register_node_type_attr_position_output(void);
#ifdef __cplusplus
}
#endif

View File

@@ -175,6 +175,14 @@ class GeoNodeExecParams {
return *(const T *)gvalue.get();
}
GPointer get_input(StringRef identifier) const
{
#ifdef DEBUG
this->check_input_access(identifier);
#endif
return provider_->get_input(identifier);
}
/**
* Store the output value for the given socket identifier.
*/

View File

@@ -49,6 +49,7 @@ class MFNetworkTreeMap {
const DerivedNodeTree &tree_;
fn::MFNetwork &network_;
MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_;
Map<const fn::MFSocket *, DSocket> dsocket_by_sockets_;
public:
MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
@@ -76,11 +77,13 @@ class MFNetworkTreeMap {
BLI_assert(dsocket->is_input() == socket.is_input());
BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty());
sockets_by_dsocket_.add(dsocket, &socket);
dsocket_by_sockets_.add_new(&socket, dsocket);
}
void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
{
sockets_by_dsocket_.add(dsocket, &socket);
dsocket_by_sockets_.add_new(&socket, dsocket);
}
void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
@@ -88,6 +91,7 @@ class MFNetworkTreeMap {
/* There can be at most one matching output socket. */
BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty());
sockets_by_dsocket_.add(dsocket, &socket);
dsocket_by_sockets_.add_new(&socket, dsocket);
}
void add(const DTreeContext &context,
@@ -180,6 +184,11 @@ class MFNetworkTreeMap {
return socket;
}
DOutputSocket try_lookup(const fn::MFOutputSocket &socket)
{
return DOutputSocket(dsocket_by_sockets_.lookup_default(&socket, {}));
}
bool is_mapped(const DSocket &dsocket) const
{
return !sockets_by_dsocket_.lookup(dsocket).is_empty();

View File

@@ -278,6 +278,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUT
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROCESSOR, def_geo_attribute_processor, "ATTRIBUTE_PROCESSOR", AttributeProcessor, "Attribute Processor", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
@@ -330,6 +331,12 @@ DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform"
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
DefNode(AttributeNode, ATTR_NODE_INDEX, 0, "INDEX", Index, "Index", "")
DefNode(AttributeNode, ATTR_NODE_SET_ATTRIBUTE, def_attr_set_attribute, "SET_ATTRIBUTE", SetAttribute, "Set Attribute", "")
DefNode(AttributeNode, ATTR_NODE_POSITION_INPUT, 0, "POSITION_INPUT", PositionInput, "Position Input", "")
DefNode(AttributeNode, ATTR_NODE_POSITION_OUTPUT, 0, "POSITION_OUTPUT", PositionOutput, "Position Output", "")
DefNode(AttributeNode, ATTR_NODE_ATTRIBUTE_INPUT, def_attr_attribute_input, "ATTRIBUTE_INPUT", AttributeInput, "Attribute Input", "")
/* undefine macros */
#undef DefNode

View File

@@ -22,8 +22,8 @@ static bool fn_node_poll_default(bNodeType *UNUSED(ntype),
const char **r_disabled_hint)
{
/* Function nodes are only supported in simulation node trees so far. */
if (!STREQ(ntree->idname, "GeometryNodeTree")) {
*r_disabled_hint = "Not a geometry node tree";
if (!STREQ(ntree->idname, "GeometryNodeTree") && !STREQ(ntree->idname, "AttributeNodeTree")) {
*r_disabled_hint = "Not a geometry or attribute node tree";
return false;
}
return true;

View File

@@ -0,0 +1,61 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstring>
#include "BKE_node.h"
#include "MEM_guardedalloc.h"
#include "BLT_translation.h"
#include "RNA_access.h"
#include "NOD_geometry.h"
#include "node_common.h"
bNodeTreeType *ntreeType_Attribute;
static void attribute_node_tree_update(bNodeTree *ntree)
{
ntreeSetOutput(ntree);
/* Needed to give correct types to reroutes. */
ntree_update_reroute_nodes(ntree);
}
static bool attribute_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
bNodeTreeType *UNUSED(ntreetype))
{
return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT);
}
void register_node_tree_type_attr()
{
bNodeTreeType *tt = ntreeType_Attribute = static_cast<bNodeTreeType *>(
MEM_callocN(sizeof(bNodeTreeType), "attribute node tree type"));
tt->type = NTREE_ATTRIBUTE;
strcpy(tt->idname, "AttributeNodeTree");
strcpy(tt->ui_name, N_("Attribute Node Editor"));
tt->ui_icon = 0; /* defined in drawnode.c */
strcpy(tt->ui_description, N_("Attribute nodes"));
tt->rna_ext.srna = &RNA_AttributeNodeTree;
tt->update = attribute_node_tree_update;
tt->valid_socket_type = attribute_node_tree_socket_type_valid;
ntreeTypeAdd(tt);
}

View File

@@ -68,6 +68,17 @@ bool geo_node_poll_default(bNodeType *UNUSED(ntype),
return true;
}
bool attr_node_poll_default(bNodeType *UNUSED(ntype),
bNodeTree *ntree,
const char **r_disabled_hint)
{
if (!STREQ(ntree->idname, "AttributeNodeTree")) {
*r_disabled_hint = "Not a attribute node tree";
return false;
}
return true;
}
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
@@ -75,3 +86,12 @@ void geo_node_type_base(bNodeType *ntype, int type, const char *name, short ncla
ntype->update_internal_links = node_update_internal_links_default;
ntype->insert_link = node_insert_link_default;
}
void attr_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
ntype->poll = attr_node_poll_default;
ntype->update_internal_links = node_update_internal_links_default;
ntype->insert_link = node_insert_link_default;
}

View File

@@ -40,6 +40,12 @@ bool geo_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
const char **r_disabled_hint);
void attr_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
bool attr_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
const char **r_disabled_hint);
namespace blender::nodes {
void update_attribute_input_socket_availabilities(bNode &node,
const StringRef name,

View File

@@ -0,0 +1,69 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate attr_node_attribute_input_out[] = {
{SOCK_FLOAT, N_("Value")},
{SOCK_INT, N_("Value")},
{SOCK_BOOLEAN, N_("Value")},
{SOCK_VECTOR, N_("Value")},
{SOCK_RGBA, N_("Value")},
{-1, ""},
};
static void attr_node_attribute_input_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "type", 0, "Type", ICON_NONE);
uiItemR(layout, ptr, "attribute_name", 0, "Name", ICON_NONE);
}
static void attr_node_attribute_input_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeSetAttribute *data = (NodeAttributeSetAttribute *)MEM_callocN(
sizeof(NodeAttributeSetAttribute), __func__);
data->type = SOCK_FLOAT;
node->storage = data;
}
static void attr_node_attribute_input_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeAttributeSetAttribute *node_storage = (NodeAttributeSetAttribute *)node->storage;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
nodeSetSocketAvailability(socket, socket->type == (eNodeSocketDatatype)node_storage->type);
}
}
void register_node_type_attr_attribute_input()
{
static bNodeType ntype;
attr_node_type_base(&ntype, ATTR_NODE_ATTRIBUTE_INPUT, "Attribute Input", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, attr_node_attribute_input_out);
node_type_storage(&ntype,
"NodeAttributeAttributeInput",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, attr_node_attribute_input_init);
node_type_update(&ntype, attr_node_attribute_input_update);
ntype.draw_buttons = attr_node_attribute_input_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,59 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_node.h"
#include "NOD_geometry.h"
#include "NOD_common.h"
#include "node_common.h"
#include "node_geometry_util.hh"
void register_node_type_attr_group(void)
{
static bNodeType ntype;
node_type_base_custom(&ntype, "AttributeNodeGroup", "Group", NODE_CLASS_GROUP, 0);
ntype.type = NODE_GROUP;
ntype.poll = attr_node_poll_default;
ntype.poll_instance = node_group_poll_instance;
ntype.insert_link = node_insert_link_default;
ntype.update_internal_links = node_update_internal_links_default;
ntype.rna_ext.srna = RNA_struct_find("AttributeNodeGroup");
BLI_assert(ntype.rna_ext.srna != nullptr);
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
node_type_socket_templates(&ntype, nullptr, nullptr);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
node_type_group_update(&ntype, node_group_update);
nodeRegisterType(&ntype);
}
void register_node_type_attribute_custom_group(bNodeType *ntype)
{
/* These methods can be overridden but need a default implementation otherwise. */
if (ntype->poll == nullptr) {
ntype->poll = attr_node_poll_default;
}
if (ntype->insert_link == nullptr) {
ntype->insert_link = node_insert_link_default;
}
if (ntype->update_internal_links == nullptr) {
ntype->update_internal_links = node_update_internal_links_default;
}
}

View File

@@ -0,0 +1,34 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate attr_node_index_out[] = {
{SOCK_INT, N_("Index")},
{-1, ""},
};
void register_node_type_attr_index()
{
static bNodeType ntype;
attr_node_type_base(&ntype, ATTR_NODE_INDEX, "Index", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, attr_node_index_out);
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,34 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate attr_node_position_input_out[] = {
{SOCK_VECTOR, N_("Position")},
{-1, ""},
};
void register_node_type_attr_position_input()
{
static bNodeType ntype;
attr_node_type_base(&ntype, ATTR_NODE_POSITION_INPUT, "Position Input", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, attr_node_position_input_out);
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,34 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate attr_node_position_output_in[] = {
{SOCK_VECTOR, N_("Position"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{-1, ""},
};
void register_node_type_attr_position_output()
{
static bNodeType ntype;
attr_node_type_base(&ntype, ATTR_NODE_POSITION_OUTPUT, "Position Output", NODE_CLASS_OUTPUT, 0);
node_type_socket_templates(&ntype, attr_node_position_output_in, nullptr);
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,65 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate attr_node_set_attribute_in[] = {
{SOCK_FLOAT, N_("Value"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -100000, 100000},
{SOCK_BOOLEAN, N_("Value")},
{SOCK_VECTOR, N_("Value"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
{SOCK_RGBA, N_("Value"), 0.8, 0.8, 0.8, 1.0},
{-1, ""},
};
static void attr_node_set_attribute_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "type", 0, "Type", ICON_NONE);
uiItemR(layout, ptr, "attribute_name", 0, "Name", ICON_NONE);
}
static void attr_node_set_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeSetAttribute *data = (NodeAttributeSetAttribute *)MEM_callocN(
sizeof(NodeAttributeSetAttribute), __func__);
data->type = SOCK_FLOAT;
node->storage = data;
}
static void attr_node_set_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeAttributeSetAttribute *node_storage = (NodeAttributeSetAttribute *)node->storage;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
nodeSetSocketAvailability(socket, socket->type == (eNodeSocketDatatype)node_storage->type);
}
}
void register_node_type_attr_set_attribute()
{
static bNodeType ntype;
attr_node_type_base(&ntype, ATTR_NODE_SET_ATTRIBUTE, "Set Attribute", NODE_CLASS_OUTPUT, 0);
node_type_socket_templates(&ntype, attr_node_set_attribute_in, nullptr);
node_type_storage(
&ntype, "NodeAttributeSetAttribute", node_free_standard_storage, node_copy_standard_storage);
node_type_init(&ntype, attr_node_set_attribute_init);
node_type_update(&ntype, attr_node_set_attribute_update);
ntype.draw_buttons = attr_node_set_attribute_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,739 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_kdopbvh.h"
#include "BLI_kdtree.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "DNA_mesh_types.h"
#include "BKE_bvhutils.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_node_tree_multi_function.hh"
#include "NOD_type_callbacks.hh"
#include "FN_multi_function_network_evaluation.hh"
#include "node_geometry_util.hh"
static void geo_node_attribute_processor_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
bNode *node = (bNode *)ptr->data;
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
uiLayout *row = uiLayoutRow(layout, true);
uiTemplateIDBrowse(
row, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr);
uiItemStringO(row, "", ICON_PLUS, "node.new_attribute_processor_group", "node_name", node->name);
uiItemR(layout, ptr, "domain", 0, "Domain", ICON_NONE);
bNodeTree *group = (bNodeTree *)node->id;
if (group == nullptr) {
return;
}
{
uiLayout *col = uiLayoutColumn(layout, false);
bNodeSocket *interface_socket = (bNodeSocket *)group->inputs.first;
AttributeProcessorInputSettings *input_settings = (AttributeProcessorInputSettings *)
storage->inputs_settings.first;
for (; interface_socket && input_settings;
interface_socket = interface_socket->next, input_settings = input_settings->next) {
PointerRNA input_ptr;
RNA_pointer_create(
ptr->owner_id, &RNA_AttributeProcessorInputSettings, input_settings, &input_ptr);
uiItemR(col, &input_ptr, "input_mode", 0, interface_socket->name, ICON_NONE);
}
}
{
uiLayout *col = uiLayoutColumn(layout, false);
bNodeSocket *interface_socket = (bNodeSocket *)group->outputs.first;
AttributeProcessorOutputSettings *output_settings = (AttributeProcessorOutputSettings *)
storage->outputs_settings.first;
for (; interface_socket && output_settings;
interface_socket = interface_socket->next, output_settings = output_settings->next) {
PointerRNA input_ptr;
RNA_pointer_create(
ptr->owner_id, &RNA_AttributeProcessorOutputSettings, output_settings, &input_ptr);
uiItemR(col, &input_ptr, "output_mode", 0, interface_socket->name, ICON_NONE);
}
}
}
static void geo_node_attribute_processor_init(bNodeTree *ntree, bNode *node)
{
NodeGeometryAttributeProcessor *node_storage = (NodeGeometryAttributeProcessor *)MEM_callocN(
sizeof(NodeGeometryAttributeProcessor), __func__);
node->storage = node_storage;
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketGeometry", "Geometry", "Geometry");
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketString", "Selection", "Selection");
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketGeometry", "Geometry", "Geometry");
}
namespace blender::nodes {
static void free_input_settings(AttributeProcessorInputSettings *input_settings)
{
MEM_freeN(input_settings->identifier);
MEM_freeN(input_settings);
}
static void free_output_settings(AttributeProcessorOutputSettings *output_settings)
{
MEM_freeN(output_settings->identifier);
MEM_freeN(output_settings);
}
static void free_inputs_settings(ListBase *inputs_settings)
{
LISTBASE_FOREACH_MUTABLE (AttributeProcessorInputSettings *, input_settings, inputs_settings) {
free_input_settings(input_settings);
}
BLI_listbase_clear(inputs_settings);
}
static void free_outputs_settings(ListBase *outputs_settings)
{
LISTBASE_FOREACH_MUTABLE (
AttributeProcessorOutputSettings *, output_settings, outputs_settings) {
free_output_settings(output_settings);
}
BLI_listbase_clear(outputs_settings);
}
static void geo_node_attribute_processor_storage_free(bNode *node)
{
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
free_inputs_settings(&storage->inputs_settings);
free_outputs_settings(&storage->outputs_settings);
MEM_freeN(storage);
}
static void geo_node_attribute_processor_storage_copy(bNodeTree *UNUSED(dest_ntree),
bNode *dst_node,
const bNode *src_node)
{
const NodeGeometryAttributeProcessor *src_storage = (const NodeGeometryAttributeProcessor *)
src_node->storage;
NodeGeometryAttributeProcessor *dst_storage = (NodeGeometryAttributeProcessor *)MEM_callocN(
sizeof(NodeGeometryAttributeProcessor), __func__);
*dst_storage = *src_storage;
BLI_listbase_clear(&dst_storage->inputs_settings);
LISTBASE_FOREACH (
const AttributeProcessorInputSettings *, src_input_settings, &src_storage->inputs_settings) {
AttributeProcessorInputSettings *dst_input_settings = (AttributeProcessorInputSettings *)
MEM_callocN(sizeof(AttributeProcessorInputSettings), __func__);
*dst_input_settings = *src_input_settings;
dst_input_settings->identifier = BLI_strdup(src_input_settings->identifier);
BLI_addtail(&dst_storage->inputs_settings, dst_input_settings);
}
BLI_listbase_clear(&dst_storage->outputs_settings);
LISTBASE_FOREACH (const AttributeProcessorOutputSettings *,
src_output_settings,
&src_storage->outputs_settings) {
AttributeProcessorOutputSettings *dst_output_settings = (AttributeProcessorOutputSettings *)
MEM_callocN(sizeof(AttributeProcessorOutputSettings), __func__);
*dst_output_settings = *src_output_settings;
dst_output_settings->identifier = BLI_strdup(src_output_settings->identifier);
BLI_addtail(&dst_storage->outputs_settings, dst_output_settings);
}
dst_node->storage = dst_storage;
}
static void geo_node_attribute_processor_group_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
bNodeTree *ngroup = (bNodeTree *)node->id;
if (ngroup && (ID_IS_LINKED(ngroup) && (ngroup->id.tag & LIB_TAG_MISSING))) {
/* Missing datablock, leave sockets unchanged so that when it comes back
* the links remain valid. */
return;
}
ListBase ngroup_inputs;
ListBase ngroup_outputs;
if (ngroup != nullptr) {
ngroup_inputs = ngroup->inputs;
ngroup_outputs = ngroup->outputs;
}
else {
BLI_listbase_clear(&ngroup_inputs);
BLI_listbase_clear(&ngroup_outputs);
}
Map<StringRefNull, bNodeSocket *> old_inputs_by_identifier;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
old_inputs_by_identifier.add_new(socket->identifier, socket);
}
Map<StringRefNull, AttributeProcessorInputSettings *> old_inputs_settings_by_identifier;
LISTBASE_FOREACH (AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
old_inputs_settings_by_identifier.add_new(input_settings->identifier, input_settings);
}
Map<StringRefNull, AttributeProcessorOutputSettings *> old_outputs_settings_by_identifier;
LISTBASE_FOREACH (
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
old_outputs_settings_by_identifier.add_new(output_settings->identifier, output_settings);
}
VectorSet<bNodeSocket *> new_inputs;
VectorSet<AttributeProcessorInputSettings *> new_inputs_settings;
VectorSet<AttributeProcessorOutputSettings *> new_output_settings;
/* Keep geometry and selection socket. */
new_inputs.add_new((bNodeSocket *)node->inputs.first);
new_inputs.add_new(((bNodeSocket *)node->inputs.first)->next);
LISTBASE_FOREACH (bNodeSocket *, interface_sock, &ngroup_inputs) {
AttributeProcessorInputSettings *input_settings =
old_inputs_settings_by_identifier.lookup_default(interface_sock->identifier, nullptr);
char identifier1[MAX_NAME];
char identifier2[MAX_NAME];
BLI_snprintf(identifier1, sizeof(identifier1), "inA%s", interface_sock->identifier);
BLI_snprintf(identifier2, sizeof(identifier2), "inB%s", interface_sock->identifier);
if (input_settings == nullptr) {
input_settings = (AttributeProcessorInputSettings *)MEM_callocN(
sizeof(AttributeProcessorInputSettings), __func__);
input_settings->identifier = BLI_strdup(interface_sock->identifier);
if (!StringRef(interface_sock->default_attribute_name).is_empty()) {
input_settings->input_mode = GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT;
}
BLI_addtail(&storage->inputs_settings, input_settings);
new_inputs_settings.add_new(input_settings);
bNodeSocket *sock_value = nodeAddSocket(
ntree, node, SOCK_IN, interface_sock->idname, identifier1, interface_sock->name);
bNodeSocket *sock_attribute = nodeAddSocket(
ntree, node, SOCK_IN, "NodeSocketString", identifier2, interface_sock->name);
node_socket_copy_default_value(sock_value, interface_sock);
new_inputs.add_new(sock_value);
new_inputs.add_new(sock_attribute);
}
else {
new_inputs_settings.add_new(input_settings);
bNodeSocket *sock_value = old_inputs_by_identifier.lookup(identifier1);
bNodeSocket *sock_attribute = old_inputs_by_identifier.lookup(identifier2);
STRNCPY(sock_value->name, interface_sock->name);
STRNCPY(sock_attribute->name, interface_sock->name);
new_inputs.add_new(sock_value);
new_inputs.add_new(sock_attribute);
}
}
LISTBASE_FOREACH (bNodeSocket *, interface_sock, &ngroup_outputs) {
AttributeProcessorOutputSettings *output_settings =
old_outputs_settings_by_identifier.lookup_default(interface_sock->identifier, nullptr);
char identifier[MAX_NAME];
BLI_snprintf(identifier, sizeof(identifier), "out%s", interface_sock->identifier);
if (output_settings == nullptr) {
output_settings = (AttributeProcessorOutputSettings *)MEM_callocN(
sizeof(AttributeProcessorOutputSettings), __func__);
output_settings->identifier = BLI_strdup(interface_sock->identifier);
if (!StringRef(interface_sock->default_attribute_name).is_empty()) {
output_settings->output_mode = GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT;
}
BLI_addtail(&storage->outputs_settings, output_settings);
new_output_settings.add_new(output_settings);
new_inputs.add_new(nodeAddSocket(
ntree, node, SOCK_IN, "NodeSocketString", identifier, interface_sock->name));
}
else {
new_output_settings.add_new(output_settings);
bNodeSocket *socket = old_inputs_by_identifier.lookup(identifier);
STRNCPY(socket->name, interface_sock->name);
new_inputs.add_new(socket);
}
}
/* Clear the maps to avoid accidental access later on when they point to invalid memory. */
old_inputs_by_identifier.clear();
old_inputs_settings_by_identifier.clear();
old_outputs_settings_by_identifier.clear();
/* Remove unused data. */
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &node->inputs) {
if (!new_inputs.contains(socket)) {
nodeRemoveSocket(ntree, node, socket);
}
}
LISTBASE_FOREACH_MUTABLE (
AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
if (!new_inputs_settings.contains(input_settings)) {
BLI_remlink(&storage->inputs_settings, input_settings);
free_input_settings(input_settings);
}
}
LISTBASE_FOREACH_MUTABLE (
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
if (!new_output_settings.contains(output_settings)) {
BLI_remlink(&storage->outputs_settings, output_settings);
free_output_settings(output_settings);
}
}
/* Sort remaining sockets and settings. */
BLI_listbase_clear(&node->inputs);
for (bNodeSocket *socket : new_inputs) {
BLI_addtail(&node->inputs, socket);
}
BLI_listbase_clear(&storage->inputs_settings);
for (AttributeProcessorInputSettings *input_settings : new_inputs_settings) {
BLI_addtail(&storage->inputs_settings, input_settings);
}
BLI_listbase_clear(&storage->outputs_settings);
for (AttributeProcessorOutputSettings *output_settings : new_output_settings) {
BLI_addtail(&storage->outputs_settings, output_settings);
}
}
static void geo_node_attribute_processor_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
bNodeTree *group = (bNodeTree *)node->id;
if (group == nullptr) {
return;
}
bNodeSocket *next_socket = (bNodeSocket *)node->inputs.first;
/* Skip geometry and selection socket. */
next_socket = next_socket->next->next;
LISTBASE_FOREACH (AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
bNodeSocket *value_socket = next_socket;
bNodeSocket *attribute_socket = value_socket->next;
nodeSetSocketAvailability(
value_socket, input_settings->input_mode == GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE);
nodeSetSocketAvailability(attribute_socket,
input_settings->input_mode ==
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE);
next_socket = attribute_socket->next;
}
LISTBASE_FOREACH (
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
nodeSetSocketAvailability(next_socket,
output_settings->output_mode ==
GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE);
next_socket = next_socket->next;
}
}
static CustomDataType get_custom_data_type(const eNodeSocketDatatype type)
{
switch (type) {
case SOCK_FLOAT:
return CD_PROP_FLOAT;
case SOCK_VECTOR:
return CD_PROP_FLOAT3;
case SOCK_RGBA:
return CD_PROP_COLOR;
case SOCK_BOOLEAN:
return CD_PROP_BOOL;
case SOCK_INT:
return CD_PROP_INT32;
default:
break;
}
BLI_assert_unreachable();
return CD_PROP_FLOAT;
}
namespace {
struct InputsCache {
Map<int, GVArrayPtr> group_inputs;
Map<std::pair<std::string, CustomDataType>, GVArrayPtr> attributes;
GVArrayPtr index;
};
} // namespace
static IndexMask prepare_index_mask_from_selection(Vector<int64_t> &selection_indices,
const AttributeDomain domain,
const int domain_size,
const GeometryComponent &component,
const GeoNodeExecParams &geo_params)
{
IndexMask selection;
const std::string selection_name = geo_params.get_input<std::string>("Selection");
if (selection_name.empty()) {
selection = IndexRange(domain_size);
}
else {
GVArray_Typed<bool> selection_attribute = component.attribute_get_for_read<bool>(
selection_name, domain, false);
for (const int i : IndexRange(domain_size)) {
if (selection_attribute[i]) {
selection_indices.append(i);
}
}
selection = selection_indices.as_span();
}
return selection;
}
static bool load_input_varrays(InputsCache &inputs_cache,
const Span<DOutputSocket> used_group_inputs,
const NodeGeometryAttributeProcessor &storage,
const bNodeTree &group,
const GeoNodeExecParams &geo_params,
const GeometryComponent &component,
const AttributeDomain domain,
const int domain_size,
fn::MFParamsBuilder &fn_params)
{
for (const DOutputSocket &dsocket : used_group_inputs) {
const DNode dnode = dsocket.node();
const GVArray *input_varray = nullptr;
const bNodeSocketType *socket_typeinfo = dsocket->typeinfo();
const CustomDataType data_type = get_custom_data_type(
(eNodeSocketDatatype)socket_typeinfo->type);
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
if (dnode->is_group_input_node()) {
const int index = dsocket->index();
input_varray = &*inputs_cache.group_inputs.lookup_or_add_cb(index, [&]() -> GVArrayPtr {
const AttributeProcessorInputSettings *input_settings = (AttributeProcessorInputSettings *)
BLI_findlink(&storage.inputs_settings, index);
const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&group.inputs, index);
if (cpp_type == nullptr) {
return {};
}
const StringRefNull identifier = interface_socket->identifier;
GVArrayPtr input_varray;
switch ((GeometryNodeAttributeProcessorInputMode)input_settings->input_mode) {
case GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE: {
const std::string input_name = "inB" + identifier;
const std::string attribute_name = geo_params.get_input<std::string>(input_name);
return component.attribute_get_for_read(attribute_name, domain, data_type);
}
case GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE: {
const std::string input_name = "inA" + identifier;
GPointer value = geo_params.get_input(input_name);
GVArrayPtr varray = std::make_unique<fn::GVArray_For_SingleValueRef>(
*value.type(), domain_size, value.get());
return varray;
}
case GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT: {
const StringRef default_attribute_name = interface_socket->default_attribute_name;
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
socket_cpp_value_get(*interface_socket, buffer);
GVArrayPtr varray = component.attribute_get_for_read(
default_attribute_name, domain, data_type, buffer);
cpp_type->destruct(buffer);
return varray;
}
}
BLI_assert_unreachable();
return {};
});
}
else if (dnode->idname() == "AttributeNodeIndex") {
if (!inputs_cache.index) {
auto get_index_func = [](const int64_t index) { return (int)index; };
inputs_cache.index = std::make_unique<
fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(get_index_func)>>>(
domain_size, domain_size, get_index_func);
}
input_varray = &*inputs_cache.index;
}
else if (dnode->idname() == "ShaderNodeAttribute") {
NodeShaderAttribute *storage = dnode->storage<NodeShaderAttribute>();
const StringRefNull attribute_name = storage->name;
input_varray = &*inputs_cache.attributes.lookup_or_add_cb(
{attribute_name, data_type}, [&]() -> GVArrayPtr {
return component.attribute_get_for_read(attribute_name, domain, data_type);
});
}
else if (dnode->idname() == "AttributeNodeAttributeInput") {
NodeAttributeAttributeInput *storage = dnode->storage<NodeAttributeAttributeInput>();
const StringRefNull attribute_name = storage->attribute_name;
input_varray = &*inputs_cache.attributes.lookup_or_add_cb(
{attribute_name, data_type}, [&]() -> GVArrayPtr {
return component.attribute_get_for_read(attribute_name, domain, data_type);
});
}
else if (dnode->idname() == "AttributeNodePositionInput") {
input_varray = &*inputs_cache.attributes.lookup_or_add_cb(
{"position", CD_PROP_FLOAT3}, [&]() -> GVArrayPtr {
return component.attribute_get_for_read("position", domain, CD_PROP_FLOAT3);
});
}
if (input_varray == nullptr) {
return false;
}
fn_params.add_readonly_single_input(*input_varray);
}
return true;
}
static bool prepare_group_outputs(const Span<DInputSocket> used_group_outputs,
const NodeGeometryAttributeProcessor &storage,
bNodeTree &group,
GeoNodeExecParams &geo_params,
GeometryComponent &component,
const AttributeDomain domain,
const int domain_size,
fn::MFParamsBuilder &fn_params,
Vector<std::unique_ptr<OutputAttribute>> &r_output_attributes)
{
for (const DInputSocket &socket : used_group_outputs) {
const DNode node = socket.node();
std::string attribute_name;
CustomDataType attribute_type;
if (node->is_group_output_node()) {
const int index = socket->index();
const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&group.outputs, index);
const AttributeProcessorOutputSettings *output_settings =
(AttributeProcessorOutputSettings *)BLI_findlink(&storage.outputs_settings, index);
attribute_type = get_custom_data_type((eNodeSocketDatatype)interface_socket->typeinfo->type);
switch ((GeometryNodeAttributeProcessorOutputMode)output_settings->output_mode) {
case GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE: {
const StringRefNull identifier = interface_socket->identifier;
const std::string socket_identifier = "out" + identifier;
attribute_name = geo_params.extract_input<std::string>(socket_identifier);
break;
}
case GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT: {
attribute_name = StringRef(interface_socket->default_attribute_name);
break;
}
}
}
else if (node->idname() == "AttributeNodeSetAttribute") {
const NodeAttributeSetAttribute *storage = node->storage<NodeAttributeSetAttribute>();
attribute_name = storage->attribute_name;
attribute_type = get_custom_data_type((eNodeSocketDatatype)storage->type);
}
else if (node->idname() == "AttributeNodePositionOutput") {
attribute_name = "position";
attribute_type = CD_PROP_FLOAT3;
}
if (attribute_name.empty()) {
return false;
}
auto attribute = std::make_unique<OutputAttribute>(
component.attribute_try_get_for_output(attribute_name, domain, attribute_type));
if (!*attribute) {
/* Cannot create the output attribute. */
return false;
}
GMutableSpan attribute_span = attribute->as_span();
/* Destruct because the function expects an uninitialized array. */
attribute_span.type().destruct_n(attribute_span.data(), domain_size);
fn_params.add_uninitialized_single_output(attribute_span);
r_output_attributes.append(std::move(attribute));
}
return true;
}
static void process_attributes_on_component(GeoNodeExecParams &geo_params,
GeometryComponent &component,
const fn::MultiFunction &network_fn,
const Span<DOutputSocket> used_group_inputs,
const Span<DInputSocket> used_group_outputs)
{
const bNode &node = geo_params.node();
bNodeTree *group = (bNodeTree *)node.id;
const NodeGeometryAttributeProcessor &storage = *(NodeGeometryAttributeProcessor *)node.storage;
const AttributeDomain domain = (AttributeDomain)storage.domain;
const int domain_size = component.attribute_domain_size(domain);
if (domain_size == 0) {
return;
}
Vector<int64_t> selection_indices;
const IndexMask selection = prepare_index_mask_from_selection(
selection_indices, domain, domain_size, component, geo_params);
fn::MFParamsBuilder fn_params{network_fn, domain_size};
fn::MFContextBuilder context;
InputsCache inputs_cache;
if (!load_input_varrays(inputs_cache,
used_group_inputs,
storage,
*group,
geo_params,
component,
domain,
domain_size,
fn_params)) {
return;
}
Vector<std::unique_ptr<OutputAttribute>> output_attributes;
if (!prepare_group_outputs(used_group_outputs,
storage,
*group,
geo_params,
component,
domain,
domain_size,
fn_params,
output_attributes)) {
return;
}
network_fn.call(selection, fn_params, context);
for (std::unique_ptr<OutputAttribute> &output_attribute : output_attributes) {
output_attribute->save();
}
}
static void process_attributes(GeoNodeExecParams &geo_params, GeometrySet &geometry_set)
{
const bNode &node = geo_params.node();
bNodeTree *group = (bNodeTree *)node.id;
if (group == nullptr) {
return;
}
geometry_set = geometry_set_realize_instances(geometry_set);
NodeTreeRefMap tree_refs;
DerivedNodeTree tree{*group, tree_refs};
fn::MFNetwork network;
ResourceScope scope;
MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, scope);
Vector<const fn::MFOutputSocket *> fn_input_sockets;
Vector<const fn::MFInputSocket *> fn_output_sockets;
const DTreeContext &root_context = tree.root_context();
const NodeTreeRef &root_tree_ref = root_context.tree();
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
if (output_nodes.size() >= 2) {
return;
}
Vector<DInputSocket> used_group_outputs;
if (output_nodes.size() == 1) {
const DNode output_node{&root_context, output_nodes[0]};
for (const InputSocketRef *socket_ref : output_node->inputs().drop_back(1)) {
used_group_outputs.append({&root_context, socket_ref});
}
}
tree.foreach_node_with_type("AttributeNodeSetAttribute", [&](const DNode dnode) {
NodeAttributeSetAttribute *storage = dnode->storage<NodeAttributeSetAttribute>();
if (storage->attribute_name[0] == '\0') {
return;
}
for (const InputSocketRef *socket_ref : dnode->inputs()) {
if (socket_ref->is_available()) {
used_group_outputs.append({dnode.context(), socket_ref});
}
}
});
tree.foreach_node_with_type("AttributeNodePositionOutput", [&](const DNode dnode) {
used_group_outputs.append(dnode.input(0));
});
if (used_group_outputs.is_empty()) {
return;
}
Vector<fn::MFInputSocket *> network_outputs;
for (const DInputSocket &socket : used_group_outputs) {
network_outputs.append(network_map.lookup(socket).first());
}
VectorSet<const fn::MFOutputSocket *> network_inputs;
VectorSet<const fn::MFInputSocket *> unlinked_inputs;
network.find_dependencies(network_outputs, network_inputs, unlinked_inputs);
BLI_assert(unlinked_inputs.is_empty());
Vector<DOutputSocket> used_group_inputs;
for (const fn::MFOutputSocket *dummy_socket : network_inputs) {
const DOutputSocket dsocket = network_map.try_lookup(*dummy_socket);
BLI_assert(dsocket);
used_group_inputs.append(dsocket);
}
fn::MFNetworkEvaluator network_fn{Vector<const fn::MFOutputSocket *>(network_inputs.as_span()),
Vector<const fn::MFInputSocket *>(network_outputs.as_span())};
if (geometry_set.has_mesh()) {
process_attributes_on_component(geo_params,
geometry_set.get_component_for_write<MeshComponent>(),
network_fn,
used_group_inputs,
used_group_outputs);
}
if (geometry_set.has_pointcloud()) {
process_attributes_on_component(geo_params,
geometry_set.get_component_for_write<PointCloudComponent>(),
network_fn,
used_group_inputs,
used_group_outputs);
}
if (geometry_set.has_curve()) {
process_attributes_on_component(geo_params,
geometry_set.get_component_for_write<CurveComponent>(),
network_fn,
used_group_inputs,
used_group_outputs);
}
}
static void geo_node_attribute_processor_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
process_attributes(params, geometry_set);
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_processor()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_PROCESSOR, "Attribute Processor", NODE_CLASS_GROUP, 0);
node_type_init(&ntype, geo_node_attribute_processor_init);
node_type_storage(&ntype,
"NodeGeometryAttributeProcessor",
blender::nodes::geo_node_attribute_processor_storage_free,
blender::nodes::geo_node_attribute_processor_storage_copy);
node_type_update(&ntype, blender::nodes::geo_node_attribute_processor_update);
node_type_group_update(&ntype, blender::nodes::geo_node_attribute_processor_group_update);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_processor_exec;
ntype.draw_buttons = geo_node_attribute_processor_layout;
nodeRegisterType(&ntype);
}

View File

@@ -100,6 +100,17 @@ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
this->foreach_node_in_context_recursive(*root_context_, callback);
}
void DerivedNodeTree::foreach_node_with_type(StringRef idname,
FunctionRef<void(DNode)> callback) const
{
/* TODO: Use more efficient approach that does not require iterating over all nodes. */
this->foreach_node([&](const DNode node) {
if (node->idname() == idname) {
callback(node);
}
});
}
void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
FunctionRef<void(DNode)> callback) const
{

View File

@@ -160,8 +160,29 @@ static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderD
return node.output(0);
}
static fn::MFOutputSocket &insert_optional_conversion(CommonMFNetworkBuilderData &common,
fn::MFOutputSocket &socket,
const fn::MFDataType &to_type)
{
const fn::MFDataType from_type = socket.data_type();
if (from_type == to_type) {
return socket;
}
const fn::MultiFunction *conversion_fn =
get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(socket, node.input(0));
return node.output(0);
}
else {
return insert_default_value_for_type(common, to_type);
}
}
static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common,
const DInputSocket &dsocket)
const DInputSocket &dsocket,
const fn::MFDataType &to_type)
{
BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo()));
@@ -170,7 +191,7 @@ static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &com
fn::MFOutputSocket *built_socket = builder.built_socket();
BLI_assert(built_socket != nullptr);
return built_socket;
return &insert_optional_conversion(common, *built_socket, to_type);
}
static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
@@ -200,7 +221,7 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
}
if (from_dsockets.is_empty()) {
/* The socket is not linked. Need to use the value of the socket itself. */
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket);
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket, to_type);
for (fn::MFInputSocket *to_socket : to_sockets) {
common.network.add_link(*built_socket, *to_socket);
}
@@ -208,7 +229,7 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
}
if (from_dsockets[0]->is_input()) {
DInputSocket from_dsocket{from_dsockets[0]};
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket);
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket, to_type);
for (fn::MFInputSocket *to_socket : to_sockets) {
common.network.add_link(*built_socket, *to_socket);
}
@@ -216,20 +237,7 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
}
DOutputSocket from_dsocket{from_dsockets[0]};
fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket);
const fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
const fn::MultiFunction *conversion_fn =
get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
from_socket = &node.output(0);
}
else {
from_socket = &insert_default_value_for_type(common, to_type);
}
}
from_socket = &insert_optional_conversion(common, *from_socket, to_type);
for (fn::MFInputSocket *to_socket : to_sockets) {
common.network.add_link(*from_socket, *to_socket);

View File

@@ -40,7 +40,8 @@ static bool sh_fn_poll_default(bNodeType *UNUSED(ntype),
bNodeTree *ntree,
const char **r_disabled_hint)
{
if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) {
if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree") &&
!STREQ(ntree->idname, "AttributeNodeTree")) {
*r_disabled_hint = "Not a shader or geometry node tree";
return false;
}

View File

@@ -86,7 +86,7 @@ void register_node_type_sh_attribute(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0);
sh_fn_node_type_base(&ntype, SH_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, NULL, sh_node_attribute_out);
node_type_init(&ntype, node_shader_init_attribute);
node_type_storage(

View File

@@ -17,6 +17,8 @@
* All rights reserved.
*/
#include "BLI_noise.h"
#include "../node_shader_util.h"
/* **************** NOISE ******************** */
@@ -48,7 +50,7 @@ static bNodeSocketTemplate sh_node_tex_noise_out[] = {
static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
tex->dimensions = 3;
@@ -86,18 +88,80 @@ static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
}
class NoiseTextureFunction : public blender::fn::MultiFunction {
public:
NoiseTextureFunction()
{
static blender::fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
signature.single_input<blender::float3>("Vector");
signature.single_input<float>("Scale");
signature.single_input<float>("Detail");
signature.single_input<float>("Roughness");
signature.single_input<float>("Distortion");
signature.single_output<float>("Fac");
signature.single_output<blender::ColorGeometry4f>("Color");
return signature.build();
}
void call(blender::IndexMask mask,
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<blender::float3> &vectors =
params.readonly_single_input<blender::float3>(0, "Vector");
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
for (int i : mask) {
const blender::float3 vector = vectors[i];
const float scale = scales[i];
const float detail = details[i];
const float noise1 = BLI_noise_generic_turbulence(
scale, vector.x, vector.y, vector.z, detail, false, 1);
const float noise2 = BLI_noise_generic_turbulence(
scale, vector.y, vector.x + 100.0f, vector.z, detail, false, 1);
const float noise3 = BLI_noise_generic_turbulence(
scale, vector.z + 100.0f, vector.y, vector.x, detail, false, 1);
r_values[i] = noise1;
r_colors[i] = {noise1, noise2, noise3, 1.0f};
}
}
};
static void sh_node_tex_noise_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
/* TODO: Not only support 3D. */
NodeTexNoise *tex = builder.dnode()->storage<NodeTexNoise>();
if (tex->dimensions != 3) {
builder.set_not_implemented();
return;
}
static NoiseTextureFunction fn;
builder.set_matching_fn(fn);
}
/* node type definition */
void register_node_type_sh_tex_noise(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
node_type_init(&ntype, node_shader_init_tex_noise);
node_type_storage(
&ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_noise);
node_type_update(&ntype, node_shader_update_tex_noise);
ntype.expand_in_mf_network = sh_node_tex_noise_expand_in_mf_network;
nodeRegisterType(&ntype);
}

View File

@@ -19,6 +19,9 @@
#include "../node_shader_util.h"
#include "BLI_hash.h"
#include "BLI_noise.h"
/* **************** VORONOI ******************** */
static bNodeSocketTemplate sh_node_tex_voronoi_in[] = {
@@ -69,7 +72,7 @@ static bNodeSocketTemplate sh_node_tex_voronoi_out[] = {
static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeTexVoronoi *tex = MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi");
NodeTexVoronoi *tex = (NodeTexVoronoi *)MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi");
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
tex->dimensions = 3;
@@ -79,6 +82,49 @@ static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = tex;
}
static const char *node_shader_gpu_name_tex_voronoi(NodeTexVoronoi *tex)
{
static const char *names[][5] = {
{
"",
"node_tex_voronoi_f1_1d",
"node_tex_voronoi_f1_2d",
"node_tex_voronoi_f1_3d",
"node_tex_voronoi_f1_4d",
},
{
"",
"node_tex_voronoi_f2_1d",
"node_tex_voronoi_f2_2d",
"node_tex_voronoi_f2_3d",
"node_tex_voronoi_f2_4d",
},
{
"",
"node_tex_voronoi_smooth_f1_1d",
"node_tex_voronoi_smooth_f1_2d",
"node_tex_voronoi_smooth_f1_3d",
"node_tex_voronoi_smooth_f1_4d",
},
{
"",
"node_tex_voronoi_distance_to_edge_1d",
"node_tex_voronoi_distance_to_edge_2d",
"node_tex_voronoi_distance_to_edge_3d",
"node_tex_voronoi_distance_to_edge_4d",
},
{
"",
"node_tex_voronoi_n_sphere_radius_1d",
"node_tex_voronoi_n_sphere_radius_2d",
"node_tex_voronoi_n_sphere_radius_3d",
"node_tex_voronoi_n_sphere_radius_4d",
},
};
return names[tex->feature][tex->dimensions];
}
static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
@@ -88,49 +134,6 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
static const char *names[][5] = {
[SHD_VORONOI_F1] =
{
"",
"node_tex_voronoi_f1_1d",
"node_tex_voronoi_f1_2d",
"node_tex_voronoi_f1_3d",
"node_tex_voronoi_f1_4d",
},
[SHD_VORONOI_F2] =
{
"",
"node_tex_voronoi_f2_1d",
"node_tex_voronoi_f2_2d",
"node_tex_voronoi_f2_3d",
"node_tex_voronoi_f2_4d",
},
[SHD_VORONOI_SMOOTH_F1] =
{
"",
"node_tex_voronoi_smooth_f1_1d",
"node_tex_voronoi_smooth_f1_2d",
"node_tex_voronoi_smooth_f1_3d",
"node_tex_voronoi_smooth_f1_4d",
},
[SHD_VORONOI_DISTANCE_TO_EDGE] =
{
"",
"node_tex_voronoi_distance_to_edge_1d",
"node_tex_voronoi_distance_to_edge_2d",
"node_tex_voronoi_distance_to_edge_3d",
"node_tex_voronoi_distance_to_edge_4d",
},
[SHD_VORONOI_N_SPHERE_RADIUS] =
{
"",
"node_tex_voronoi_n_sphere_radius_1d",
"node_tex_voronoi_n_sphere_radius_2d",
"node_tex_voronoi_n_sphere_radius_3d",
"node_tex_voronoi_n_sphere_radius_4d",
},
};
NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
float metric = tex->distance;
@@ -138,7 +141,7 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
BLI_assert(tex->dimensions > 0 && tex->dimensions < 5);
return GPU_stack_link(
mat, node, names[tex->feature][tex->dimensions], in, out, GPU_constant(&metric));
mat, node, node_shader_gpu_name_tex_voronoi(tex), in, out, GPU_constant(&metric));
}
static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
@@ -178,17 +181,81 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node
nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS);
}
class VoronoiTextureFunction : public blender::fn::MultiFunction {
public:
VoronoiTextureFunction()
{
static blender::fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
signature.single_input<blender::float3>("Vector");
signature.single_input<float>("Scale");
signature.single_input<float>("Randomness");
signature.single_output<float>("Distance");
signature.single_output<blender::ColorGeometry4f>("Color");
signature.single_output<blender::float3>("Position");
return signature.build();
}
void call(blender::IndexMask mask,
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<blender::float3> &vectors =
params.readonly_single_input<blender::float3>(0, "Vector");
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
blender::MutableSpan<float> r_distances = params.uninitialized_single_output<float>(
3, "Distance");
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
params.uninitialized_single_output<blender::ColorGeometry4f>(4, "Color");
blender::MutableSpan<blender::float3> r_positions =
params.uninitialized_single_output<blender::float3>(5, "Position");
for (int i : mask) {
const float scale = scales[i];
const blender::float3 vector = vectors[i] * scale;
float da[4];
float pa[12];
BLI_noise_voronoi(vector.x, vector.y, vector.z, da, pa, 1, 0);
blender::ColorGeometry4f color;
BLI_noise_cell_v3(pa[0], pa[1], pa[2], color);
color.a = 1.0f;
r_distances[i] = da[0];
r_colors[i] = color;
r_positions[i] = {pa[0], pa[1], pa[2]};
}
}
};
static void sh_node_tex_voronoi_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
NodeTexVoronoi *tex = builder.dnode()->storage<NodeTexVoronoi>();
if (tex->dimensions != 3 || tex->feature != SHD_VORONOI_F1 ||
tex->distance != SHD_VORONOI_EUCLIDEAN) {
builder.set_not_implemented();
return;
}
static VoronoiTextureFunction fn;
builder.set_matching_fn(fn);
}
void register_node_type_sh_tex_voronoi(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0);
sh_fn_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0);
node_type_socket_templates(&ntype, sh_node_tex_voronoi_in, sh_node_tex_voronoi_out);
node_type_init(&ntype, node_shader_init_tex_voronoi);
node_type_storage(
&ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_voronoi);
node_type_update(&ntype, node_shader_update_tex_voronoi);
ntype.expand_in_mf_network = sh_node_tex_voronoi_expand_in_mf_network;
nodeRegisterType(&ntype);
}