Compare commits

...

127 Commits

Author SHA1 Message Date
d546905f1d replace std::variant with std::any 2021-03-21 12:33:18 +01:00
90959c2372 Cleanup: quiet warning 2021-03-20 15:54:52 +01:00
98721c8543 BLI: improve support for generic algorithms with c++ containers
Some generic algorithms from the standard library like `std::any_of`
did not work with all container and iterator types. To improve the
situation, this patch adds various type members to containers
and iterators.

Custom iterators for Set, Map and IndexRange now have an iterator
category, which soe algorithms require. IndexRange could become
a random access iterator, but adding all the missing methods can
be done when it is necessary.
2021-03-20 15:42:35 +01:00
59d3ec1eef Cleanup: quiet warning 2021-03-20 15:38:17 +01:00
56da5ae2ac LineArt: Fix blank baking for when source type is object. 2021-03-20 14:07:51 +01:00
b19bd3692d Fix T86746: Description missing from Quick Liquid
The description was missing from the Quick Liquid operator.

The fix adds the following description:
"Make selected objects liquid"

Reviewed By: sebbas

Maniphest Tasks: T86746

Differential Revision: https://developer.blender.org/D10777
2021-03-20 13:44:36 +01:00
95a2549d90 Fix T86745: trace sequence keyframe offset
The offset when creating the keyframes was set to `frame_target + i` but
`i` starts iterating from the current frame number.

The fix uses just `i` as the frame number.

Reviewed By: antoniov

Maniphest Tasks: T86745

Differential Revision: https://developer.blender.org/D10772
2021-03-20 13:43:00 +01:00
d0f05bfbcd LineArt: Fixed source selection for nested-instanced collection. 2021-03-20 12:36:35 +01:00
3d5239ff40 Fix T86730: LineArt: keep contour when there's perpendicular faces. 2021-03-20 12:36:27 +01:00
d5a705873e Join curves: compensate for different bevel depths
When joining curves, the resulting curve will inherit the bevel depth of
the active curve, but the radii would stay the same which leads to
changed appearance when joining.

Now compensate for this taking the different bevel depths into account
(if present).

Was a feature request here (and I also think we had reports about this
-- which were usually turned down as not-a-bug):
https://blender.community/c/rightclickselect/bhhbbc/

Differential Revision: https://developer.blender.org/D10752
2021-03-20 08:14:49 +01:00
f65a3172a8 Fix issues introduced strip loading refactoring
- Adding effect strip resulted in strip with no name
 - Adding sound strip attempted to read `fit_method` RNA property,
   that did not exist, causing error messages in console

These issues were introduced in bbb1936411.
2021-03-20 01:53:03 +01:00
3433d1b7f4 Fix T86355: Added effect strip has wrong length
SEQ_add_effect_strip used SeqLoadData.image.end_frame to set end
frame. This was mistake introduced in last refactoring patch.

Use effect data, not image data, when adding effect strips.

Reviewed By: sergey

Differential Revision: D10633
2021-03-20 01:40:53 +01:00
c81dfa2426 Fix T85824: Transition between adjustment regression
When transition effect is placed between 2 adjustment layer strips,
only first adjustment layer was rendered by effect.

Limit timeline_frame range to adjustment strip frame range.

This timeline configuration is technically invalid, because strips
should overlap when using transition effect. This was never restricted
and instead of producing no image, transition effect used first and
last frame of source strip. Many users got used to this "feature" so
I think it make sense to fix this case so it behaves like other strip
types.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D10562
2021-03-20 01:40:53 +01:00
Peter Fog
8b3f5f755e VSE UI: Cleanup scene panel style
- Make properties alligned
 - Add decorate to volume
 - Add heading to Grease Pencil and Transparent

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D10663
2021-03-20 01:40:53 +01:00
Peter Fog
3d9ee83d88 VSE: Preview images when moving strip handles
Add option to override current frame whem transforming strip handles.
Option can be found in View menu of VSE preview, or in timeline when
using backdrop.

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D10424
2021-03-20 01:40:53 +01:00
Peter Fog
1c095203c2 VSE: Text strip improvements
- Position text in center of image
 - Increase default font size so it's more visible
 - Increase font size limit
 - Increase limit for location, so text can be scolled off screen
 - Wrap text by default
 - Tweak default box and shadow color
 - Change text shadow position
 - Text box no longer casts shadow

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D10571
2021-03-20 01:40:53 +01:00
Peter Fog
913b71bb8b VSE: Add bold and italic option for text strip
Bold and italic fonts can be switched quickly by presing corresponding
button.

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D10542
2021-03-20 01:40:53 +01:00
Eitan
7bf977e9f0 Fix T54395: Original image size set incorrectly
`SequenceElement` type `orig_height` and `orig_width` members were
set to incorrect size when using proxies and not set when strip was
added which caused value to be unset.

Since now image dimensions must be read when strip is created,
these members can be initialized. When proxies are used, do not set
original size since it is not guaranteed, that proxies are exact size.

These values are not guaranteed to be up to date or exact. They should
be used for strictly informative purposes.

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D6506
2021-03-20 01:40:53 +01:00
Félix
bfad8deb0b VSE: Add new_meta RNA API function
This function can be used to create empty meta strip, which is not
straightforward when using operators. Very useful for import/export
scripts.

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D10661
2021-03-20 01:40:53 +01:00
Félix
4158405c0b VSE: Add move_to_meta RNA API function
This function can be used to move strips into meta strips with no
side effects like change of selection state.

Reviewed By: ISS

Differential Revision: https://developer.blender.org/D10759
2021-03-20 01:40:53 +01:00
01f028a677 Theme: Use curved noodles in the nodes editor.
Curved noodles increase readability especially in nodes with multiple input sockets.
2021-03-20 01:12:49 +01:00
3494946560 Nodes: implement dot graph exporter for derived node tree
This visualization of nested node groups makes it easier to debug
some issues. Muted nodes, muted links, reroute nodes and unavailable
sockets are removed from the visualization to keep it clean.

Nested node groups are visualized using colored clusters.
2021-03-19 22:37:48 +01:00
087f8a78f8 Nodes: expose tree name and whether a node is a frame in node tree ref 2021-03-19 22:33:43 +01:00
2dd055b2d4 Geometry Nodes: fix crash when inserting certain invalid links
For example, it would crash when connecting a float to a geometry socket,
under some circumstances.
2021-03-19 22:26:55 +01:00
250a69ee82 Fix T86728: Blender freezes when playhead is dragged in this .blend
Porting the deadlock bugfix in WASAPI from upstream Audaspace.
2021-03-19 21:42:55 +01:00
00215692d1 Nodes: make distinction between directly and logically linked sockets more clear
This also moves the handling of muted nodes from derived node tree to
node tree ref.
2021-03-19 21:13:10 +01:00
48731f45c2 Nodes: provide access to type specific data through node tree ref 2021-03-19 21:13:10 +01:00
b96acd0663 EEVEE: Volumetrics: Add back support for light clamp
The new clamping works by modifying the lamp internal radius which
then soften the light contribution.

However this does remove more light compare to the old solution.
This is because the clamp now affects the light over a much larger
distance since it is smoother. Old scene needs manual tweaking.
2021-03-19 21:11:06 +01:00
89ef0da551 EEVEE: Volumetrics: Add support for soft volumetric shadows
Soft surface shadows were already supported but now we support
soft shadows of the volume themselves.

This is only enabled if the light casts shadow and the scene soft
shadows toggle is enabled.
2021-03-19 21:11:06 +01:00
355f884b2f EEVEE: Volumetrics: Add Area light shape support
Previously area lights were just considered as point lights.
We now use a "most representative point" technique that make the
light shape appearant and gives more homogenous result.

This technique is quite cheap but it is not physically correct.

So I came up with a power function to have almost the same intensity
output as cycles in the general case.
2021-03-19 21:11:06 +01:00
0a0f737f91 EEVEE: Volumetrics: Add special attenuation volume for lights
This makes volume lights more efficient if they have lower power.
2021-03-19 21:11:06 +01:00
54f52cac7c EEVEE: Volumetrics: Fix sun volumetric shadow
Sun lights are treated as distant light source and need to gather
shadowing from the full frustum.

This might have performance impact on certain scenes.
2021-03-19 21:11:06 +01:00
884f934a85 EEVEE: Lights: Add Volume and diffuse light power slider
This adds 2 new sliders for light objects that modulates the diffuse
light and the volume light intensities.

This also changes the way volume light is computed using point lamp
representation. We use "Point Light Attenuation Without Singularity"
from Cem Yuksel instead of the usual inverse square law.
2021-03-19 21:11:06 +01:00
fb3e5b7f98 LineArt: Add missing modifer and sanity checks to bake operator
Previously we could crash because we would not check if the modifier in
question actually was a line art modifier. We also did not query if the
modifier was disabled.
2021-03-19 21:02:21 +01:00
17a5db7303 Geometry Nodes: Add geometry instances data to the spreadsheet
This patch adds data about instances generated by geometry nodes
to the spreadsheet. The transform data is decomposed into position,
rotation, and scale, and there is a name column to display the name
of the instanced object or collection.

This data is implemented specifically for the spreadsheet, because
we're not sure that we want to expose this data as attributes for the
use elsewhere.

Differential Revision: https://developer.blender.org/D10770
2021-03-19 15:30:41 -04:00
9de8c2fa18 BLI: Add accessors for translation, rotation, and scale of float4x4 2021-03-19 15:28:58 -04:00
dd7feb09c9 Cleanup: rename x1/x2/y1/y2 to x/y/width/height 2021-03-19 20:24:56 +01:00
9b806c49d0 Geometry Nodes: Refactor / fix point separate node
The point separate node was failing in situations where one of the
outputs was empty. In addition, the code was not structured very well.
This new implementation stores less temporary information, is more
geometry component type agnostic, and is more self-descriptive.
It also solves the problems mentioned above.

Fixes T86573

Differential Revision: https://developer.blender.org/D10764
2021-03-19 15:09:17 -04:00
a6ab232f8a LineArt: Added back the missing "allow_clipping_boundaries" option.
This option was missing from the UI.
Also add missing camera settings depsgraph relation.
2021-03-19 18:56:26 +01:00
cff4445a86 LineArt: Modifier defaults settings optimization.
Make options that are usually only useful in edge cases off by default.
These settings would requite extra work and thus the modifier evaluation
would be needlessly slower as most users do not need these options to be
on.
2021-03-19 18:31:03 +01:00
3420c3d8c6 LineArt: Remove geometry space chaining
It caused some chaining errors when used in combination with image space
chaining. After some internal discussion, we realized it is not
useful as chaining in image space essentially does the same thing.
2021-03-19 18:26:33 +01:00
a00249dd22 LibOverride: Stash away 'leftover' objects/collections from auto-resync process.
Instead of storing those in scne's master collection, which is fairly
annoying, we now add them to a (hidden) specific collection. Easy to
ignore, or check and cleanup.
2021-03-19 17:33:08 +01:00
a9e64d8613 Cleanup: Use uint8_t for num of channels. 2021-03-19 17:11:47 +01:00
b9d2e2ec97 Cleanup: Use enum class for MemoryBufferExtend. 2021-03-19 17:11:47 +01:00
9f86933f2e Cleanup: Remove unused states and vars from MemoryBuffer. 2021-03-19 17:11:47 +01:00
31d5c5078c Cleanup: MemoryBuffer do not store width and height. 2021-03-19 17:11:47 +01:00
8cb1089795 Cleanup: Rename copyContentFrom to fill_from. 2021-03-19 17:11:47 +01:00
dc9aea9903 Cleanup: Add copy constructor to MemoryBuffer. 2021-03-19 17:11:47 +01:00
260e50ed82 Cleanup: Use ref to read from encapsuled data.
MemoryBuffer->getRect was returning a ptr.
2021-03-19 17:11:47 +01:00
c905dd24b6 Cleanup: Replace ptr with ref (COM_MemoryBuffer). 2021-03-19 17:11:47 +01:00
b9447ab053 Cleanup: Replace ptr with ref. 2021-03-19 17:11:47 +01:00
e5ffefe606 Cleanup: Use enum class for DataType. 2021-03-19 17:11:47 +01:00
b5f70d92c2 Cleanup: enum class ChunkOrdering. 2021-03-19 17:11:47 +01:00
9c2d4ffbc1 Cleanup: Use enum class for CompositorQuality. 2021-03-19 17:11:47 +01:00
de504e6dec Cleanup: Use enum class for CompositorPriority. 2021-03-19 17:11:47 +01:00
50c5435438 Cleanup: compositor - chunk order
No functional changes.
2021-03-19 17:11:47 +01:00
18b87e2e0b Cleanup: Remove unneeded complexity
`determineDependingMemoryProxies` was mapping a value in a temp vector.
2021-03-19 17:11:47 +01:00
eb7a601e1b Geometry Nodes: Make cone primitive 2m tall by default
This gives the cone mesh primitive more pleasing proportions by default.
2021-03-19 11:22:39 -04:00
dbe45073d2 Geometry Nodes: Move cone primtive to rest on its base by default
This is generally what people expect when generating a cone. Note that
this translation currently happens after the rotation, but since the rotation
will likely be removed in the future, that won't be a problem for long.
2021-03-19 11:21:24 -04:00
97b83b6a67 Geometry Nodes: Implicit interpolations to and from the edge domain
This patch adds the remaining 6 interpolations for mesh domains.
The new interpolations are:
 - Corner / point / polygon to edge
 - Edge to corner / point / polygon

After this it is possible to adapt an attribute to and from every
mesh domain. This is simple to test with the "Attribute Convert" node.

Though, as a note for the future, there are still some improvements
possible to the interpolations, like lazily calculating values for the
interpolations where it's possible, and slightly improving the
algorithms used for some interpolations, like using corner angles
for polygon to point.

Differential Revision: https://developer.blender.org/D10765
2021-03-19 08:59:56 -04:00
a7f4270748 Fix Cycles NaN assert in random walk SSS due to very small throughput
Now terminate if there are many bounces and the throughput gets so small
that we get precision issues.
2021-03-19 13:20:42 +01:00
d235f6a48e LibOverride: fix code trying to auto-resync linked overrides.
This is not only potentially extremely expensive, it is also fairly
futile, and code is not designed to handle it currently anyway (could
easily end up in inifinite loops and other crashes).
2021-03-19 12:24:53 +01:00
Angus Stanton
ae650b016f Fix T86208: copy node group button is inconsistent in geometry nodes
Differential Revision: https://developer.blender.org/D10740
2021-03-19 11:24:27 +01:00
de296e8429 Cleanup: add const. 2021-03-19 08:18:00 +01:00
ef944b5020 Cleanup: Replace std::vector with blender::Vector. 2021-03-19 08:11:11 +01:00
64a413b0c7 Fix T86710: Crash When Adding Node Group.
When adding a node group there can be no inputs in the input map that
was triggering an assert.
2021-03-19 08:04:30 +01:00
a7455ae5f8 Cleanup: remove unused function 2021-03-19 16:23:52 +11:00
74609bfd51 Cleanup: minor changes to pose-mode apply visual transform
- Remove use of evaluated poses, instead calculate transformations
  into an array which is applied afterwards.

- Only update ID's for poses that have been changed.
2021-03-19 15:33:43 +11:00
36deb8a48e BLI: Add location, rotation, scale constructor to float4x4
This is simply a convenience when using this type. More similar
constructors can be added in the future when they are useful.

Differential Revision: https://developer.blender.org/D10714
2021-03-18 17:29:39 -04:00
Patrick Busch
fc62d38ce1 Python API: Expose CurveProfile Reset View function
Allow python access to the `reset_view` functionality which before
was only available through the menu. This was suggested for
consistency after D10561.

Differential Revision: https://developer.blender.org/D10595
2021-03-18 17:25:36 -04:00
Pratik Borhade
7e3efac9a8 Fix T86701: Geometry nodes Cube and UV Sphere mesh size
The size in the transform matrices was extra, since it is also
passed as an argument to the BMesh operators.

Differential Revision: https://developer.blender.org/D10763
2021-03-18 17:00:47 -04:00
894e8b18e4 Geometry Nodes: Don't create empty components when realizing instances
Previously even if the input goemetry set had no point cloud or no mesh
instances, `geometry_set_realize_instances` would create empty data.
This isn't necessarily bad, but it can complicate things down the line if
there are a bunch of empty components getting passed around.
2021-03-18 16:32:49 -04:00
b1150fa1f5 Fix T86448 EEVEE: SSRefraction Depth regression
Caused by recent change for contact shadow raytracing. rB4e236326c137
2021-03-18 18:07:46 +01:00
51c7ff9222 GPencil: Rename Options panel to Settings
This change is to keep consistency with other panels with the same functionality.

Reviewed by @pablovazquez
2021-03-18 17:24:20 +01:00
1e1d96f0a8 Fix T86677: select grouped in node editor crashes without active node
This was reported for geometry nodes, but was true for all nodetrees
(e.g. after deleting the active node). Geometry node trees just made
this more obvious since they start without an active node to begin with.

Fix provided by @lone_noel, thx!

Maniphest Tasks: T86677

Differential Revision: https://developer.blender.org/D10762
2021-03-18 17:15:42 +01:00
cddfd11581 Fix T86548: Sculpt: Mask by Color tool not working
Caused by {rB2917f550caa9} which renamed the entry in the toolsystem,
but not the corresponding keymap.

Maniphest Tasks: T86548

Differential Revision: https://developer.blender.org/D10725
2021-03-18 17:10:50 +01:00
1919b104d3 LibOverride: Fix missing update after an override reset.
We need to brute-force reset IDs with `ID_RECALC_ALL` here...
2021-03-18 15:59:18 +01:00
59f92a218a LibOverride: Outliner: Rename Add... to Make....
Also more consistent with the name used for the 3DView operator.
2021-03-18 15:59:18 +01:00
07f9a73a8f Fix 3DView not updating on some NC_ID notifiers.
Outliner uses a lot `NC_ID | NA_EDITED` e.g., which was not caught by
the View3D editor for update.
2021-03-18 15:59:18 +01:00
06351c0504 LibOverride: Outliner: Tweak override creation from instancing empty.
Now behavior is similar to the one from 3DView: once override of the
collection is successfuly created, we remove the instancing empty from
the scene.
2021-03-18 15:59:18 +01:00
7ee365d8c7 Fix missing view3d updates after recent NC_SPACE notifier filters
Since {rB46aa70cb486d}, using `NC_SPACE | ND_SPACE_VIEW3D` as notifier is
restricted to space data as a reference. This was still used though for
RNA updates in other places (namely `rna_camera`, `rna_scene`,
`rna_animviz`), and passing NULL would automatically set the notifier
reference to the owner id. Above commit would happily filter these out,
leading to missing refreshes.

Now use more specific notifiers (in case of animviz a new
`ND_DRAW_ANIMVIZ` was added).

This was reported for Camera background images btw.
Fixes T86670.

Maniphest Tasks: T86670

Differential Revision: https://developer.blender.org/D10758
2021-03-18 15:23:23 +01:00
2dd040a349 UI: Dynamically increase data-block name button size if there's space
Data-block selectors (or other selectors using the
`UILayout.teamplate_search` or `UILayout.template_search_preview`)
in headers used a fixed size width for the name button. Often that meant
the text would be clipped to fit even though there was plenty of
whitespace surrounding the data-block selector.

Now the name button can increase in width with the contained string,
clamped to some arbitrary and quite big maximum (3 times the minimum
width).
We could further take the available space into account, to reduce the
need for scrolling in the header. But I don't think that is much of an
issue and the name clipping it would re-introduce would probably be more
annoying.
2021-03-18 15:09:31 +01:00
248d9809ca Fix T86594: Overrides: Possible collection "duplication".
Issue was actually in some Collection management code, a bit too eager
to add collection to the scene master one when it was not actually
needed.
2021-03-18 14:26:29 +01:00
f75d690ee4 LineArt: Add missing dna variable rename
Forgot to add this in: "Cleanup, LineArt: Rename LineartLine -> LineartEdge"
df28063795
2021-03-18 14:21:18 +01:00
6f7e632a6f Cleanup, LineArt: Sample -> Resample
Clear up what sample length does by renaming the option and variables.
2021-03-18 13:20:44 +01:00
3f0cd3fcc1 Cleanup: Remove unused variable in gpencil_add_lineart.c 2021-03-18 13:16:00 +01:00
1b05948e4d LineArt: Expose the allow_duplication property for instanced objects.
It was missing from the UI.
2021-03-18 13:13:08 +01:00
d39a1e3cab Fix T86692: Start Line Art GP objects on frame 0
If playback starts before the first GP frame, we will get "ghost"
strokes.
2021-03-18 12:50:55 +01:00
c114c78f57 LineArt: Do not calculate lines for objects not included
Only use non included objects for potential occlusion queries.
Don't calculate lines for them.

This fixes intersection lines appearing on objects not included by the
lineart modifier.
2021-03-18 12:42:41 +01:00
df28063795 Cleanup, LineArt: Rename LineartLine -> LineartEdge
Also cleanup various other "line" variable names
2021-03-18 12:41:44 +01:00
d7fb38ddd4 GPencil: Fix unreported datablock type changed using Dopesheet box select
When doing a box selection in the dopesheet in the header area, the data block type was changed to annotation because the pointer was not usable and produced an unexpected result.

Thanks to @pullup for finding a way to reproduce the bug.
2021-03-18 12:37:00 +01:00
8ab52daa3a BLO: Functions for temporarily loading a single datablock
This introduces two functions to the blenloader module, here shown as
calls for brevity:

* `temp_lib_ctx = BLO_library_temp_load_id(real_main, blend_file_path, idcode, idname, reports);`
* Now the data in `temp_lib_ctx->temp_id` can be used (but ought not to
  be not assigned to non-temp datablocks).
* `BLO_library_temp_free(temp_lib_ctx);`

The first loads a datablock from a blend file, and returns it as part of
a `struct TempLibraryContext`. This struct contains the temp-loaded ID,
as well as enough information to correctly free everything again.

Differential Revision: https://developer.blender.org/D10736
2021-03-18 11:09:15 +01:00
aeff59073b Fix T86219: Compositor backdrop not using Color Management View
Transforms in certain cases

This was caused by wrong flag checking in {rB278011e44d43}, which just
seems to be a copy-paste error.

For example, enabeling 'Auto-Offset' in the View menu would lead to CM
being ignored.

Maniphest Tasks: T86219

Differential Revision: https://developer.blender.org/D10751
2021-03-18 09:16:58 +01:00
decfd5c169 Cleanup: use doxy sections for pose_transform.c 2021-03-18 13:55:32 +11:00
30b5fd1a3c Cleanup: remove MJPEG reference from error message
Proxy coded has been changed to h264. Error code is more generic now.
2021-03-18 00:14:41 +01:00
bb6765f28f Cleanup: spelling 2021-03-18 09:36:44 +11:00
0c58ad8a34 Cleanup: remove unused common_restrict_check function
Since c10ad8e9ea hiding edit-mode objects
is supported.

Although this function had already been removed in
aeb8e81f27.
2021-03-18 09:06:56 +11:00
de6d6e171e Cleanup: Remove commented code
Mistake in earlier commit rBbe33d3eccdcdf252. Also use const and
set layouts disabled when the line art is baked.
2021-03-17 14:54:30 -04:00
a41d3c0ebe Geometry Nodes: Rename "Subdivide Smooth" back to "Subdivision Surface"
Following concerns raised in the commit that changed the name initially,
rB2e19509e60b39837, it makes more sense to keep the "Surface" name for
this node because it has a specific meaning that should not be confused
with other types of subdivision.
2021-03-17 14:40:38 -04:00
1cc427ce2b Geometry Nodes: Use consistent default for cylinder node
- Use 2m height instead of 1m
 - Default to N-Gon fill for the top and bottom
2021-03-17 14:17:52 -04:00
be33d3eccd UI: Tweak labels, descriptions, and panel layout for line art
Changes include:
 - Use `IFACE_` for UI labels set in the modifier panels
 - Use a sub-sub-panel for transparency
 - Fix grammar and spelling mistakes
 - Use more natural user-friendly wording
 - Make descriptions more specific and more useful
 - Don't capitalize "line art" in descriptions (tooltips)

These changes are aimed at making the UI strings more consistent with
the rest of the UI and being more helpful to someone trying to understand
how to use the modifier.

Differential Revision: https://developer.blender.org/D10750
2021-03-17 13:46:41 -04:00
0ff3f96a1b Nodes: make derived link data more obvious in NodeTreeRef
`NodeTreeRef` is a thin wrapper on top of `bNodeTree`. By default it
should not hide anything from the underlying `bNodeTree` (before this
it was hiding muted links).

For convenience some "derived" data is cached on sockets. For example
all the connected sockets when reroutes and muted links are ignored.

A nice side benefit of this refactor is that `NodeTreeRef` requires
less memory than before.
2021-03-17 16:43:54 +01:00
e9eb08fea1 BLI: support equality operator on Span and Vector
This is quite convenient sometimes and is available for `std::vector` as well.
2021-03-17 16:38:49 +01:00
bf620020f1 BLI: improve implicit conversions of spans
Some conversions that should work did not work before.
For example, `MutableSpan<int *> -> MutableSpan<const int *>`.
2021-03-17 15:27:31 +01:00
256da1c4b9 LineArt: Fix transparenct mask in cutting function. 2021-03-17 15:24:26 +01:00
0bae3002cf LineArt: Fix transparency flag error during cutting and chaining. 2021-03-17 15:23:48 +01:00
7f769567d0 LineArt: Remove "Render" in structure names. 2021-03-17 15:23:20 +01:00
0f1482279c LineArt: better explaination in lineart_line_cut() 2021-03-17 15:21:12 +01:00
4de7dc42c9 UI: Correct icon and description for "Object Line Art"
The icon should be the same as other object icons rather than "Cube",
and the description was just the collection description copy and pasted.
2021-03-17 08:51:59 -04:00
9cf25b9c44 Cleanup: Reduce variable scope 2021-03-17 13:01:30 +01:00
296984b40e Modify Dopesheet block between keyframes
This makes more visible where ends each keyframe. To use, as we did before, full block sometimes looks hard to view where a keyframe ends.

Reviewed By: pepeland, mendio, Severin

Differential Revision: https://developer.blender.org/D10588
2021-03-17 12:57:44 +01:00
Charlie Jolly
266cd7bb82 Nodes: Add support to mute node wires
This patch adds the ability to mute individual wires in the node editor.
This is invoked like the cut links operator but with a new shortcut.

Mute = Ctrl + Alt
Cut = Ctrl

Dragging over wires will toggle the mute state for that wire.
The muted wires are drawn in red with a bar across the center.
Red is used in the nodes context to indicate invalid links, muted links and internal links.

When a wire is muted it exposes the original node buttons which are normally hidden when a wire is connected.

Downstream and upstream links connected using reroute nodes are also muted.

Outside scope of patch:
- Add support for pynodes e.g. Animation Nodes
- Requires minor change to check for muted links using the `is_muted` link property or the `is_linked` socket property.

Maniphest Tasks: T52659

Differential Revision: https://developer.blender.org/D2807
2021-03-17 11:54:16 +00:00
20bf736ff8 Fix T86645: Executing "Operator Cheat Sheet" Crashes Blender
The "Operator Cheat Sheet" operator collects info about all operators, and as
part of that executes the callbacks to create dynamic enums. The callback to
enumerate the Outliner ID operations depends on Outliner context. If this isn't
available, it can just return a context-less version of the enum, that is, a
static enum with all available items. This was already done in case no context
is available at all.
2021-03-17 12:51:27 +01:00
Wannes Malfait
2a4bde04c5 Fix T86655: remove doubled transforms
Differential Revision: https://developer.blender.org/D10744
2021-03-17 12:23:17 +01:00
1185708911 Cleanup: clang format 2021-03-17 12:18:35 +01:00
3157c3680b Cleanup: remove unnecessary namespace specifiers 2021-03-17 11:52:02 +01:00
633f1cdec9 Cleanup: improve gathering supported domains by geometry type 2021-03-17 11:52:02 +01:00
e6bdd57191 Fix T86609: GPencil: Sculpt brush cursor disappears on undo.
Regression from rB2a8122fb65c5, somehow that piece of code was lost
during the refactor.
2021-03-17 11:38:31 +01:00
ac60e64745 BLI: provide a default hash for enums
This avoids some boilerplate code that was necessary when using enums
as keys in maps or sets.
2021-03-17 11:37:46 +01:00
e00a47ffd6 Geometry Nodes: store domain and data type in attribute hints
The information is not exposed in the attribute search yet.
2021-03-17 11:15:39 +01:00
e91dd645a9 Fix concern: lookup could fail.
Concern raised on {93e2491ee724}.
2021-03-17 09:18:39 +01:00
187f358f33 Cleanup: Use blender::MultiValueMap.
Fixed concern raise on {93e2491ee724}.
2021-03-17 09:14:38 +01:00
97defd9cd7 Cryptomatte: Show Name of Object/Material Under The Cursor.
This change shows the object or material name with the cursor when picking for a cryptomatte node.

Reviewed By: Julian Eisel

Differential Revision: https://developer.blender.org/D10705
2021-03-17 08:59:04 +01:00
f4639276ee Fix outliner multi-object edit-armature operations
Recursive restrict selection (hide/selectable flag) & renaming
edit-bones both used the active object, even when bones for another
non-active object were being operated on.

Bone renaming would rename a bone in the active object
(if that name exists).
2021-03-17 17:19:41 +11:00
d512fe441b Comments: document memory management for BPyPropStore 2021-03-17 15:27:37 +11:00
277e9b4355 PyAPI: use union to store pointer poll callback
Reduces `BPyPropStore` size by one pointer.
2021-03-17 15:15:16 +11:00
e8f6c65b44 UI: Rename Init to Initialize Face Sets in menu
Reviewed By: HooglyBoogly

Differential Revision: https://developer.blender.org/D10741
2021-03-17 02:43:46 +01:00
366 changed files with 5093 additions and 3019 deletions

View File

@@ -35,7 +35,11 @@ void WASAPIDevice::start()
{
lock();
if(!m_playing)
// thread is still running, we can abort stopping it
if(m_stop)
m_stop = false;
// thread is not running, let's start it
else if(!m_playing)
{
if(m_thread.joinable())
m_thread.join();
@@ -53,20 +57,35 @@ void WASAPIDevice::updateStream()
UINT32 buffer_size;
data_t* buffer;
lock();
if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
{
m_playing = false;
m_stop = false;
unlock();
return;
}
IAudioRenderClient* render_client = nullptr;
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
{
m_playing = false;
m_stop = false;
unlock();
return;
}
UINT32 padding;
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
{
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
@@ -75,31 +94,40 @@ void WASAPIDevice::updateStream()
if(FAILED(render_client->GetBuffer(length, &buffer)))
{
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
lock();
mix((data_t*)buffer, length);
unlock();
if(FAILED(render_client->ReleaseBuffer(length, 0)))
{
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
unlock();
m_audio_client->Start();
auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2);
for(;;)
{
lock();
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
{
m_audio_client->Stop();
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
@@ -109,44 +137,52 @@ void WASAPIDevice::updateStream()
{
m_audio_client->Stop();
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
lock();
mix((data_t*)buffer, length);
unlock();
if(FAILED(render_client->ReleaseBuffer(length, 0)))
{
m_audio_client->Stop();
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
// stop thread
if(!m_playing)
if(m_stop)
{
m_audio_client->Stop();
SafeRelease(&render_client);
m_playing = false;
m_stop = false;
unlock();
return;
}
unlock();
std::this_thread::sleep_for(sleepDuration);
}
}
void WASAPIDevice::playing(bool playing)
{
if(!m_playing && playing)
if((!m_playing || m_stop) && playing)
start();
else
m_playing = playing;
m_stop = true;
}
WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
m_playing(false),
m_stop(false),
m_imm_device_enumerator(nullptr),
m_imm_device(nullptr),

View File

@@ -48,6 +48,11 @@ private:
*/
bool m_playing;
/**
* Whether the current playback should stop.
*/
bool m_stop;
IMMDeviceEnumerator* m_imm_device_enumerator;
IMMDevice* m_imm_device;
IAudioClient* m_audio_client;

View File

@@ -1234,7 +1234,8 @@ static void add_nodes(Scene *scene,
for (BL::NodeLink &b_link : b_ntree.links) {
/* Ignore invalid links to avoid unwanted cycles created in graph.
* Also ignore links with unavailable sockets. */
if (!(b_link.is_valid() && b_link.from_socket().enabled() && b_link.to_socket().enabled())) {
if (!(b_link.is_valid() && b_link.from_socket().enabled() && b_link.to_socket().enabled()) ||
b_link.is_muted()) {
continue;
}
/* get blender link data */

View File

@@ -640,6 +640,12 @@ ccl_device_noinline
/* If we hit the surface, we are done. */
break;
}
else if (throughput.x < VOLUME_THROUGHPUT_EPSILON &&
throughput.y < VOLUME_THROUGHPUT_EPSILON &&
throughput.z < VOLUME_THROUGHPUT_EPSILON) {
/* Avoid unnecessary work and precision issue when throughput gets really small. */
break;
}
}
kernel_assert(isfinite_safe(throughput.x) && isfinite_safe(throughput.y) &&

View File

@@ -16,6 +16,12 @@
CCL_NAMESPACE_BEGIN
/* Ignore paths that have volume throughput below this value, to avoid unnecessary work
* and precision issues.
* todo: this value could be tweaked or turned into a probability to avoid unnecessary
* work in volumes and subsurface scattering. */
#define VOLUME_THROUGHPUT_EPSILON 1e-6f
/* Events for probalistic scattering */
typedef enum VolumeIntegrateResult {
@@ -226,7 +232,6 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
const float object_step_size)
{
float3 tp = *throughput;
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
/* Prepare for stepping.
* For shadows we do not offset all segments, since the starting point is
@@ -254,13 +259,15 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
/* compute attenuation over segment */
if (volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) {
/* Compute expf() only for every Nth step, to save some calculations
* because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */
* because exp(a)*exp(b) = exp(a+b), also do a quick VOLUME_THROUGHPUT_EPSILON
* check then. */
sum += (-sigma_t * dt);
if ((i & 0x07) == 0) { /* ToDo: Other interval? */
tp = *throughput * exp3(sum);
/* stop if nearly all light is blocked */
if (tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps)
if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON &&
tp.z < VOLUME_THROUGHPUT_EPSILON)
break;
}
}
@@ -572,7 +579,6 @@ kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
const float object_step_size)
{
float3 tp = *throughput;
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
/* Prepare for stepping.
* Using a different step offset for the first step avoids banding artifacts. */
@@ -669,7 +675,8 @@ kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
tp = new_tp;
/* stop if nearly all light blocked */
if (tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) {
if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON &&
tp.z < VOLUME_THROUGHPUT_EPSILON) {
tp = zero_float3();
break;
}
@@ -770,8 +777,6 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
VolumeSegment *segment,
const float object_step_size)
{
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
/* prepare for volume stepping */
int max_steps;
float step_size, step_shade_offset, steps_offset;
@@ -895,8 +900,9 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
break;
/* stop if nearly all light blocked */
if (accum_transmittance.x < tp_eps && accum_transmittance.y < tp_eps &&
accum_transmittance.z < tp_eps)
if (accum_transmittance.x < VOLUME_THROUGHPUT_EPSILON &&
accum_transmittance.y < VOLUME_THROUGHPUT_EPSILON &&
accum_transmittance.z < VOLUME_THROUGHPUT_EPSILON)
break;
}

View File

@@ -26,7 +26,7 @@ ccl_device void kernel_path_init(KernelGlobals *kg)
int ray_index = ccl_global_id(0) + ccl_global_id(1) * ccl_global_size(0);
/* This is the first assignment to ray_state;
* So we dont use ASSIGN_RAY_STATE macro.
* So we don't use ASSIGN_RAY_STATE macro.
*/
kernel_split_state.ray_state[ray_index] = RAY_ACTIVE;

View File

@@ -282,7 +282,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_wintab.maxAzimuth = Orientation[0].axMax;
m_wintab.maxAltitude = Orientation[1].axMax;
}
else { /* no so dont do tilt stuff */
else { /* No so don't do tilt stuff. */
m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
}
}
@@ -1057,7 +1057,7 @@ void GHOST_WindowWin32::processWin32TabletInitEvent()
m_wintab.maxAzimuth = Orientation[0].axMax;
m_wintab.maxAltitude = Orientation[1].axMax;
}
else { /* no so dont do tilt stuff */
else { /* No so don't do tilt stuff. */
m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
}
}

View File

@@ -51,7 +51,7 @@
#include <stdio.h> /* needed for FILE* */
/* needed for uintptr_t and attributes, exception, dont use BLI anywhere else in MEM_* */
/* Needed for uintptr_t and attributes, exception, don't use BLI anywhere else in `MEM_*` */
#include "../../source/blender/blenlib/BLI_compiler_attrs.h"
#include "../../source/blender/blenlib/BLI_sys_types.h"

View File

@@ -825,6 +825,7 @@ const bTheme U_theme_default = {
.vertex_size = 3,
.outline_width = 1,
.facedot_size = 4,
.noodle_curving = 4,
.grid_levels = 2,
.syntaxl = RGBA(0x565656ff),
.syntaxs = RGBA(0x975b5bff),

View File

@@ -953,7 +953,7 @@
frame_node="#9b9b9ba0"
matte_node="#977474"
distor_node="#749797"
noodle_curving="0"
noodle_curving="4"
grid_levels="2"
input_node="#cb3d4a"
output_node="#cb3d4a"

View File

@@ -1844,6 +1844,7 @@ def km_node_editor(params):
("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("node.add_reroute", {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "shift": True}, None),
("node.links_cut", {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True}, None),
("node.links_mute", {"type": 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True, "alt": True}, None),
("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.backimage_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
("node.backimage_zoom", {"type": 'V', "value": 'PRESS', "repeat": True},
@@ -6627,7 +6628,7 @@ def km_3d_view_tool_sculpt_color_filter(params):
def km_3d_view_tool_sculpt_mask_by_color(params):
return (
"3D View Tool: Sculpt, Mask By Color",
"3D View Tool: Sculpt, Mask by Color",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'},

View File

@@ -1113,6 +1113,7 @@ def km_node_editor(params):
("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("node.add_reroute", {"type": params.action_tweak, "value": 'ANY', "shift": True}, None),
("node.links_cut", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, None),
("node.links_mute", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.backimage_fit", {"type": 'A', "value": 'PRESS', "alt": True}, None),
("node.backimage_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),

View File

@@ -91,7 +91,32 @@ class NewGeometryNodeTreeAssign(bpy.types.Operator):
return {'FINISHED'}
class CopyGeometryNodeTreeAssign(bpy.types.Operator):
"""Copy the active geometry node group and assign it to the active modifier"""
bl_idname = "node.copy_geometry_node_group_assign"
bl_label = "Copy Geometry Node Group"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return geometry_modifier_poll(context)
def execute(self, context):
modifier = context.object.modifiers.active
if modifier is None:
return {'CANCELLED'}
group = modifier.node_group
if group is None:
return {'CANCELLED'}
modifier.node_group = group.copy()
return {'FINISHED'}
classes = (
NewGeometryNodesModifier,
NewGeometryNodeTreeAssign,
CopyGeometryNodeTreeAssign,
)

View File

@@ -441,6 +441,7 @@ class QuickSmoke(ObjectModeOperator, Operator):
class QuickLiquid(Operator):
"""Make selected objects liquid"""
bl_idname = "object.quick_liquid"
bl_label = "Quick Liquid"
bl_options = {'REGISTER', 'UNDO'}

View File

@@ -62,18 +62,6 @@ class COLLECTION_PT_collection_flags(CollectionButtonsPanel, Panel):
col.prop(vlc, "indirect_only", toggle=False)
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
bl_label = "Line Art"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
collection = context.collection
row = layout.row()
row.prop(collection, "lineart_usage")
class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
bl_label = "Instancing"
@@ -86,10 +74,24 @@ class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
row = layout.row()
row.prop(collection, "instance_offset")
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
bl_label = "Line Art"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
collection = context.collection
row = layout.row()
row.prop(collection, "lineart_usage")
classes = (
COLLECTION_PT_collection_flags,
COLLECTION_PT_lineart_collection,
COLLECTION_PT_instancing,
COLLECTION_PT_lineart_collection,
)
if __name__ == "__main__": # only for live edit.

View File

@@ -97,7 +97,12 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
col = layout.column()
col.prop(light, "color")
col.prop(light, "energy")
col.separator()
col.prop(light, "diffuse_factor", text="Diffuse")
col.prop(light, "specular_factor", text="Specular")
col.prop(light, "volume_factor", text="Volume")
col.separator()

View File

@@ -245,8 +245,8 @@ class MATERIAL_PT_gpencil_custom_props(GPMaterialButtonsPanel, PropertyPanel, Pa
_property_type = bpy.types.Material
class MATERIAL_PT_gpencil_options(GPMaterialButtonsPanel, Panel):
bl_label = "Options"
class MATERIAL_PT_gpencil_settings(GPMaterialButtonsPanel, Panel):
bl_label = "Settings"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
@@ -275,7 +275,7 @@ classes = (
MATERIAL_PT_gpencil_surface,
MATERIAL_PT_gpencil_strokecolor,
MATERIAL_PT_gpencil_fillcolor,
MATERIAL_PT_gpencil_options,
MATERIAL_PT_gpencil_settings,
MATERIAL_PT_gpencil_custom_props,
)

View File

@@ -164,7 +164,10 @@ class NODE_HT_header(Header):
elif ob:
active_modifier = ob.modifiers.active
if active_modifier and active_modifier.type == "NODES":
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
if active_modifier.node_group:
row.template_ID(active_modifier, "node_group", new="node.copy_geometry_node_group_assign")
else:
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
else:
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
@@ -331,6 +334,7 @@ class NODE_MT_node(Menu):
layout.operator("node.link_make", text="Make and Replace Links").replace = True
layout.operator("node.links_cut")
layout.operator("node.links_detach")
layout.operator("node.links_mute")
layout.separator()

View File

@@ -352,8 +352,12 @@ class SEQUENCER_MT_view(Menu):
if is_sequencer_view:
layout.prop(st, "show_region_hud")
layout.separator()
if st.view_type == 'SEQUENCER':
layout.prop(st, "show_backdrop", text="Preview as Backdrop")
if is_preview or st.show_backdrop:
layout.prop(st, "show_transform_preview", text="Preview During Transform")
layout.separator()
@@ -724,6 +728,7 @@ class SEQUENCER_MT_strip_image_transform(Menu):
layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION'
layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL'
class SEQUENCER_MT_strip_transform(Menu):
bl_label = "Transform"
@@ -1271,7 +1276,13 @@ class SEQUENCER_PT_effect_text_style(SequencerButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
col = layout.column()
col.template_ID(strip, "font", open="font.open", unlink="font.unlink")
row = col.row(align=True)
row.use_property_decorate = False
row.template_ID(strip, "font", open="font.open", unlink="font.unlink")
row.prop(strip, "use_bold", text="", icon="BOLD")
row.prop(strip, "use_italic", text="", icon="ITALIC")
col.prop(strip, "font_size")
col.prop(strip, "color")
@@ -1294,7 +1305,6 @@ class SEQUENCER_PT_effect_text_style(SequencerButtonsPanel, Panel):
row.prop_decorator(strip, "box_color")
row = layout.row(align=True, heading="Box Margin")
row.use_property_decorate = False
sub = row.row(align=True)
sub.prop(strip, "box_margin")
sub.active = strip.use_box and (not strip.mute)
@@ -1415,36 +1425,36 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
return (strip.type == 'SCENE')
def draw(self, context):
strip = act_strip(context)
scene = strip.scene
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
strip = act_strip(context)
layout.active = not strip.mute
layout.template_ID(strip, "scene")
scene = strip.scene
layout.prop(strip, "scene_input")
if scene:
layout.prop(scene, "audio_volume", text="Volume")
layout.template_ID(strip, "scene", text="Scene")
layout.prop(strip, "scene_input", text="Input")
if strip.scene_input == 'CAMERA':
layout.alignment = 'RIGHT'
sub = layout.column(align=True)
split = sub.split(factor=0.5, align=True)
layout.template_ID(strip, "scene_camera", text="Camera")
if scene:
# Build a manual split layout as a hack to get proper alignment with the rest of the buttons.
sub = layout.row(align=True)
sub.use_property_decorate = True
split = sub.split(factor=0.4, align=True)
split.alignment = 'RIGHT'
split.label(text="Camera")
split.template_ID(strip, "scene_camera")
layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
split.label(text="Volume")
split.prop(scene, "audio_volume", text="")
sub.use_property_decorate = False
if strip.scene_input == 'CAMERA':
layout = layout.column(heading="Show")
layout.prop(strip, "use_grease_pencil", text="Grease Pencil")
if scene:
# Warning, this is not a good convention to follow.
# Expose here because setting the alpha from the 'Render' menu is very inconvenient.
# layout.label(text="Preview")
layout.prop(scene.render, "film_transparent")

View File

@@ -34,7 +34,8 @@ class SPREADSHEET_HT_header(bpy.types.Header):
layout.prop(space, "object_eval_state", text="")
if space.object_eval_state != "ORIGINAL":
layout.prop(space, "geometry_component_type", text="")
layout.prop(space, "attribute_domain", text="")
if space.geometry_component_type != 'INSTANCES':
layout.prop(space, "attribute_domain", text="")
if used_id:
layout.label(text=used_id.name, icon="OBJECT_DATA")

View File

@@ -3144,7 +3144,7 @@ class VIEW3D_MT_face_sets(Menu):
layout.separator()
layout.menu("VIEW3D_MT_face_sets_init", text="Init Face Sets")
layout.menu("VIEW3D_MT_face_sets_init", text="Initialize Face Sets")
layout.separator()

View File

@@ -518,7 +518,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeTriangulate"),
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivideSmooth"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeSubdivide"),
# These should be in a sub-menu, but that requires a refactor to build the add menu manually.

View File

@@ -201,11 +201,6 @@ void BKE_pose_apply_action(struct Object *ob,
struct bAction *action,
struct AnimationEvalContext *anim_eval_context);
/* get_objectspace_bone_matrix has to be removed still */
void get_objectspace_bone_matrix(struct Bone *bone,
float M_accumulatedMatrix[4][4],
int root,
int posed);
void vec_roll_to_mat3(const float vec[3], const float roll, float r_mat[3][3]);
void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]);
void mat3_to_vec_roll(const float mat[3][3], float r_vec[3], float *r_roll);

View File

@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 12
#define BLENDER_FILE_SUBVERSION 13
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@@ -66,6 +66,8 @@ void BKE_curveprofile_selected_handle_set(struct CurveProfile *profile, int type
void BKE_curveprofile_reverse(struct CurveProfile *profile);
void BKE_curveprofile_reset_view(struct CurveProfile *profile);
void BKE_curveprofile_reset(struct CurveProfile *profile);
void BKE_curveprofile_create_samples(struct CurveProfile *profile,

View File

@@ -49,16 +49,6 @@ enum class GeometryOwnershipType {
ReadOnly = 2,
};
/* Make it possible to use the component type as key in hash tables. */
namespace blender {
template<> struct DefaultHash<GeometryComponentType> {
uint64_t operator()(const GeometryComponentType &value) const
{
return (uint64_t)value;
}
};
} // namespace blender
namespace blender::bke {
class ComponentAttributeProviders;
}

View File

@@ -42,6 +42,7 @@
extern "C" {
#endif
struct Collection;
struct ID;
struct IDOverrideLibrary;
struct IDOverrideLibraryProperty;
@@ -81,6 +82,7 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,

View File

@@ -627,6 +627,7 @@ struct bNodeLink *nodeAddLink(struct bNodeTree *ntree,
struct bNodeSocket *tosock);
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link);
bool nodeLinkIsHidden(const struct bNodeLink *link);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);
@@ -1289,10 +1290,11 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node);
void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len);
/* Update the runtime layer names with the cryptomatte layer names of the references
* render layer or image. */
void ntreeCompositCryptomatteUpdateLayerNames(bNode *node);
struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node);
/** \} */
/* -------------------------------------------------------------------- */
@@ -1358,7 +1360,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_BOOLEAN 1003
#define GEO_NODE_POINT_DISTRIBUTE 1004
#define GEO_NODE_POINT_INSTANCE 1005
#define GEO_NODE_SUBDIVIDE_SMOOTH 1006
#define GEO_NODE_SUBDIVISION_SURFACE 1006
#define GEO_NODE_OBJECT_INFO 1007
#define GEO_NODE_ATTRIBUTE_RANDOMIZE 1008
#define GEO_NODE_ATTRIBUTE_MATH 1009

View File

@@ -20,13 +20,17 @@
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_session_uuid.h"
#include "BLI_set.hh"
#include "DNA_ID.h"
#include "DNA_customdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_session_uuid_types.h"
#include "BKE_attribute.h"
struct ModifierData;
struct Object;
struct bNode;
@@ -77,9 +81,26 @@ struct NodeWarning {
std::string message;
};
struct AvailableAttributeInfo {
AttributeDomain domain;
CustomDataType data_type;
uint64_t hash() const
{
uint64_t domain_hash = (uint64_t)domain;
uint64_t data_type_hash = (uint64_t)data_type;
return (domain_hash * 33) ^ (data_type_hash * 89);
}
friend bool operator==(const AvailableAttributeInfo &a, const AvailableAttributeInfo &b)
{
return a.domain == b.domain && a.data_type == b.data_type;
}
};
struct NodeUIStorage {
blender::Vector<NodeWarning> warnings;
blender::Set<std::string> attribute_name_hints;
blender::MultiValueMap<std::string, AvailableAttributeInfo> attribute_hints;
};
struct NodeTreeUIStorage {
@@ -103,4 +124,6 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const NodeTreeEvaluationContext &context,
const bNode &node,
const blender::StringRef attribute_name);
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type);

View File

@@ -359,7 +359,7 @@ void DM_init(DerivedMesh *dm,
dm->needsFree = 1;
dm->dirty = (DMDirtyFlag)0;
/* don't use CustomData_reset(...); because we dont want to touch customdata */
/* Don't use CustomData_reset(...); because we don't want to touch custom-data. */
copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->edgeData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->faceData.typemap, CD_NUMTYPES, -1);

View File

@@ -1532,14 +1532,6 @@ void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan,
/** \name Bone Space to Space Conversion API
* \{ */
void get_objectspace_bone_matrix(struct Bone *bone,
float M_accumulatedMatrix[4][4],
int UNUSED(root),
int UNUSED(posed))
{
copy_m4_m4(M_accumulatedMatrix, bone->arm_mat);
}
/* Convert World-Space Matrix to Pose-Space Matrix */
void BKE_armature_mat_world_to_pose(Object *ob, const float inmat[4][4], float outmat[4][4])
{

View File

@@ -674,9 +674,10 @@ bool NamedLegacyCustomDataProvider::foreach_attribute(
return true;
}
void NamedLegacyCustomDataProvider::supported_domains(Vector<AttributeDomain> &r_domains) const
void NamedLegacyCustomDataProvider::foreach_domain(
const FunctionRef<void(AttributeDomain)> callback) const
{
r_domains.append_non_duplicates(domain_);
callback(domain_);
}
} // namespace blender::bke

View File

@@ -18,6 +18,9 @@
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
#include "BKE_geometry_set.hh"
namespace blender::bke {
@@ -105,7 +108,7 @@ template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
class DerivedArrayReadAttribute final : public ReadAttribute {
private:
blender::Span<StructT> data_;
Span<StructT> data_;
public:
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
@@ -159,10 +162,10 @@ template<typename StructT,
void (*SetFunc)(StructT &, const ElemT &)>
class DerivedArrayWriteAttribute final : public WriteAttribute {
private:
blender::MutableSpan<StructT> data_;
MutableSpan<StructT> data_;
public:
DerivedArrayWriteAttribute(AttributeDomain domain, blender::MutableSpan<StructT> data)
DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
{
}
@@ -247,7 +250,7 @@ class BuiltinAttributeProvider {
virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
virtual bool exists(const GeometryComponent &component) const = 0;
blender::StringRefNull name() const
StringRefNull name() const
{
return name_;
}
@@ -270,13 +273,12 @@ class BuiltinAttributeProvider {
class DynamicAttributesProvider {
public:
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
const blender::StringRef attribute_name) const = 0;
const StringRef attribute_name) const = 0;
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
const blender::StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component,
const blender::StringRef attribute_name) const = 0;
const StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const blender::StringRef UNUSED(attribute_name),
const StringRef UNUSED(attribute_name),
const AttributeDomain UNUSED(domain),
const CustomDataType UNUSED(data_type)) const
{
@@ -286,7 +288,7 @@ class DynamicAttributesProvider {
virtual bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const = 0;
virtual void supported_domains(blender::Vector<AttributeDomain> &r_domains) const = 0;
virtual void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const = 0;
};
/**
@@ -308,25 +310,24 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
}
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
const blender::StringRef attribute_name) const final;
const StringRef attribute_name) const final;
WriteAttributePtr try_get_for_write(GeometryComponent &component,
const blender::StringRef attribute_name) const final;
const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component,
const blender::StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_create(GeometryComponent &component,
const blender::StringRef attribute_name,
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
void supported_domains(blender::Vector<AttributeDomain> &r_domains) const final
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
{
r_domains.append_non_duplicates(domain_);
callback(domain_);
}
private:
@@ -388,7 +389,7 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
void supported_domains(Vector<AttributeDomain> &r_domains) const final;
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final;
};
/**
@@ -448,49 +449,46 @@ class ComponentAttributeProviders {
* higher priority when an attribute name is looked up. Usually, that means that builtin
* providers are checked before dynamic ones.
*/
blender::Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
/**
* An ordered list of dynamic attribute providers. The order is important because that is order
* in which they are checked when an attribute is looked up.
*/
blender::Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
/**
* All the domains that are supported by at least one of the providers above.
*/
blender::Vector<AttributeDomain> supported_domains_;
VectorSet<AttributeDomain> supported_domains_;
public:
ComponentAttributeProviders(
blender::Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
blender::Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
ComponentAttributeProviders(Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
: dynamic_attribute_providers_(dynamic_attribute_providers)
{
blender::Set<AttributeDomain> domains;
for (const BuiltinAttributeProvider *provider : builtin_attribute_providers) {
/* Use #add_new to make sure that no two builtin attributes have the same name. */
builtin_attribute_providers_.add_new(provider->name(), provider);
supported_domains_.append_non_duplicates(provider->domain());
supported_domains_.add(provider->domain());
}
for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) {
provider->supported_domains(supported_domains_);
provider->foreach_domain([&](AttributeDomain domain) { supported_domains_.add(domain); });
}
}
const blender::Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers()
const
const Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers() const
{
return builtin_attribute_providers_;
}
blender::Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
{
return dynamic_attribute_providers_;
}
blender::Span<AttributeDomain> supported_domains() const
Span<AttributeDomain> supported_domains() const
{
return supported_domains_;
}
};
} // namespace blender::bke
} // namespace blender::bke

View File

@@ -485,6 +485,11 @@ void BKE_collection_add_from_collection(Main *bmain,
collection_child_add(collection, collection_dst, 0, true);
is_instantiated = true;
}
else if (!is_instantiated && collection_find_child(collection, collection_dst)) {
/* If given collection_dst is already instantiated in scene, even if its 'model' src one is
* not, do not add it to master scene collection. */
is_instantiated = true;
}
}
FOREACH_SCENE_COLLECTION_END;

View File

@@ -435,6 +435,14 @@ static void curveprofile_build_steps(CurveProfile *profile)
}
}
/**
* Reset the view to the clipping rectangle.
*/
void BKE_curveprofile_reset_view(CurveProfile *profile)
{
profile->view_rect = profile->clip_rect;
}
/**
* Resets the profile to the current preset.
*

View File

@@ -341,7 +341,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
/* map faces to quads */
if (em->tottri != bm->totface) {
/* over alloc, since we dont know how many ngon or quads we have */
/* Over allocate, since we don't know how many ngon or quads we have. */
/* map fake face index to looptri */
face_as_quad_map = MEM_mallocN(sizeof(int) * totface, __func__);

View File

@@ -2310,7 +2310,7 @@ static void adaptive_domain_adjust(
z - fds->res_min[2]);
max_den = (fuel) ? MAX2(density[index], fuel[index]) : density[index];
/* check high resolution bounds if max density isnt already high enough */
/* Check high resolution bounds if max density isn't already high enough. */
if (max_den < fds->adapt_threshold && fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
int i, j, k;
/* high res grid index */

View File

@@ -296,6 +296,47 @@ static ReadAttributePtr adapt_mesh_domain_corner_to_polygon(const Mesh &mesh,
return new_attribute;
}
template<typename T>
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
/* For every edge, mix values from the two adjacent corners (the current and next corner). */
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const int loop_index_next = (loop_index + 1) % poly.totloop;
const MLoop &loop = mesh.mloop[loop_index];
const int edge_index = loop.e;
mixer.mix_in(edge_index, old_values[loop_index]);
mixer.mix_in(edge_index, old_values[loop_index_next]);
}
}
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
ReadAttributePtr attribute)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
template<typename T>
void adapt_mesh_domain_polygon_to_point_impl(const Mesh &mesh,
Span<T> old_values,
@@ -365,6 +406,42 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_corner(const Mesh &mesh,
return new_attribute;
}
template<typename T>
void adapt_mesh_domain_polygon_to_edge_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
const T value = old_values[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
mixer.mix_in(loop.e, value);
}
}
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_polygon_to_edge(const Mesh &mesh,
ReadAttributePtr attribute)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_polygon_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
@@ -406,6 +483,162 @@ static ReadAttributePtr adapt_mesh_domain_point_to_polygon(const Mesh &mesh,
return new_attribute;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
mixer.mix_in(edge_index, old_values[edge.v1]);
mixer.mix_in(edge_index, old_values[edge.v2]);
}
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
ReadAttributePtr attribute)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
template<typename T>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
/* For every corner, mix the values from the adjacent edges on the polygon. */
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const int loop_index_prev = (loop_index - 1) % poly.totloop;
const MLoop &loop = mesh.mloop[loop_index];
const MLoop &loop_prev = mesh.mloop[loop_index_prev];
mixer.mix_in(loop_index, old_values[loop.e]);
mixer.mix_in(loop_index, old_values[loop_prev.e]);
}
}
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
ReadAttributePtr attribute)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
template<typename T>
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
const T value = old_values[edge_index];
mixer.mix_in(edge.v1, value);
mixer.mix_in(edge.v2, value);
}
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
ReadAttributePtr attribute)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
/**
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_edge_to_polygon_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
mixer.mix_in(poly_index, old_values[loop.e]);
}
}
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_edge_to_polygon(const Mesh &mesh,
ReadAttributePtr attribute)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_edge_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
} // namespace blender::bke
ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
@@ -429,7 +662,10 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
case ATTR_DOMAIN_POLYGON:
return blender::bke::adapt_mesh_domain_corner_to_polygon(*mesh_, std::move(attribute));
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
default:
BLI_assert(false);
break;
}
break;
@@ -440,7 +676,10 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
case ATTR_DOMAIN_POLYGON:
return blender::bke::adapt_mesh_domain_point_to_polygon(*mesh_, std::move(attribute));
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
default:
BLI_assert(false);
break;
}
break;
@@ -451,12 +690,30 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
return blender::bke::adapt_mesh_domain_polygon_to_point(*mesh_, std::move(attribute));
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_polygon_to_corner(*mesh_, std::move(attribute));
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_polygon_to_edge(*mesh_, std::move(attribute));
default:
BLI_assert(false);
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
switch (new_domain) {
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
case ATTR_DOMAIN_POLYGON:
return blender::bke::adapt_mesh_domain_edge_to_polygon(*mesh_, std::move(attribute));
default:
BLI_assert(false);
break;
}
break;
}
default:
BLI_assert(false);
break;
}
@@ -782,9 +1039,9 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
return true;
}
void supported_domains(Vector<AttributeDomain> &r_domains) const final
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
{
r_domains.append_non_duplicates(ATTR_DOMAIN_POINT);
callback(ATTR_DOMAIN_POINT);
}
};

View File

@@ -230,6 +230,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
}
}
/* Don't create an empty mesh. */
if ((totverts + totloops + totedges + totpolys) == 0) {
return nullptr;
}
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
/* Copy settings from the first input geometry set with a mesh. */
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -366,6 +371,9 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
{
Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups,
convert_points_to_vertices);
if (new_mesh == nullptr) {
return;
}
MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
dst_component.replace(new_mesh);
@@ -397,6 +405,9 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT);
}
}
if (totpoint == 0) {
return;
}
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);

View File

@@ -663,6 +663,7 @@ static void lib_override_library_create_post_process(Main *bmain,
ViewLayer *view_layer,
ID *id_root,
ID *id_reference,
Collection *residual_storage,
const bool is_resync)
{
BKE_main_collection_sync(bmain);
@@ -718,7 +719,12 @@ static void lib_override_library_create_post_process(Main *bmain,
Object *ob_new = (Object *)id_root->newid;
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
if (is_resync && residual_storage != NULL) {
BKE_collection_object_add(bmain, residual_storage, ob_new);
}
else {
BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
}
}
break;
}
@@ -734,7 +740,7 @@ static void lib_override_library_create_post_process(Main *bmain,
BLI_assert(ob_new->id.override_library != NULL &&
ob_new->id.override_library->reference == &ob->id);
Collection *default_instantiating_collection = NULL;
Collection *default_instantiating_collection = residual_storage;
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
if (default_instantiating_collection == NULL) {
@@ -799,7 +805,8 @@ bool BKE_lib_override_library_create(
return success;
}
lib_override_library_create_post_process(bmain, scene, view_layer, id_root, id_reference, false);
lib_override_library_create_post_process(
bmain, scene, view_layer, id_root, id_reference, NULL, false);
/* Cleanup. */
BKE_main_id_clear_newpoins(bmain);
@@ -858,10 +865,15 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
* \param id_root: The root liboverride ID to resync from.
* \return true if override was successfully resynced.
*/
bool BKE_lib_override_library_resync(
Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, const bool do_hierarchy_enforce)
bool BKE_lib_override_library_resync(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
ID *id_root,
Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(!ID_IS_LINKED(id_root));
ID *id_root_reference = id_root->override_library->reference;
@@ -1069,8 +1081,13 @@ bool BKE_lib_override_library_resync(
* since we already relinked old root override collection to new resync'ed one above. So this
* call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
* that we do not have any stray objects. */
lib_override_library_create_post_process(
bmain, scene, view_layer, id_root_reference, id_root, true);
lib_override_library_create_post_process(bmain,
scene,
view_layer,
id_root_reference,
id_root,
override_resync_residual_storage,
true);
/* Cleanup. */
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
@@ -1097,6 +1114,23 @@ bool BKE_lib_override_library_resync(
*/
void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
{
/* We use a specific collection to gather/store all 'orphaned' override collections and objects
* generated by resyncprocess. This avoids putting them in scene's master collection. */
#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
Collection *override_resync_residual_storage = BLI_findstring(
&bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
if (override_resync_residual_storage != NULL &&
override_resync_residual_storage->id.lib != NULL) {
override_resync_residual_storage = NULL;
}
if (override_resync_residual_storage == NULL) {
override_resync_residual_storage = BKE_collection_add(
bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
/* Hide the collection from viewport and render. */
override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
COLLECTION_RESTRICT_RENDER;
}
BKE_main_relations_create(bmain, 0);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
@@ -1108,7 +1142,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
* those used by current existing overrides. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
continue;
}
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
@@ -1130,7 +1164,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
continue;
}
@@ -1180,9 +1214,14 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
continue;
}
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
if (ID_IS_LINKED(id)) {
continue;
}
do_continue = true;
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
const bool success = BKE_lib_override_library_resync(bmain, scene, view_layer, id, false);
const bool success = BKE_lib_override_library_resync(
bmain, scene, view_layer, id, override_resync_residual_storage, false);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@@ -1193,6 +1232,10 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
}
FOREACH_MAIN_LISTBASE_END;
}
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
}
}
/**
@@ -2240,10 +2283,11 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK;
/* Full rebuild of Depsgraph! */
/* Note: this is really brute force, in theory updates from RNA should have handled this already,
* but for now let's play it safe. */
DEG_id_tag_update_ex(bmain, local, ID_RECALC_COPY_ON_WRITE);
/* Note: Since we reload full content from linked ID here, potentially from edited local
* override, we do not really have a way to know *what* is changed, so we need to rely on the
* massive destruction weapon of `ID_RECALC_ALL` here. */
DEG_id_tag_update_ex(bmain, local, ID_RECALC_ALL);
/* For same reason as above, also assume that the relationships between IDs changed. */
DEG_relations_tag_update(bmain);
}

View File

@@ -881,7 +881,7 @@ Mesh *BKE_mesh_new_nomain(
NULL, ID_ME, BKE_idtype_idcode_to_name(ID_ME), LIB_ID_CREATE_LOCALIZE);
BKE_libblock_init_empty(&mesh->id);
/* don't use CustomData_reset(...); because we dont want to touch customdata */
/* Don't use CustomData_reset(...); because we don't want to touch custom-data. */
copy_vn_i(mesh->vdata.typemap, CD_NUMTYPES, -1);
copy_vn_i(mesh->edata.typemap, CD_NUMTYPES, -1);
copy_vn_i(mesh->fdata.typemap, CD_NUMTYPES, -1);

View File

@@ -635,7 +635,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
/* map faces to quads */
if (looptri_len != mpoly_len) {
/* over alloc, since we dont know how many ngon or quads we have */
/* Over allocate, since we don't know how many ngon or quads we have. */
/* map fake face index to looptri */
face_as_quad_map = MEM_mallocN(sizeof(int) * looptri_len, __func__);

View File

@@ -107,6 +107,9 @@ static void node_free_node(bNodeTree *ntree, bNode *node);
static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
bNodeSocket *sock,
const bool do_id_user);
static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
struct bNode *node,
const bool mute);
static void ntree_init_data(ID *id)
{
@@ -2215,6 +2218,106 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link)
}
}
/* Check if all output links are muted or not. */
static bool nodeMuteFromSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock)
{
int tot = 0;
int muted = 0;
LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) {
if (link->fromsock == sock) {
tot++;
if (link->flag & NODE_LINK_MUTED) {
muted++;
}
}
}
return tot == muted;
}
static void nodeMuteLink(bNodeLink *link)
{
link->flag |= NODE_LINK_MUTED;
link->flag |= NODE_LINK_TEST;
if (!(link->tosock->flag & SOCK_MULTI_INPUT)) {
link->tosock->flag &= ~SOCK_IN_USE;
}
}
static void nodeUnMuteLink(bNodeLink *link)
{
link->flag &= ~NODE_LINK_MUTED;
link->flag |= NODE_LINK_TEST;
link->tosock->flag |= SOCK_IN_USE;
}
/* Upstream muting. Always happens when unmuting but checks when muting. O(n^2) algorithm.*/
static void nodeMuteRerouteInputLinks(bNodeTree *ntree, bNode *node, const bool mute)
{
if (node->type != NODE_REROUTE) {
return;
}
if (!mute || nodeMuteFromSocketLinks(ntree, (bNodeSocket *)node->outputs.first)) {
bNodeSocket *sock = (bNodeSocket *)node->inputs.first;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (!(link->flag & NODE_LINK_VALID) || (link->tosock != sock)) {
continue;
}
if (mute) {
nodeMuteLink(link);
}
else {
nodeUnMuteLink(link);
}
nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
}
}
}
/* Downstream muting propagates when reaching reroute nodes. O(n^2) algorithm.*/
static void nodeMuteRerouteOutputLinks(bNodeTree *ntree, bNode *node, const bool mute)
{
if (node->type != NODE_REROUTE) {
return;
}
bNodeSocket *sock;
sock = (bNodeSocket *)node->outputs.first;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (!(link->flag & NODE_LINK_VALID) || (link->fromsock != sock)) {
continue;
}
if (mute) {
nodeMuteLink(link);
}
else {
nodeUnMuteLink(link);
}
nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
}
}
void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link)
{
if (link->tosock) {
bool mute = !(link->flag & NODE_LINK_MUTED);
if (mute) {
nodeMuteLink(link);
}
else {
nodeUnMuteLink(link);
}
if (link->tonode->type == NODE_REROUTE) {
nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
}
if (link->fromnode->type == NODE_REROUTE) {
nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
}
}
if (ntree) {
ntree->update |= NTREE_UPDATE_LINKS;
}
}
void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
@@ -2257,6 +2360,10 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link->flag &= ~NODE_LINK_VALID;
}
if (fromlink->flag & NODE_LINK_MUTED) {
link->flag |= NODE_LINK_MUTED;
}
ntree->update |= NTREE_UPDATE_LINKS;
}
else {
@@ -4014,7 +4121,9 @@ void ntreeTagUsedSockets(bNodeTree *ntree)
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
link->fromsock->flag |= SOCK_IN_USE;
link->tosock->flag |= SOCK_IN_USE;
if (!(link->flag & NODE_LINK_MUTED)) {
link->tosock->flag |= SOCK_IN_USE;
}
}
}
@@ -4828,8 +4937,8 @@ static void registerGeometryNodes()
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
register_node_type_geo_sample_texture();
register_node_type_geo_subdivide_smooth();
register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();

View File

@@ -158,8 +158,11 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const NodeTreeEvaluationContext &context,
const bNode &node,
const StringRef attribute_name)
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type)
{
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
node_ui_storage.attribute_name_hints.add_as(attribute_name);
node_ui_storage.attribute_hints.add_as(attribute_name,
AvailableAttributeInfo{domain, data_type});
}

View File

@@ -60,6 +60,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class Array {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using const_iterator = const T *;
using size_type = int64_t;
private:
/** The beginning of the array. It might point into the inline buffer. */
T *data_;

View File

@@ -22,7 +22,7 @@ extern "C" {
/* only include from header */
#ifndef __BLI_ENDIAN_SWITCH_H__
# error "this file isnt to be directly included"
# error "this file isn't to be directly included"
#endif
/** \file

View File

@@ -35,6 +35,16 @@ struct float4x4 {
{
}
/* Assumes an XYZ euler order. */
static float4x4 from_loc_eul_scale(const float3 location,
const float3 rotation,
const float3 scale)
{
float4x4 mat;
loc_eul_size_to_mat4(mat.values, location, rotation, scale);
return mat;
}
operator float *()
{
return &values[0][0];
@@ -79,6 +89,26 @@ struct float4x4 {
return m * float3(v);
}
float3 translation() const
{
return float3(values[3]);
}
/* Assumes XYZ rotation order. */
float3 to_euler() const
{
float3 euler;
mat4_to_eul(euler, values);
return euler;
}
float3 scale() const
{
float3 scale;
mat4_to_size(scale, values);
return scale;
}
float4x4 inverted() const
{
float4x4 result;

View File

@@ -88,11 +88,18 @@ namespace blender {
* If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on
* the value. If there is no such method, this will result in a compiler error. Usually that means
* that you have to implement a hash function using one of three strategies listed above.
*
* In the case of an enum type, the default hash is just to cast the enum value to an integer.
*/
template<typename T> struct DefaultHash {
uint64_t operator()(const T &value) const
{
return value.hash();
if constexpr (std::is_enum_v<T>) {
return (uint64_t)value;
}
else {
return value.hash();
}
}
};

View File

@@ -82,11 +82,18 @@ class IndexRange {
}
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = int64_t;
using pointer = const int64_t *;
using reference = const int64_t &;
using difference_type = std::ptrdiff_t;
private:
int64_t current_;
public:
constexpr Iterator(int64_t current) : current_(current)
constexpr explicit Iterator(int64_t current) : current_(current)
{
}
@@ -96,9 +103,21 @@ class IndexRange {
return *this;
}
constexpr bool operator!=(const Iterator &iterator) const
constexpr Iterator operator++(int) const
{
return current_ != iterator.current_;
Iterator copied_iterator = *this;
++copied_iterator;
return copied_iterator;
}
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
{
return a.current_ != b.current_;
}
constexpr friend bool operator==(const Iterator &a, const Iterator &b)
{
return a.current_ == b.current_;
}
constexpr int64_t operator*() const

View File

@@ -21,7 +21,7 @@
* \brief Single link-list utility macros. (header only api).
*
* Use this api when the structure defines its own ``next`` pointer
* and a double linked list such as #ListBase isnt needed.
* and a double linked list such as #ListBase isn't needed.
*/
#define BLI_LINKS_PREPEND(list, link) \

View File

@@ -120,6 +120,9 @@ template<
*/
typename Allocator = GuardedAllocator>
class Map {
public:
using size_type = int64_t;
private:
/**
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
@@ -623,6 +626,9 @@ class Map {
* This uses the "curiously recurring template pattern" (CRTP).
*/
template<typename SubIterator> struct BaseIterator {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
Slot *slots_;
int64_t total_slots_;
int64_t current_slot_;
@@ -642,6 +648,13 @@ class Map {
return *this;
}
BaseIterator operator++(int) const
{
BaseIterator copied_iterator = *this;
++copied_iterator;
return copied_iterator;
}
friend bool operator!=(const BaseIterator &a, const BaseIterator &b)
{
BLI_assert(a.slots_ == b.slots_);
@@ -649,6 +662,11 @@ class Map {
return a.current_slot_ != b.current_slot_;
}
friend bool operator==(const BaseIterator &a, const BaseIterator &b)
{
return !(a != b);
}
SubIterator begin() const
{
for (int64_t i = 0; i < total_slots_; i++) {
@@ -672,6 +690,10 @@ class Map {
class KeyIterator final : public BaseIterator<KeyIterator> {
public:
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<KeyIterator>(slots, total_slots, current_slot)
{
@@ -685,6 +707,10 @@ class Map {
class ValueIterator final : public BaseIterator<ValueIterator> {
public:
using value_type = Value;
using pointer = const Value *;
using reference = const Value &;
ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<ValueIterator>(slots, total_slots, current_slot)
{
@@ -698,6 +724,10 @@ class Map {
class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
public:
using value_type = Value;
using pointer = Value *;
using reference = Value &;
MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
{
@@ -726,6 +756,10 @@ class Map {
class ItemIterator final : public BaseIterator<ItemIterator> {
public:
using value_type = Item;
using pointer = Item *;
using reference = Item &;
ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<ItemIterator>(slots, total_slots, current_slot)
{
@@ -740,6 +774,10 @@ class Map {
class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
public:
using value_type = MutableItem;
using pointer = MutableItem *;
using reference = MutableItem &;
MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
{

View File

@@ -38,6 +38,9 @@
namespace blender {
template<typename Key, typename Value> class MultiValueMap {
public:
using size_type = int64_t;
private:
using MapType = Map<Key, Vector<Value>>;
MapType map_;

View File

@@ -119,6 +119,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class Set {
public:
class Iterator;
using value_type = Key;
using pointer = Key *;
using const_pointer = const Key *;
using reference = Key &;
using const_reference = const Key &;
using iterator = Iterator;
using size_type = int64_t;
private:
/**
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
@@ -401,6 +411,13 @@ class Set {
* also change their hash.
*/
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
using difference_type = std::ptrdiff_t;
private:
const Slot *slots_;
int64_t total_slots_;
@@ -422,17 +439,34 @@ class Set {
return *this;
}
Iterator operator++(int) const
{
Iterator copied_iterator = *this;
++copied_iterator;
return copied_iterator;
}
const Key &operator*() const
{
return *slots_[current_slot_].key();
}
const Key *operator->() const
{
return slots_[current_slot_].key();
}
friend bool operator!=(const Iterator &a, const Iterator &b)
{
BLI_assert(a.slots_ == b.slots_);
BLI_assert(a.total_slots_ == b.total_slots_);
return a.current_slot_ != b.current_slot_;
}
friend bool operator==(const Iterator &a, const Iterator &b)
{
return !(a != b);
}
};
Iterator begin() const

View File

@@ -85,6 +85,15 @@ namespace blender {
* modified.
*/
template<typename T> class Span {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = const T *;
using size_type = int64_t;
private:
const T *data_ = nullptr;
int64_t size_ = 0;
@@ -132,12 +141,11 @@ template<typename T> class Span {
}
/**
* Support implicit conversions like the ones below:
* Support implicit conversions like the one below:
* Span<T *> -> Span<const T *>
*/
template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr>
constexpr Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size())
constexpr Span(Span<U> span) : data_(static_cast<const T *>(span.data())), size_(span.size())
{
}
@@ -418,6 +426,19 @@ template<typename T> class Span {
return Span<NewT>(reinterpret_cast<const NewT *>(data_), new_size);
}
friend bool operator==(const Span<T> a, const Span<T> b)
{
if (a.size() != b.size()) {
return false;
}
return std::equal(a.begin(), a.end(), b.begin());
}
friend bool operator!=(const Span<T> a, const Span<T> b)
{
return !(a == b);
}
/**
* A debug utility to print the content of the Span. Every element will be printed on a
* separate line using the given callback.
@@ -447,6 +468,15 @@ template<typename T> class Span {
* MutableSpan.
*/
template<typename T> class MutableSpan {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using size_type = int64_t;
private:
T *data_;
int64_t size_;
@@ -467,11 +497,27 @@ template<typename T> class MutableSpan {
{
}
/**
* Support implicit conversions like the one below:
* MutableSpan<T *> -> MutableSpan<const T *>
*/
template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr>
constexpr MutableSpan(MutableSpan<U> span)
: data_(static_cast<T *>(span.data())), size_(span.size())
{
}
constexpr operator Span<T>() const
{
return Span<T>(data_, size_);
}
template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr>
constexpr operator Span<U>() const
{
return Span<U>(static_cast<const U *>(data_), size_);
}
/**
* Returns the number of elements in the array.
*/
@@ -653,12 +699,13 @@ template<typename T> class MutableSpan {
/**
* Returns a new span to the same underlying memory buffer. No conversions are done.
* The caller is responsible for making sure that the type cast is valid.
*/
template<typename NewT> constexpr MutableSpan<NewT> cast() const
{
BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0);
int64_t new_size = size_ * sizeof(T) / sizeof(NewT);
return MutableSpan<NewT>(reinterpret_cast<NewT *>(data_), new_size);
return MutableSpan<NewT>((NewT *)data_, new_size);
}
};

View File

@@ -80,6 +80,14 @@ template<
*/
typename Allocator = GuardedAllocator>
class Stack {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using size_type = int64_t;
private:
using Chunk = StackChunk<T>;

View File

@@ -76,6 +76,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class Vector {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using const_iterator = const T *;
using size_type = int64_t;
private:
/**
* Use pointers instead of storing the size explicitly. This reduces the number of instructions
@@ -879,6 +889,16 @@ class Vector {
return IndexRange(this->size());
}
friend bool operator==(const Vector &a, const Vector &b)
{
return a.as_span() == b.as_span();
}
friend bool operator!=(const Vector &a, const Vector &b)
{
return !(a == b);
}
/**
* Print some debug information about the vector.
*/

View File

@@ -100,6 +100,16 @@ template<
*/
typename Allocator = GuardedAllocator>
class VectorSet {
public:
using value_type = Key;
using pointer = Key *;
using const_pointer = const Key *;
using reference = Key &;
using const_reference = const Key &;
using iterator = Key *;
using const_iterator = const Key *;
using size_type = int64_t;
private:
/**
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by

View File

@@ -58,7 +58,7 @@ typedef intptr_t offset_t;
* typically we rely on the 'count' to avoid iterating past the end. */
// #define USE_TERMINATE_PARANOID
/* Currently totalloc isnt used. */
/* Currently totalloc isn't used. */
// #define USE_TOTALLOC
/* pad must be power of two */

View File

@@ -76,7 +76,7 @@
*/
#define USEDWORD MAKE_ID('u', 's', 'e', 'd')
/* currently totalloc isnt used */
/* Currently totalloc isn't used. */
// #define USE_TOTALLOC
/* optimize pool size */

View File

@@ -1289,7 +1289,7 @@ static BChunkList *bchunk_list_from_data_merge(const BArrayInfo *info,
BLI_assert(i_prev <= data_len);
for (size_t i = i_prev; i < data_len;) {
/* Assumes exiting chunk isnt a match! */
/* Assumes exiting chunk isn't a match! */
const BChunkRef *cref_found = table_lookup(
info, table, table_len, i_table_start, data, data_len, i, table_hash_array);
@@ -1317,7 +1317,7 @@ static BChunkList *bchunk_list_from_data_merge(const BArrayInfo *info,
BChunk *chunk_found = cref_found->link;
if (bchunk_data_compare(chunk_found, data, data_len, i_prev)) {
/* may be useful to remove table data, assuming we dont have
/* May be useful to remove table data, assuming we don't have
* repeating memory where it would be useful to re-use chunks. */
i += chunk_found->data_len;
bchunk_list_append(info, bs_mem, chunk_list, chunk_found);

View File

@@ -193,7 +193,7 @@ void _bli_array_binary_or(
* \param span_step: Indices to iterate over,
* initialize both values to the array length to initialize iteration.
* \param r_span_len: The length of the span, useful when \a use_wrap is enabled,
* where calculating the length isnt a simple subtraction.
* where calculating the length isn't a simple subtraction.
*/
bool _bli_array_iter_span(const void *arr,
unsigned int arr_len,

View File

@@ -167,7 +167,7 @@ bool BLI_tridiagonal_solve_cyclic(
return false;
}
/* prepare the noncyclic system; relies on tridiagonal_solve ignoring values */
/* Prepare the non-cyclic system; relies on tridiagonal_solve ignoring values. */
memcpy(b2, b, bytes);
b2[0] -= a0;
b2[count - 1] -= cN;

View File

@@ -147,4 +147,13 @@ TEST(index_range, constexpr_)
BLI_STATIC_ASSERT(range.size() == 1, "");
EXPECT_EQ(compiles[0], 1);
}
TEST(index_range, GenericAlgorithms)
{
IndexRange range{4, 10};
EXPECT_TRUE(std::any_of(range.begin(), range.end(), [](int v) { return v == 6; }));
EXPECT_FALSE(std::any_of(range.begin(), range.end(), [](int v) { return v == 20; }));
EXPECT_EQ(std::count_if(range.begin(), range.end(), [](int v) { return v < 7; }), 3);
}
} // namespace blender::tests

View File

@@ -565,6 +565,45 @@ TEST(map, AddOrModifyExceptions)
EXPECT_ANY_THROW({ map.add_or_modify(3, create_fn, modify_fn); });
}
namespace {
enum class TestEnum {
A = 0,
B = 1,
C = 2,
D = 1,
};
}
TEST(map, EnumKey)
{
Map<TestEnum, int> map;
map.add(TestEnum::A, 4);
map.add(TestEnum::B, 6);
EXPECT_EQ(map.lookup(TestEnum::A), 4);
EXPECT_EQ(map.lookup(TestEnum::B), 6);
EXPECT_EQ(map.lookup(TestEnum::D), 6);
EXPECT_FALSE(map.contains(TestEnum::C));
map.lookup(TestEnum::D) = 10;
EXPECT_EQ(map.lookup(TestEnum::B), 10);
}
TEST(map, GenericAlgorithms)
{
Map<int, int> map;
map.add(5, 2);
map.add(1, 4);
map.add(2, 2);
map.add(7, 1);
map.add(8, 6);
EXPECT_TRUE(std::any_of(map.keys().begin(), map.keys().end(), [](int v) { return v == 1; }));
EXPECT_TRUE(std::any_of(map.values().begin(), map.values().end(), [](int v) { return v == 1; }));
EXPECT_TRUE(std::any_of(
map.items().begin(), map.items().end(), [](auto item) { return item.value == 1; }));
EXPECT_EQ(std::count(map.values().begin(), map.values().end(), 2), 2);
EXPECT_EQ(std::count(map.values().begin(), map.values().end(), 4), 1);
EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/

View File

@@ -526,6 +526,24 @@ TEST(set, AddExceptions)
EXPECT_EQ(set.size(), 0);
}
TEST(set, ForwardIterator)
{
Set<int> set = {5, 2, 6, 4, 1};
Set<int>::iterator iter1 = set.begin();
int value1 = *iter1;
Set<int>::iterator iter2 = iter1++;
EXPECT_EQ(*iter1, value1);
EXPECT_EQ(*iter2, *(++iter1));
}
TEST(set, GenericAlgorithms)
{
Set<int> set = {1, 20, 30, 40};
EXPECT_FALSE(std::any_of(set.begin(), set.end(), [](int v) { return v == 5; }));
EXPECT_TRUE(std::any_of(set.begin(), set.end(), [](int v) { return v == 30; }));
EXPECT_EQ(std::count(set.begin(), set.end(), 20), 1);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/

View File

@@ -392,4 +392,33 @@ TEST(span, Constexpr)
EXPECT_EQ(span.slice(1, 2).size(), 2);
}
TEST(span, ImplicitConversions)
{
BLI_STATIC_ASSERT((std::is_convertible_v<MutableSpan<int>, Span<int>>), "");
BLI_STATIC_ASSERT((std::is_convertible_v<Span<int *>, Span<const int *>>), "");
BLI_STATIC_ASSERT((std::is_convertible_v<MutableSpan<int *>, Span<int *>>), "");
BLI_STATIC_ASSERT((std::is_convertible_v<MutableSpan<int *>, Span<const int *>>), "");
BLI_STATIC_ASSERT((std::is_convertible_v<MutableSpan<int *>, MutableSpan<const int *>>), "");
BLI_STATIC_ASSERT((!std::is_convertible_v<MutableSpan<const int *>, MutableSpan<int *>>), "");
BLI_STATIC_ASSERT((!std::is_convertible_v<Span<const int *>, Span<int *>>), "");
BLI_STATIC_ASSERT((!std::is_convertible_v<Span<int *>, MutableSpan<const int *>>), "");
}
TEST(span, Comparison)
{
std::array<int, 3> a = {3, 4, 5};
std::array<int, 4> b = {3, 4, 5, 6};
EXPECT_FALSE(Span(a) == Span(b));
EXPECT_FALSE(Span(b) == Span(a));
EXPECT_TRUE(Span(a) == Span(b).take_front(3));
EXPECT_TRUE(Span(a) == Span(a));
EXPECT_TRUE(Span(b) == Span(b));
EXPECT_TRUE(Span(a) != Span(b));
EXPECT_TRUE(Span(b) != Span(a));
EXPECT_FALSE(Span(a) != Span(b).take_front(3));
EXPECT_FALSE(Span(a) != Span(a));
}
} // namespace blender::tests

View File

@@ -18,6 +18,7 @@
*/
#pragma once
#include "BLI_listbase.h"
#include "BLI_sys_types.h"
/** \file
@@ -221,6 +222,26 @@ void BLO_library_link_end(struct Main *mainl,
int BLO_library_link_copypaste(struct Main *mainl, BlendHandle *bh, const uint64_t id_types_mask);
/**
* Struct for temporarily loading datablocks from a blend file.
*/
typedef struct TempLibraryContext {
struct Main *temp_main;
struct BlendHandle *blendhandle;
struct LibraryLink_Params liblink_params;
struct Library *lib;
/* The ID datablock that was loaded. Is NULL if loading failed. */
struct ID *temp_id;
} TempLibraryContext;
TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
const char *blend_file_path,
const short idcode,
const char *idname,
struct ReportList *reports);
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx);
/** \} */
void *BLO_library_read_struct(struct FileData *fd, struct BHead *bh, const char *blockname);

View File

@@ -50,6 +50,7 @@ set(SRC
intern/blend_validate.c
intern/readblenentry.c
intern/readfile.c
intern/readfile_tempload.c
intern/undofile.c
intern/versioning_250.c
intern/versioning_260.c

View File

@@ -0,0 +1,58 @@
/*
* 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.
*/
/** \file
* \ingroup blenloader
*/
#include "BLO_readfile.h"
#include "MEM_guardedalloc.h"
#include "BKE_report.h"
#include "DNA_ID.h"
TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
const char *blend_file_path,
const short idcode,
const char *idname,
struct ReportList *reports)
{
TempLibraryContext *temp_lib_ctx = MEM_callocN(sizeof(*temp_lib_ctx), __func__);
temp_lib_ctx->blendhandle = BLO_blendhandle_from_file(blend_file_path, reports);
BLO_library_link_params_init(&temp_lib_ctx->liblink_params, real_main, 0, LIB_TAG_TEMP_MAIN);
temp_lib_ctx->temp_main = BLO_library_link_begin(
&temp_lib_ctx->blendhandle, blend_file_path, &temp_lib_ctx->liblink_params);
temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->temp_main,
&temp_lib_ctx->blendhandle,
idcode,
idname,
&temp_lib_ctx->liblink_params);
return temp_lib_ctx;
}
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx)
{
BLO_library_link_end(
temp_lib_ctx->temp_main, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
BLO_blendhandle_close(temp_lib_ctx->blendhandle);
MEM_freeN(temp_lib_ctx);
}

View File

@@ -36,6 +36,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_hair_types.h"
#include "DNA_light_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
@@ -1892,6 +1893,18 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 293, 13)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "GeometryNodeSubdivideSmooth")) {
STRNCPY(node->idname, "GeometryNodeSubdivisionSurface");
}
}
}
}
}
/**
* Versioning code until next subversion bump goes here.
*
@@ -1903,5 +1916,12 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "diff_fac")) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->diff_fac = 1.0f;
light->volume_fac = 1.0f;
}
}
}
}

View File

@@ -939,7 +939,7 @@ void BM_data_layer_free(BMesh *bm, CustomData *data, int type)
data->pool = NULL;
has_layer = CustomData_free_layer_active(data, type, 0);
/* assert because its expensive to realloc - better not do if layer isnt present */
/* Assert because its expensive to realloc - better not do if layer isn't present. */
BLI_assert(has_layer != false);
UNUSED_VARS_NDEBUG(has_layer);
@@ -961,7 +961,7 @@ void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n)
data->pool = NULL;
has_layer = CustomData_free_layer(data, type, 0, CustomData_get_layer_index_n(data, type, n));
/* assert because its expensive to realloc - better not do if layer isnt present */
/* Assert because its expensive to realloc - better not do if layer isn't present. */
BLI_assert(has_layer != false);
UNUSED_VARS_NDEBUG(has_layer);

View File

@@ -158,7 +158,7 @@ int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
/**
* \brief Iterator as Array
*
* Allocates a new array, has the advantage that you dont need to know the size ahead of time.
* Allocates a new array, has the advantage that you don't need to know the size ahead of time.
*
* Takes advantage of less common iterator usage to avoid counting twice,
* which you might end up doing when #BM_iter_as_array is used.
@@ -410,7 +410,7 @@ int BM_iter_mesh_count_flag(const char itype, BMesh *bm, const char hflag, const
*/
/* see bug T36923 for why we need this,
* allow adding but not removing, this isnt _totally_ safe since
* allow adding but not removing, this isn't _totally_ safe since
* you could add/remove within the same loop, but catches common cases
*/
#ifdef DEBUG

View File

@@ -58,7 +58,7 @@ static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenera
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (BMO_vert_flag_test(bm, l_iter->v, VERT_INPUT) &&
/* ensure this vertex isnt part of a contiguous group */
/* Ensure this vertex isn't part of a contiguous group. */
((BMO_vert_flag_test(bm, l_iter->prev->v, VERT_INPUT) == 0) ||
(BMO_vert_flag_test(bm, l_iter->next->v, VERT_INPUT) == 0))) {
if (!l_tag_prev) {

View File

@@ -586,8 +586,8 @@ static bool bm_edge_test_cb(BMEdge *e, void *bm_v)
static bool bm_edge_test_rail_cb(BMEdge *e, void *UNUSED(bm_v))
{
/* normally operators dont check for hidden state
* but alternative would be to pass slot of rail edges */
/* Normally operators don't check for hidden state
* but alternative would be to pass slot of rail edges. */
if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
return false;
}

View File

@@ -599,7 +599,7 @@ void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op)
const short oflag = EDGE_MARK;
int i;
/* check flags dont change once set */
/* Check flags don't change once set. */
#ifndef NDEBUG
int tot_test;
#endif

View File

@@ -1344,7 +1344,7 @@ void BM_mesh_esubdivide(BMesh *bm,
{
BMOperator op;
/* use_sphere isnt exposed here since its only used for new primitives */
/* `use_sphere` isn't exposed here since its only used for new primitives. */
BMO_op_initf(bm,
&op,
BMO_FLAG_DEFAULTS,

View File

@@ -932,7 +932,7 @@ static void bm_edgering_pair_order(BMesh *bm,
BLI_assert(bm_edgering_pair_order_is_flipped(bm, el_store_a, el_store_b) == false);
}
else {
/* if we dont share and edge - flip */
/* If we don't share and edge - flip. */
BMEdge *e = BM_edge_exists(((LinkData *)lb_a->first)->data, ((LinkData *)lb_b->first)->data);
if (e == NULL || !BMO_edge_flag_test(bm, e, EDGE_RING)) {
BM_edgeloop_flip(bm, el_store_b);

View File

@@ -939,7 +939,7 @@ static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first)
* intended for speed over flexibility.
* can only collapse edges connected to (1, 2) tris.
*
* Important - dont add vert/edge/face data on collapsing!
* Important - don't add vert/edge/face data on collapsing!
*
* \param r_e_clear_other: Let caller know what edges we remove besides \a e_clear
* \param customdata_flag: Merge factor, scales from 0 - 1 ('v_clear' -> 'v_other')
@@ -1394,7 +1394,7 @@ void BM_mesh_decimate_collapse(BMesh *bm,
* \note
* - `eheap_table[e_index_mirr]` is only removed from the heap at the last moment
* since its possible (in theory) for collapsing `e` to remove `e_mirr`.
* - edges sharing a vertex are ignored, so the pivot vertex isnt moved to one side.
* - edges sharing a vertex are ignored, so the pivot vertex isn't moved to one side.
*/
BMEdge *e = BLI_heap_pop_min(eheap);

View File

@@ -401,7 +401,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
earray[i] = e_iter;
}
/* Remove all edges/verts left behind from dissolving,
* NULL'ing the vertex array so we dont re-use. */
* NULL'ing the vertex array so we don't re-use. */
for (i = bm->totedge - 1; i != -1; i--) {
e_iter = earray[i];

View File

@@ -77,12 +77,12 @@ static bool bm_vert_dissolve_fan_test(BMVert *v)
static bool bm_vert_dissolve_fan(BMesh *bm, BMVert *v)
{
/* collapse under 2 conditions.
/* Collapse under 2 conditions:
* - vert connects to 4 manifold edges (and 4 faces).
* - vert connects to 1 manifold edge, 2 boundary edges (and 2 faces).
*
* This covers boundary verts of a quad grid and center verts.
* note that surrounding faces dont have to be quads.
* note that surrounding faces don't have to be quads.
*/
BMIter iter;

View File

@@ -102,13 +102,13 @@ extern "C" {
* ExecutionGroups that have no viewer-node,
* will use a default one.
* There are several possible chunk orders
* - [@ref OrderOfChunks.COM_TO_CENTER_OUT]:
* - [@ref ChunkOrdering.CenterOut]:
* Start calculating from a configurable point and order by nearest chunk.
* - [@ref OrderOfChunks.COM_TO_RANDOM]:
* - [@ref ChunkOrdering.Random]:
* Randomize all chunks.
* - [@ref OrderOfChunks.COM_TO_TOP_DOWN]:
* - [@ref ChunkOrdering.TopDown]:
* Start calculation from the bottom to the top of the image.
* - [@ref OrderOfChunks.COM_TO_RULE_OF_THIRDS]:
* - [@ref ChunkOrdering.RuleOfThirds]:
* Experimental order based on 9 hot-spots in the image.
*
* When the chunk-order is determined, the first few chunks will be checked if they can be scheduled.
@@ -122,7 +122,7 @@ extern "C" {
*
* \see ExecutionGroup.execute
* \see ViewerOperation.getChunkOrder
* \see OrderOfChunks
* \see ChunkOrdering
*
* \section interest Area of interest
* An ExecutionGroup can have dependencies to other ExecutionGroup's.

View File

@@ -22,41 +22,41 @@
* \brief possible data types for sockets
* \ingroup Model
*/
typedef enum DataType {
enum class DataType {
/** \brief Value data type */
COM_DT_VALUE = 1,
Value = 0,
/** \brief Vector data type */
COM_DT_VECTOR = 2,
Vector = 1,
/** \brief Color data type */
COM_DT_COLOR = 4,
} DataType;
Color = 2,
};
/**
* \brief Possible quality settings
* \see CompositorContext.quality
* \ingroup Execution
*/
typedef enum CompositorQuality {
enum class CompositorQuality {
/** \brief High quality setting */
COM_QUALITY_HIGH = 0,
High = 0,
/** \brief Medium quality setting */
COM_QUALITY_MEDIUM = 1,
Medium = 1,
/** \brief Low quality setting */
COM_QUALITY_LOW = 2,
} CompositorQuality;
Low = 2,
};
/**
* \brief Possible priority settings
* \ingroup Execution
*/
typedef enum CompositorPriority {
enum class CompositorPriority {
/** \brief High quality setting */
COM_PRIORITY_HIGH = 2,
High = 2,
/** \brief Medium quality setting */
COM_PRIORITY_MEDIUM = 1,
Medium = 1,
/** \brief Low quality setting */
COM_PRIORITY_LOW = 0,
} CompositorPriority;
Low = 0,
};
// configurable items
@@ -87,18 +87,18 @@ typedef enum CompositorPriority {
* \brief The order of chunks to be scheduled
* \ingroup Execution
*/
typedef enum OrderOfChunks {
enum class ChunkOrdering {
/** \brief order from a distance to centerX/centerY */
COM_TO_CENTER_OUT = 0,
CenterOut = 0,
/** \brief order randomly */
COM_TO_RANDOM = 1,
Random = 1,
/** \brief no ordering */
COM_TO_TOP_DOWN = 2,
/** \brief experimental ordering with 9 hotspots */
COM_TO_RULE_OF_THIRDS = 3,
} OrderOfChunks;
TopDown = 2,
/** \brief experimental ordering with 9 hot-spots. */
RuleOfThirds = 3,
#define COM_ORDER_OF_CHUNKS_DEFAULT COM_TO_CENTER_OUT
Default = ChunkOrdering::CenterOut,
};
#define COM_RULE_OF_THIRDS_DIVIDER 100.0f

View File

@@ -17,22 +17,14 @@
*/
#include "COM_ChunkOrder.h"
#include "BLI_math.h"
ChunkOrder::ChunkOrder()
void ChunkOrder::update_distance(ChunkOrderHotspot *hotspots, unsigned int len_hotspots)
{
distance = 0.0;
number = 0;
x = 0;
y = 0;
}
void ChunkOrder::update_distance(ChunkOrderHotspot **hotspots, unsigned int len_hotspots)
{
double new_distance = FLT_MAX;
double new_distance = DBL_MAX;
for (int index = 0; index < len_hotspots; index++) {
ChunkOrderHotspot *hotspot = hotspots[index];
double distance_to_hotspot = hotspot->calc_distance(x, y);
double distance_to_hotspot = hotspots[index].calc_distance(x, y);
if (distance_to_hotspot < new_distance) {
new_distance = distance_to_hotspot;
}

View File

@@ -23,17 +23,17 @@
#endif
#include "COM_ChunkOrderHotspot.h"
struct ChunkOrder {
unsigned int number;
int x;
int y;
double distance;
ChunkOrder();
/** Helper to determine the order how chunks are prioritized during execution. */
struct ChunkOrder {
unsigned int index = 0;
int x = 0;
int y = 0;
double distance = 0.0;
friend bool operator<(const ChunkOrder &a, const ChunkOrder &b);
void update_distance(ChunkOrderHotspot **hotspots, unsigned int len_hotspots);
void update_distance(ChunkOrderHotspot *hotspots, unsigned int len_hotspots);
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")

View File

@@ -24,7 +24,7 @@ CompositorContext::CompositorContext()
{
this->m_scene = nullptr;
this->m_rd = nullptr;
this->m_quality = COM_QUALITY_HIGH;
this->m_quality = CompositorQuality::High;
this->m_hasActiveOpenCLDevices = false;
this->m_fastCalculation = false;
this->m_viewSettings = nullptr;

View File

@@ -428,22 +428,22 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node
const DataType src_data_type = from.getDataType();
const DataType dst_data_type = to.getDataType();
if (src_data_type == COM_DT_VALUE && dst_data_type == COM_DT_COLOR) {
if (src_data_type == DataType::Value && dst_data_type == DataType::Color) {
return new ConvertValueToColorOperation();
}
if (src_data_type == COM_DT_VALUE && dst_data_type == COM_DT_VECTOR) {
if (src_data_type == DataType::Value && dst_data_type == DataType::Vector) {
return new ConvertValueToVectorOperation();
}
if (src_data_type == COM_DT_COLOR && dst_data_type == COM_DT_VALUE) {
if (src_data_type == DataType::Color && dst_data_type == DataType::Value) {
return new ConvertColorToValueOperation();
}
if (src_data_type == COM_DT_COLOR && dst_data_type == COM_DT_VECTOR) {
if (src_data_type == DataType::Color && dst_data_type == DataType::Vector) {
return new ConvertColorToVectorOperation();
}
if (src_data_type == COM_DT_VECTOR && dst_data_type == COM_DT_VALUE) {
if (src_data_type == DataType::Vector && dst_data_type == DataType::Value) {
return new ConvertVectorToValueOperation();
}
if (src_data_type == COM_DT_VECTOR && dst_data_type == COM_DT_COLOR) {
if (src_data_type == DataType::Vector && dst_data_type == DataType::Color) {
return new ConvertVectorToColorOperation();
}

View File

@@ -165,13 +165,13 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
}
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<IN_%p>", socket);
switch (socket->getDataType()) {
case COM_DT_VALUE:
case DataType::Value:
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
break;
case COM_DT_VECTOR:
case DataType::Vector:
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
break;
case COM_DT_COLOR:
case DataType::Color:
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
break;
}
@@ -203,13 +203,13 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
}
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<OUT_%p>", socket);
switch (socket->getDataType()) {
case COM_DT_VALUE:
case DataType::Value:
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
break;
case COM_DT_VECTOR:
case DataType::Vector:
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
break;
case COM_DT_COLOR:
case DataType::Color:
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
break;
}
@@ -390,13 +390,13 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
std::string color;
switch (from->getDataType()) {
case COM_DT_VALUE:
case DataType::Value:
color = "gray";
break;
case COM_DT_VECTOR:
case DataType::Vector:
color = "blue";
break;
case COM_DT_COLOR:
case DataType::Color:
color = "orange";
break;
}

View File

@@ -34,10 +34,15 @@
#include "COM_defines.h"
#include "BLI_math.h"
#include "BLI_rand.hh"
#include "BLI_string.h"
#include "BLT_translation.h"
#include "MEM_guardedalloc.h"
#include "PIL_time.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -65,19 +70,19 @@ CompositorPriority ExecutionGroup::getRenderPriotrity()
return this->getOutputOperation()->getRenderPriority();
}
bool ExecutionGroup::canContainOperation(NodeOperation *operation)
bool ExecutionGroup::can_contain(NodeOperation &operation)
{
if (!this->m_initialized) {
return true;
}
if (operation->isReadBufferOperation()) {
if (operation.isReadBufferOperation()) {
return true;
}
if (operation->isWriteBufferOperation()) {
if (operation.isWriteBufferOperation()) {
return false;
}
if (operation->isSetOperation()) {
if (operation.isSetOperation()) {
return true;
}
@@ -87,7 +92,7 @@ bool ExecutionGroup::canContainOperation(NodeOperation *operation)
}
/* complex ops can't be added to other groups (except their own, which they initialize, see
* above) */
if (operation->isComplex()) {
if (operation.isComplex()) {
return false;
}
@@ -96,7 +101,7 @@ bool ExecutionGroup::canContainOperation(NodeOperation *operation)
bool ExecutionGroup::addOperation(NodeOperation *operation)
{
if (!canContainOperation(operation)) {
if (!can_contain(*operation)) {
return false;
}
@@ -178,6 +183,103 @@ void ExecutionGroup::determineNumberOfChunks()
}
}
blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() const
{
int index;
blender::Array<unsigned int> chunk_order(m_chunks_len);
for (int chunk_index = 0; chunk_index < this->m_chunks_len; chunk_index++) {
chunk_order[chunk_index] = chunk_index;
}
NodeOperation *operation = this->getOutputOperation();
float centerX = 0.5f;
float centerY = 0.5f;
ChunkOrdering order_type = ChunkOrdering::Default;
if (operation->isViewerOperation()) {
ViewerOperation *viewer = (ViewerOperation *)operation;
centerX = viewer->getCenterX();
centerY = viewer->getCenterY();
order_type = viewer->getChunkOrder();
}
const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
switch (order_type) {
case ChunkOrdering::Random: {
static blender::RandomNumberGenerator rng;
blender::MutableSpan<unsigned int> span = chunk_order.as_mutable_span();
/* Shuffle twice to make it more random. */
rng.shuffle(span);
rng.shuffle(span);
break;
}
case ChunkOrdering::CenterOut: {
ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f);
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
rcti rect;
determineChunkRect(&rect, index);
chunk_orders[index].index = index;
chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(&hotspot, 1);
}
std::sort(&chunk_orders[0], &chunk_orders[this->m_chunks_len - 1]);
for (index = 0; index < this->m_chunks_len; index++) {
chunk_order[index] = chunk_orders[index].index;
}
break;
}
case ChunkOrdering::RuleOfThirds: {
unsigned int tx = border_width / 6;
unsigned int ty = border_height / 6;
unsigned int mx = border_width / 2;
unsigned int my = border_height / 2;
unsigned int bx = mx + 2 * tx;
unsigned int by = my + 2 * ty;
float addition = this->m_chunks_len / COM_RULE_OF_THIRDS_DIVIDER;
ChunkOrderHotspot hotspots[9]{
ChunkOrderHotspot(mx, my, addition * 0),
ChunkOrderHotspot(tx, my, addition * 1),
ChunkOrderHotspot(bx, my, addition * 2),
ChunkOrderHotspot(bx, by, addition * 3),
ChunkOrderHotspot(tx, ty, addition * 4),
ChunkOrderHotspot(bx, ty, addition * 5),
ChunkOrderHotspot(tx, by, addition * 6),
ChunkOrderHotspot(mx, ty, addition * 7),
ChunkOrderHotspot(mx, by, addition * 8),
};
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
rcti rect;
determineChunkRect(&rect, index);
chunk_orders[index].index = index;
chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(hotspots, 9);
}
std::sort(&chunk_orders[0], &chunk_orders[this->m_chunks_len]);
for (index = 0; index < this->m_chunks_len; index++) {
chunk_order[index] = chunk_orders[index].index;
}
break;
}
case ChunkOrdering::TopDown:
default:
break;
}
return chunk_order;
}
/**
* this method is called for the top execution groups. containing the compositor node or the
* preview node or the viewer node)
@@ -195,119 +297,15 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
if (this->m_chunks_len == 0) {
return;
} /** \note Early break out. */
unsigned int chunkNumber;
unsigned int chunk_index;
this->m_executionStartTime = PIL_check_seconds_timer();
this->m_chunks_finished = 0;
this->m_bTree = bTree;
unsigned int index;
unsigned int *chunkOrder = (unsigned int *)MEM_mallocN(sizeof(unsigned int) * this->m_chunks_len,
__func__);
for (chunkNumber = 0; chunkNumber < this->m_chunks_len; chunkNumber++) {
chunkOrder[chunkNumber] = chunkNumber;
}
NodeOperation *operation = this->getOutputOperation();
float centerX = 0.5;
float centerY = 0.5;
OrderOfChunks chunkorder = COM_ORDER_OF_CHUNKS_DEFAULT;
if (operation->isViewerOperation()) {
ViewerOperation *viewer = (ViewerOperation *)operation;
centerX = viewer->getCenterX();
centerY = viewer->getCenterY();
chunkorder = viewer->getChunkOrder();
}
const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
switch (chunkorder) {
case COM_TO_RANDOM:
for (index = 0; index < 2 * this->m_chunks_len; index++) {
int index1 = rand() % this->m_chunks_len;
int index2 = rand() % this->m_chunks_len;
int s = chunkOrder[index1];
chunkOrder[index1] = chunkOrder[index2];
chunkOrder[index2] = s;
}
break;
case COM_TO_CENTER_OUT: {
ChunkOrderHotspot *hotspots[1];
hotspots[0] = new ChunkOrderHotspot(border_width * centerX, border_height * centerY, 0.0f);
rcti rect;
ChunkOrder *chunkOrders = (ChunkOrder *)MEM_mallocN(sizeof(ChunkOrder) * this->m_chunks_len,
__func__);
for (index = 0; index < this->m_chunks_len; index++) {
determineChunkRect(&rect, index);
chunkOrders[index].number = index;
chunkOrders[index].x = rect.xmin - this->m_viewerBorder.xmin;
chunkOrders[index].y = rect.ymin - this->m_viewerBorder.ymin;
chunkOrders[index].update_distance(hotspots, 1);
}
std::sort(&chunkOrders[0], &chunkOrders[this->m_chunks_len - 1]);
for (index = 0; index < this->m_chunks_len; index++) {
chunkOrder[index] = chunkOrders[index].number;
}
delete hotspots[0];
MEM_freeN(chunkOrders);
break;
}
case COM_TO_RULE_OF_THIRDS: {
ChunkOrderHotspot *hotspots[9];
unsigned int tx = border_width / 6;
unsigned int ty = border_height / 6;
unsigned int mx = border_width / 2;
unsigned int my = border_height / 2;
unsigned int bx = mx + 2 * tx;
unsigned int by = my + 2 * ty;
float addition = this->m_chunks_len / COM_RULE_OF_THIRDS_DIVIDER;
hotspots[0] = new ChunkOrderHotspot(mx, my, addition * 0);
hotspots[1] = new ChunkOrderHotspot(tx, my, addition * 1);
hotspots[2] = new ChunkOrderHotspot(bx, my, addition * 2);
hotspots[3] = new ChunkOrderHotspot(bx, by, addition * 3);
hotspots[4] = new ChunkOrderHotspot(tx, ty, addition * 4);
hotspots[5] = new ChunkOrderHotspot(bx, ty, addition * 5);
hotspots[6] = new ChunkOrderHotspot(tx, by, addition * 6);
hotspots[7] = new ChunkOrderHotspot(mx, ty, addition * 7);
hotspots[8] = new ChunkOrderHotspot(mx, by, addition * 8);
rcti rect;
ChunkOrder *chunkOrders = (ChunkOrder *)MEM_mallocN(sizeof(ChunkOrder) * this->m_chunks_len,
__func__);
for (index = 0; index < this->m_chunks_len; index++) {
determineChunkRect(&rect, index);
chunkOrders[index].number = index;
chunkOrders[index].x = rect.xmin - this->m_viewerBorder.xmin;
chunkOrders[index].y = rect.ymin - this->m_viewerBorder.ymin;
chunkOrders[index].update_distance(hotspots, 9);
}
std::sort(&chunkOrders[0], &chunkOrders[this->m_chunks_len]);
for (index = 0; index < this->m_chunks_len; index++) {
chunkOrder[index] = chunkOrders[index].number;
}
delete hotspots[0];
delete hotspots[1];
delete hotspots[2];
delete hotspots[3];
delete hotspots[4];
delete hotspots[5];
delete hotspots[6];
delete hotspots[7];
delete hotspots[8];
MEM_freeN(chunkOrders);
break;
}
case COM_TO_TOP_DOWN:
default:
break;
}
blender::Array<unsigned int> chunk_order = determine_chunk_execution_order();
DebugInfo::execution_group_started(this);
DebugInfo::graphviz(graph);
@@ -324,10 +322,10 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
for (index = startIndex; index < this->m_chunks_len && numberEvaluated < maxNumberEvaluated;
index++) {
chunkNumber = chunkOrder[index];
int yChunk = chunkNumber / this->m_x_chunks_len;
int xChunk = chunkNumber - (yChunk * this->m_x_chunks_len);
switch (m_chunk_execution_states[chunkNumber]) {
chunk_index = chunk_order[index];
int yChunk = chunk_index / this->m_x_chunks_len;
int xChunk = chunk_index - (yChunk * this->m_x_chunks_len);
switch (m_chunk_execution_states[chunk_index]) {
case eChunkExecutionState::NOT_SCHEDULED: {
scheduleChunkWhenPossible(graph, xChunk, yChunk);
finished = false;
@@ -361,17 +359,13 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
}
DebugInfo::execution_group_finished(this);
DebugInfo::graphviz(graph);
MEM_freeN(chunkOrder);
}
MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
{
rcti rect;
std::vector<MemoryProxy *> memoryproxies;
determineChunkRect(&rect, chunkNumber);
this->determineDependingMemoryProxies(&memoryproxies);
MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
sizeof(MemoryBuffer *) * this->m_max_read_buffer_offset, __func__);
rcti output;
@@ -379,18 +373,18 @@ MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
this->determineDependingAreaOfInterest(&rect, readOperation, &output);
MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
memoryProxy, &output);
*memoryProxy, output);
memoryBuffers[readOperation->getOffset()] = memoryBuffer;
}
return memoryBuffers;
}
MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy *memoryProxy,
rcti *rect)
MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &memoryProxy,
rcti &rect)
{
MemoryBuffer *imageBuffer = memoryProxy->getBuffer();
MemoryBuffer *result = new MemoryBuffer(memoryProxy, rect);
result->copyContentFrom(imageBuffer);
MemoryBuffer *imageBuffer = memoryProxy.getBuffer();
MemoryBuffer *result = new MemoryBuffer(&memoryProxy, rect, MemoryBufferState::Temporary);
result->fill_from(*imageBuffer);
return result;
}
@@ -460,13 +454,14 @@ void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int chunkNumb
determineChunkRect(rect, xChunk, yChunk);
}
MemoryBuffer *ExecutionGroup::allocateOutputBuffer(int /*chunkNumber*/, rcti *rect)
MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
{
// we assume that this method is only called from complex execution groups.
NodeOperation *operation = this->getOutputOperation();
if (operation->isWriteBufferOperation()) {
WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
MemoryBuffer *buffer = new MemoryBuffer(writeOperation->getMemoryProxy(), rect);
MemoryBuffer *buffer = new MemoryBuffer(
writeOperation->getMemoryProxy(), rect, MemoryBufferState::Temporary);
return buffer;
}
return nullptr;
@@ -516,54 +511,44 @@ bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
return false;
}
bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, int xChunk, int yChunk)
bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
const int chunk_x,
const int chunk_y)
{
if (xChunk < 0 || xChunk >= (int)this->m_x_chunks_len) {
if (chunk_x < 0 || chunk_x >= (int)this->m_x_chunks_len) {
return true;
}
if (yChunk < 0 || yChunk >= (int)this->m_y_chunks_len) {
return true;
}
int chunkNumber = yChunk * this->m_x_chunks_len + xChunk;
// chunk is already executed
if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::EXECUTED) {
if (chunk_y < 0 || chunk_y >= (int)this->m_y_chunks_len) {
return true;
}
// chunk is scheduled, but not executed
if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::SCHEDULED) {
// Check if chunk is already executed or scheduled and not yet executed.
const int chunk_index = chunk_y * this->m_x_chunks_len + chunk_x;
if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::EXECUTED) {
return true;
}
if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::SCHEDULED) {
return false;
}
// chunk is nor executed nor scheduled.
std::vector<MemoryProxy *> memoryProxies;
this->determineDependingMemoryProxies(&memoryProxies);
rcti rect;
determineChunkRect(&rect, xChunk, yChunk);
unsigned int index;
bool canBeExecuted = true;
determineChunkRect(&rect, chunk_x, chunk_y);
bool can_be_executed = true;
rcti area;
for (index = 0; index < m_read_operations.size(); index++) {
ReadBufferOperation *readOperation = m_read_operations[index];
for (ReadBufferOperation *read_operation : m_read_operations) {
BLI_rcti_init(&area, 0, 0, 0, 0);
MemoryProxy *memoryProxy = memoryProxies[index];
determineDependingAreaOfInterest(&rect, readOperation, &area);
ExecutionGroup *group = memoryProxy->getExecutor();
MemoryProxy *memory_proxy = read_operation->getMemoryProxy();
determineDependingAreaOfInterest(&rect, read_operation, &area);
ExecutionGroup *group = memory_proxy->getExecutor();
if (group != nullptr) {
if (!group->scheduleAreaWhenPossible(graph, &area)) {
canBeExecuted = false;
}
}
else {
throw "ERROR";
if (!group->scheduleAreaWhenPossible(graph, &area)) {
can_be_executed = false;
}
}
if (canBeExecuted) {
scheduleChunk(chunkNumber);
if (can_be_executed) {
scheduleChunk(chunk_index);
}
return false;
@@ -576,13 +561,6 @@ void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
}
void ExecutionGroup::determineDependingMemoryProxies(std::vector<MemoryProxy *> *memoryProxies)
{
for (ReadBufferOperation *readOperation : m_read_operations) {
memoryProxies->push_back(readOperation->getMemoryProxy());
}
}
bool ExecutionGroup::isOpenCL()
{
return this->m_openCL;

View File

@@ -22,6 +22,7 @@
# include "MEM_guardedalloc.h"
#endif
#include "BLI_array.hh"
#include "BLI_rect.h"
#include "BLI_vector.hh"
@@ -183,7 +184,7 @@ class ExecutionGroup {
* \brief check whether parameter operation can be added to the execution group
* \param operation: the operation to be added
*/
bool canContainOperation(NodeOperation *operation);
bool can_contain(NodeOperation &operation);
/**
* \brief calculate the actual chunk size of this execution group.
@@ -217,7 +218,7 @@ class ExecutionGroup {
* true: package(s) are scheduled
* false: scheduling is deferred (depending workpackages are scheduled)
*/
bool scheduleChunkWhenPossible(ExecutionSystem *graph, int xChunk, int yChunk);
bool scheduleChunkWhenPossible(ExecutionSystem *graph, const int chunk_x, const int chunk_y);
/**
* \brief try to schedule a specific area.
@@ -248,6 +249,11 @@ class ExecutionGroup {
ReadBufferOperation *readOperation,
rcti *output);
/**
* Return the execution order of the user visible chunks.
*/
blender::Array<unsigned int> determine_chunk_execution_order() const;
public:
// constructors
ExecutionGroup();
@@ -333,7 +339,7 @@ class ExecutionGroup {
* \brief compose multiple chunks into a single chunk
* \return Memorybuffer *consolidated chunk
*/
MemoryBuffer *constructConsolidatedMemoryBuffer(MemoryProxy *memoryProxy, rcti *rect);
MemoryBuffer *constructConsolidatedMemoryBuffer(MemoryProxy &memoryProxy, rcti &rect);
/**
* \brief initExecution is called just before the execution of the whole graph will be done.
@@ -363,7 +369,7 @@ class ExecutionGroup {
* \param rect: the rect of that chunk
* \see determineChunkRect
*/
MemoryBuffer *allocateOutputBuffer(int chunkNumber, rcti *rect);
MemoryBuffer *allocateOutputBuffer(rcti &rect);
/**
* \brief after a chunk is executed the needed resources can be freed or unlocked.
@@ -396,14 +402,6 @@ class ExecutionGroup {
*/
void execute(ExecutionSystem *graph);
/**
* \brief this method determines the MemoryProxy's where this execution group depends on.
* \note After this method determineDependingAreaOfInterest can be called to determine
* \note the area of the MemoryProxy.creator that has to be executed.
* \param memoryProxies: result
*/
void determineDependingMemoryProxies(std::vector<MemoryProxy *> *memoryProxies);
/**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk.
* \note Only gives useful results after the determination of the chunksize

View File

@@ -177,10 +177,10 @@ void ExecutionSystem::execute()
WorkScheduler::start(this->m_context);
execute_groups(COM_PRIORITY_HIGH);
execute_groups(CompositorPriority::High);
if (!this->getContext().isFastCalculation()) {
execute_groups(COM_PRIORITY_MEDIUM);
execute_groups(COM_PRIORITY_LOW);
execute_groups(CompositorPriority::Medium);
execute_groups(CompositorPriority::Low);
}
WorkScheduler::finish();

View File

@@ -64,8 +64,8 @@ class ExecutionGroup;
* \see NodeOperation base class for all operations in the system
*
* \section EM_Step3 Step3: add additional conversions to the operation system
* - Data type conversions: the system has 3 data types COM_DT_VALUE, COM_DT_VECTOR,
* COM_DT_COLOR. The user can connect a Value socket to a color socket. As values are ordered
* - Data type conversions: the system has 3 data types DataType::Value, DataType::Vector,
* DataType::Color. The user can connect a Value socket to a color socket. As values are ordered
* differently than colors a conversion happens.
*
* - Image size conversions: the system can automatically convert when resolutions do not match.

View File

@@ -23,88 +23,53 @@
static unsigned int determine_num_channels(DataType datatype)
{
switch (datatype) {
case COM_DT_VALUE:
case DataType::Value:
return COM_NUM_CHANNELS_VALUE;
case COM_DT_VECTOR:
case DataType::Vector:
return COM_NUM_CHANNELS_VECTOR;
case COM_DT_COLOR:
case DataType::Color:
default:
return COM_NUM_CHANNELS_COLOR;
}
}
unsigned int MemoryBuffer::determineBufferSize()
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state)
{
return getWidth() * getHeight();
}
int MemoryBuffer::getWidth() const
{
return this->m_width;
}
int MemoryBuffer::getHeight() const
{
return this->m_height;
}
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, unsigned int chunkNumber, rcti *rect)
{
BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax);
this->m_width = BLI_rcti_size_x(&this->m_rect);
this->m_height = BLI_rcti_size_y(&this->m_rect);
m_rect = rect;
this->m_memoryProxy = memoryProxy;
this->m_chunkNumber = chunkNumber;
this->m_num_channels = determine_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * determineBufferSize() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = COM_MB_ALLOCATED;
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
}
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, rcti *rect)
MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect)
{
BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax);
this->m_width = BLI_rcti_size_x(&this->m_rect);
this->m_height = BLI_rcti_size_y(&this->m_rect);
this->m_memoryProxy = memoryProxy;
this->m_chunkNumber = -1;
this->m_num_channels = determine_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * determineBufferSize() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = COM_MB_TEMPORARILY;
this->m_datatype = memoryProxy->getDataType();
}
MemoryBuffer::MemoryBuffer(DataType dataType, rcti *rect)
{
BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax);
this->m_width = BLI_rcti_size_x(&this->m_rect);
this->m_height = BLI_rcti_size_y(&this->m_rect);
this->m_height = this->m_rect.ymax - this->m_rect.ymin;
m_rect = rect;
this->m_memoryProxy = nullptr;
this->m_chunkNumber = -1;
this->m_num_channels = determine_num_channels(dataType);
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * determineBufferSize() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = COM_MB_TEMPORARILY;
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = MemoryBufferState::Temporary;
this->m_datatype = dataType;
}
MemoryBuffer *MemoryBuffer::duplicate()
MemoryBuffer::MemoryBuffer(const MemoryBuffer &src)
: MemoryBuffer(src.m_memoryProxy, src.m_rect, MemoryBufferState::Temporary)
{
MemoryBuffer *result = new MemoryBuffer(this->m_memoryProxy, &this->m_rect);
memcpy(result->m_buffer,
this->m_buffer,
this->determineBufferSize() * this->m_num_channels * sizeof(float));
return result;
}
void MemoryBuffer::clear()
{
memset(this->m_buffer, 0, this->determineBufferSize() * this->m_num_channels * sizeof(float));
memcpy(m_buffer, src.m_buffer, buffer_len() * m_num_channels * sizeof(float));
}
float MemoryBuffer::getMaximumValue()
void MemoryBuffer::clear()
{
memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float));
}
float MemoryBuffer::get_max_value() const
{
float result = this->m_buffer[0];
const unsigned int size = this->determineBufferSize();
const unsigned int size = this->buffer_len();
unsigned int i;
const float *fp_src = this->m_buffer;
@@ -119,19 +84,17 @@ float MemoryBuffer::getMaximumValue()
return result;
}
float MemoryBuffer::getMaximumValue(rcti *rect)
float MemoryBuffer::get_max_value(const rcti &rect) const
{
rcti rect_clamp;
/* first clamp the rect by the bounds or we get un-initialized values */
BLI_rcti_isect(rect, &this->m_rect, &rect_clamp);
BLI_rcti_isect(&rect, &this->m_rect, &rect_clamp);
if (!BLI_rcti_is_empty(&rect_clamp)) {
MemoryBuffer *temp = new MemoryBuffer(this->m_datatype, &rect_clamp);
temp->copyContentFrom(this);
float result = temp->getMaximumValue();
delete temp;
return result;
MemoryBuffer temp_buffer(this->m_datatype, rect_clamp);
temp_buffer.fill_from(*this);
return temp_buffer.get_max_value();
}
BLI_assert(0);
@@ -146,28 +109,23 @@ MemoryBuffer::~MemoryBuffer()
}
}
void MemoryBuffer::copyContentFrom(MemoryBuffer *otherBuffer)
void MemoryBuffer::fill_from(const MemoryBuffer &src)
{
if (!otherBuffer) {
BLI_assert(0);
return;
}
unsigned int otherY;
unsigned int minX = MAX2(this->m_rect.xmin, otherBuffer->m_rect.xmin);
unsigned int maxX = MIN2(this->m_rect.xmax, otherBuffer->m_rect.xmax);
unsigned int minY = MAX2(this->m_rect.ymin, otherBuffer->m_rect.ymin);
unsigned int maxY = MIN2(this->m_rect.ymax, otherBuffer->m_rect.ymax);
unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin);
unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax);
unsigned int minY = MAX2(this->m_rect.ymin, src.m_rect.ymin);
unsigned int maxY = MIN2(this->m_rect.ymax, src.m_rect.ymax);
int offset;
int otherOffset;
for (otherY = minY; otherY < maxY; otherY++) {
otherOffset = ((otherY - otherBuffer->m_rect.ymin) * otherBuffer->m_width + minX -
otherBuffer->m_rect.xmin) *
otherOffset = ((otherY - src.m_rect.ymin) * src.getWidth() + minX - src.m_rect.xmin) *
this->m_num_channels;
offset = ((otherY - this->m_rect.ymin) * this->m_width + minX - this->m_rect.xmin) *
offset = ((otherY - this->m_rect.ymin) * getWidth() + minX - this->m_rect.xmin) *
this->m_num_channels;
memcpy(&this->m_buffer[offset],
&otherBuffer->m_buffer[otherOffset],
&src.m_buffer[otherOffset],
(maxX - minX) * this->m_num_channels * sizeof(float));
}
}
@@ -176,7 +134,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
const int offset = (this->m_width * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
this->m_num_channels;
memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels);
}
@@ -186,7 +144,7 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
const int offset = (this->m_width * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
this->m_num_channels;
float *dst = &this->m_buffer[offset];
const float *src = color;
@@ -204,7 +162,7 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4]
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
BLI_assert(this->m_datatype == COM_DT_COLOR);
BLI_assert(this->m_datatype == DataType::Color);
float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
/* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
* but compositor uses pixel space. For now let's just divide the values and

View File

@@ -31,21 +31,19 @@ class MemoryBuffer;
* \brief state of a memory buffer
* \ingroup Memory
*/
typedef enum MemoryBufferState {
enum class MemoryBufferState {
/** \brief memory has been allocated on creator device and CPU machine,
* but kernel has not been executed */
COM_MB_ALLOCATED = 1,
/** \brief memory is available for use, content has been created */
COM_MB_AVAILABLE = 2,
Default = 0,
/** \brief chunk is consolidated from other chunks. special state.*/
COM_MB_TEMPORARILY = 6,
} MemoryBufferState;
Temporary = 6,
};
typedef enum MemoryBufferExtend {
COM_MB_CLIP,
COM_MB_EXTEND,
COM_MB_REPEAT,
} MemoryBufferExtend;
enum class MemoryBufferExtend {
Clip,
Extend,
Repeat,
};
class MemoryProxy;
@@ -60,7 +58,7 @@ class MemoryBuffer {
MemoryProxy *m_memoryProxy;
/**
* \brief the type of buffer COM_DT_VALUE, COM_DT_VECTOR, COM_DT_COLOR
* \brief the type of buffer DataType::Value, DataType::Vector, DataType::Color
*/
DataType m_datatype;
@@ -69,12 +67,6 @@ class MemoryBuffer {
*/
rcti m_rect;
/**
* brief refers to the chunk-number within the execution-group where related to the MemoryProxy
* \see memoryProxy
*/
unsigned int m_chunkNumber;
/**
* \brief state of the buffer
*/
@@ -89,41 +81,30 @@ class MemoryBuffer {
* \brief the number of channels of a single value in the buffer.
* For value buffers this is 1, vector 3 and color 4
*/
unsigned int m_num_channels;
int m_width;
int m_height;
uint8_t m_num_channels;
public:
/**
* \brief construct new MemoryBuffer for a chunk
* \brief construct new temporarily MemoryBuffer for an area
*/
MemoryBuffer(MemoryProxy *memoryProxy, unsigned int chunkNumber, rcti *rect);
MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state);
/**
* \brief construct new temporarily MemoryBuffer for an area
*/
MemoryBuffer(MemoryProxy *memoryProxy, rcti *rect);
MemoryBuffer(DataType datatype, const rcti &rect);
/**
* \brief construct new temporarily MemoryBuffer for an area
* Copy constructor
*/
MemoryBuffer(DataType datatype, rcti *rect);
MemoryBuffer(const MemoryBuffer &src);
/**
* \brief destructor
*/
~MemoryBuffer();
/**
* \brief read the ChunkNumber of this MemoryBuffer
*/
unsigned int getChunkNumber()
{
return this->m_chunkNumber;
}
unsigned int get_num_channels()
uint8_t get_num_channels()
{
return this->m_num_channels;
}
@@ -137,25 +118,17 @@ class MemoryBuffer {
return this->m_buffer;
}
/**
* \brief after execution the state will be set to available by calling this method
*/
void setCreatedState()
{
this->m_state = COM_MB_AVAILABLE;
}
inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
{
int w = this->m_width;
int h = this->m_height;
const int w = getWidth();
const int h = getHeight();
x = x - m_rect.xmin;
y = y - m_rect.ymin;
switch (extend_x) {
case COM_MB_CLIP:
case MemoryBufferExtend::Clip:
break;
case COM_MB_EXTEND:
case MemoryBufferExtend::Extend:
if (x < 0) {
x = 0;
}
@@ -163,15 +136,15 @@ class MemoryBuffer {
x = w;
}
break;
case COM_MB_REPEAT:
case MemoryBufferExtend::Repeat:
x = (x >= 0.0f ? (x % w) : (x % w) + w);
break;
}
switch (extend_y) {
case COM_MB_CLIP:
case MemoryBufferExtend::Clip:
break;
case COM_MB_EXTEND:
case MemoryBufferExtend::Extend:
if (y < 0) {
y = 0;
}
@@ -179,7 +152,7 @@ class MemoryBuffer {
y = h;
}
break;
case COM_MB_REPEAT:
case MemoryBufferExtend::Repeat:
y = (y >= 0.0f ? (y % h) : (y % h) + h);
break;
}
@@ -190,15 +163,15 @@ class MemoryBuffer {
MemoryBufferExtend extend_x,
MemoryBufferExtend extend_y)
{
float w = (float)this->m_width;
float h = (float)this->m_height;
const float w = (float)getWidth();
const float h = (float)getHeight();
x = x - m_rect.xmin;
y = y - m_rect.ymin;
switch (extend_x) {
case COM_MB_CLIP:
case MemoryBufferExtend::Clip:
break;
case COM_MB_EXTEND:
case MemoryBufferExtend::Extend:
if (x < 0) {
x = 0.0f;
}
@@ -206,15 +179,15 @@ class MemoryBuffer {
x = w;
}
break;
case COM_MB_REPEAT:
case MemoryBufferExtend::Repeat:
x = fmodf(x, w);
break;
}
switch (extend_y) {
case COM_MB_CLIP:
case MemoryBufferExtend::Clip:
break;
case COM_MB_EXTEND:
case MemoryBufferExtend::Extend:
if (y < 0) {
y = 0.0f;
}
@@ -222,7 +195,7 @@ class MemoryBuffer {
y = h;
}
break;
case COM_MB_REPEAT:
case MemoryBufferExtend::Repeat:
y = fmodf(y, h);
break;
}
@@ -231,11 +204,11 @@ class MemoryBuffer {
inline void read(float *result,
int x,
int y,
MemoryBufferExtend extend_x = COM_MB_CLIP,
MemoryBufferExtend extend_y = COM_MB_CLIP)
MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
{
bool clip_x = (extend_x == COM_MB_CLIP && (x < m_rect.xmin || x >= m_rect.xmax));
bool clip_y = (extend_y == COM_MB_CLIP && (y < m_rect.ymin || y >= m_rect.ymax));
bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < m_rect.xmin || x >= m_rect.xmax));
bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < m_rect.ymin || y >= m_rect.ymax));
if (clip_x || clip_y) {
/* clip result outside rect is zero */
memset(result, 0, this->m_num_channels * sizeof(float));
@@ -244,7 +217,7 @@ class MemoryBuffer {
int u = x;
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
const int offset = (this->m_width * y + x) * this->m_num_channels;
const int offset = (getWidth() * y + x) * this->m_num_channels;
float *buffer = &this->m_buffer[offset];
memcpy(result, buffer, sizeof(float) * this->m_num_channels);
}
@@ -253,19 +226,19 @@ class MemoryBuffer {
inline void readNoCheck(float *result,
int x,
int y,
MemoryBufferExtend extend_x = COM_MB_CLIP,
MemoryBufferExtend extend_y = COM_MB_CLIP)
MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
{
int u = x;
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
const int offset = (this->m_width * v + u) * this->m_num_channels;
const int offset = (getWidth() * v + u) * this->m_num_channels;
BLI_assert(offset >= 0);
BLI_assert(offset < this->determineBufferSize() * this->m_num_channels);
BLI_assert(!(extend_x == COM_MB_CLIP && (u < m_rect.xmin || u >= m_rect.xmax)) &&
!(extend_y == COM_MB_CLIP && (v < m_rect.ymin || v >= m_rect.ymax)));
BLI_assert(offset < this->buffer_len() * this->m_num_channels);
BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < m_rect.xmin || u >= m_rect.xmax)) &&
!(extend_y == MemoryBufferExtend::Clip && (v < m_rect.ymin || v >= m_rect.ymax)));
float *buffer = &this->m_buffer[offset];
memcpy(result, buffer, sizeof(float) * this->m_num_channels);
}
@@ -275,26 +248,26 @@ class MemoryBuffer {
inline void readBilinear(float *result,
float x,
float y,
MemoryBufferExtend extend_x = COM_MB_CLIP,
MemoryBufferExtend extend_y = COM_MB_CLIP)
MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
{
float u = x;
float v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
if ((extend_x != COM_MB_REPEAT && (u < 0.0f || u >= this->m_width)) ||
(extend_y != COM_MB_REPEAT && (v < 0.0f || v >= this->m_height))) {
if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) ||
(extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) {
copy_vn_fl(result, this->m_num_channels, 0.0f);
return;
}
BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
result,
this->m_width,
this->m_height,
getWidth(),
getHeight(),
this->m_num_channels,
u,
v,
extend_x == COM_MB_REPEAT,
extend_y == COM_MB_REPEAT);
extend_x == MemoryBufferExtend::Repeat,
extend_y == MemoryBufferExtend::Repeat);
}
void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
@@ -304,7 +277,7 @@ class MemoryBuffer {
*/
inline bool isTemporarily() const
{
return this->m_state == COM_MB_TEMPORARILY;
return this->m_state == MemoryBufferState::Temporary;
}
/**
@@ -314,38 +287,45 @@ class MemoryBuffer {
* \note take care when running this on a new buffer since it wont fill in
* uninitialized values in areas where the buffers don't overlap.
*/
void copyContentFrom(MemoryBuffer *otherBuffer);
void fill_from(const MemoryBuffer &src);
/**
* \brief get the rect of this MemoryBuffer
*/
rcti *getRect()
const rcti &get_rect() const
{
return &this->m_rect;
return this->m_rect;
}
/**
* \brief get the width of this MemoryBuffer
*/
int getWidth() const;
const int getWidth() const
{
return BLI_rcti_size_x(&m_rect);
}
/**
* \brief get the height of this MemoryBuffer
*/
int getHeight() const;
const int getHeight() const
{
return BLI_rcti_size_y(&m_rect);
}
/**
* \brief clear the buffer. Make all pixels black transparent.
*/
void clear();
MemoryBuffer *duplicate();
float getMaximumValue();
float getMaximumValue(rcti *rect);
float get_max_value() const;
float get_max_value(const rcti &rect) const;
private:
unsigned int determineBufferSize();
const int buffer_len() const
{
return getWidth() * getHeight();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")

View File

@@ -33,7 +33,7 @@ void MemoryProxy::allocate(unsigned int width, unsigned int height)
result.ymin = 0;
result.ymax = height;
this->m_buffer = new MemoryBuffer(this, 1, &result);
this->m_buffer = new MemoryBuffer(this, result, MemoryBufferState::Default);
}
void MemoryProxy::free()

View File

@@ -45,12 +45,12 @@ Node::Node(bNode *editorNode, bool create_sockets)
if (create_sockets) {
bNodeSocket *input = (bNodeSocket *)editorNode->inputs.first;
while (input != nullptr) {
DataType dt = COM_DT_VALUE;
DataType dt = DataType::Value;
if (input->type == SOCK_RGBA) {
dt = COM_DT_COLOR;
dt = DataType::Color;
}
if (input->type == SOCK_VECTOR) {
dt = COM_DT_VECTOR;
dt = DataType::Vector;
}
this->addInputSocket(dt, input);
@@ -58,12 +58,12 @@ Node::Node(bNode *editorNode, bool create_sockets)
}
bNodeSocket *output = (bNodeSocket *)editorNode->outputs.first;
while (output != nullptr) {
DataType dt = COM_DT_VALUE;
DataType dt = DataType::Value;
if (output->type == SOCK_RGBA) {
dt = COM_DT_COLOR;
dt = DataType::Color;
}
if (output->type == SOCK_VECTOR) {
dt = COM_DT_VECTOR;
dt = DataType::Vector;
}
this->addOutputSocket(dt, output);
@@ -158,21 +158,21 @@ void NodeInput::setLink(NodeOutput *link)
m_link = link;
}
float NodeInput::getEditorValueFloat()
float NodeInput::getEditorValueFloat() const
{
PointerRNA ptr;
RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr);
return RNA_float_get(&ptr, "default_value");
}
void NodeInput::getEditorValueColor(float *value)
void NodeInput::getEditorValueColor(float *value) const
{
PointerRNA ptr;
RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr);
return RNA_float_get_array(&ptr, "default_value", value);
}
void NodeInput::getEditorValueVector(float *value)
void NodeInput::getEditorValueVector(float *value) const
{
PointerRNA ptr;
RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr);

View File

@@ -281,9 +281,9 @@ class NodeInput {
return m_link;
}
float getEditorValueFloat();
void getEditorValueColor(float *value);
void getEditorValueVector(float *value);
float getEditorValueFloat() const;
void getEditorValueColor(float *value) const;
void getEditorValueVector(float *value) const;
};
/**

View File

@@ -188,7 +188,8 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink
if (!(b_nodelink->flag & NODE_LINK_VALID)) {
return;
}
if ((b_nodelink->fromsock->flag & SOCK_UNAVAIL) || (b_nodelink->tosock->flag & SOCK_UNAVAIL)) {
if ((b_nodelink->fromsock->flag & SOCK_UNAVAIL) || (b_nodelink->tosock->flag & SOCK_UNAVAIL) ||
(b_nodelink->flag & NODE_LINK_MUTED)) {
return;
}

Some files were not shown because too many files have changed in this diff Show More