1
1

Compare commits

...

324 Commits

Author SHA1 Message Date
7030e3a016 Splines: Fix curve to mesh node issues
- The expected flat axis of profile objects is now the Z axis, like
    the existing curve objects.
 - Simplify/speed up the "sharp edge" marking logic.
2021-04-26 15:49:51 -05:00
936dfae3de Merge branch 'master' into geometry-nodes-curve-support 2021-04-26 15:14:32 -05:00
7e17dbfcf3 Cleanup: Move displist.cc to C++
This is a change split from the geometry nodes curves branch. Since
curve object modifier evaluation happens in this file, moving it to C++
will be quite helpful to support the `GeometrySet` type. Other than
that, the code in the branch intends to replace a fair amount of this
file anyway, so I don't plan to do any further cleanup here.

Differential Revision: https://developer.blender.org/D11078
2021-04-26 15:11:53 -05:00
e032ca2e25 Cleanup: Replace modifyVolume with modifyGeometrySet
This allows us to remove a callback from the modifier type info struct.
In the future the these modifiers might just be replaced by nodes
internally anyway, but in the meantime it's nice to unify the handling
of evaluated geometry a bit.

Differential Revision: https://developer.blender.org/D11080
2021-04-26 14:42:03 -05:00
399aed9a81 Splines: Fix interpolation for bezier splines
Also parallelize some of the calculations, since I was reworking
how the mapping was calculated anyway. I still haven't tuned the
grain size.
2021-04-26 14:17:25 -05:00
a7bda03516 Fix T87842: Outliner in Blender File mode has large performance impact
The Outliner was doing a full rebuild of its tree in response to transform
notifiers. I don't see any reason for this, a simple redraw without rebuilding
should be just fine.
The same optimization could be done for other object notifiers, but I'll check
on them separately.
2021-04-26 19:30:31 +02:00
607cd463b3 Merge branch 'blender-v2.93-release' 2021-04-26 19:21:24 +02:00
49b3d00c10 Fix: crash after recent commit
This was a mistake in rBb67fe05d4bea2d3c9efbd127e9d9dc3a897e89e6.
2021-04-26 19:19:44 +02:00
eb0d680851 Merge branch 'blender-v2.93-release' 2021-04-26 18:05:10 +02:00
38bfa8902b Fix T87771: Immediate Crash on "Edit Source" in Compositor
Caused by {rB278011e44d43}.

Framebuffer management since above commit now seems to require region
bind/unbind in for the operator to be able to redraw correctly without
using the same framebuffer in multiple contexts.

Maniphest Tasks: T87771

Differential Revision: https://developer.blender.org/D11084
2021-04-26 18:00:26 +02:00
c266632ff6 LineArt: UI fixes to match the content for 2.93 manual.
Reviewed by: Sebastian Parborg

Differential Revision: https://developer.blender.org/D11089
2021-04-26 23:40:46 +08:00
d89d53b453 Merge branch 'blender-v2.93-release' 2021-04-26 16:44:06 +02:00
b67fe05d4b Depsgraph: support depending on collection geometry
This fixes T87666 and T83252.

The boolean modifier and geometry nodes can depend on the geometry
of an entire collection. Before, the modifiers had to manually create relations
to all the objects in the collection. This worked for the most part, but was
cumbersome and did not solve all issues. For example, the modifiers were not
properly updated when objects were added/removed from the referenced collection.

This commit introduces the concept of "collection geometry" in the depsgraph.
The geometry of a collection depends on the transforms and geometry of all
the objects in it. The boolean modifier and geometry nodes can now just depend
on the collection geometry instead of creating all the dependencies themselves.

Differential Revision: https://developer.blender.org/D11053
2021-04-26 16:35:22 +02:00
bac9562f15 Cleanup: spelling 2021-04-26 22:58:35 +10:00
8f03c9b19a Cleanup: compiler warning 2021-04-26 22:58:35 +10:00
e12a8aedce Cleanup: remove use of deprecated uint32, utin16 types 2021-04-26 22:58:35 +10:00
94960250b5 Cycles: Fix build with OptiX 7.3 SDK 2021-04-26 14:55:39 +02:00
d7c762d2f7 Cleanup: Rearrange the functions in 'transform_convert_mesh.c'
The creation of `TransCustomData` is best located at the beginning of the
file as it is a specific struct of the file and can be used a lot locally.
2021-04-26 09:35:03 -03:00
Erik Abrahamsson
a65d5dadeb DeprecationWarning fix
This gets rid of a `DeprecationWarning` in bpy_types.py caused by invalid escape sequences.
More info here: https://docs.python.org/3/library/re.html

Reviewed By: mont29

Differential Revision: https://developer.blender.org/D10998
2021-04-26 09:47:03 +02:00
9b64927a8a Merge branch 'blender-v2.93-release' 2021-04-26 09:16:53 +02:00
a17ea1a669 Spreadsheet: combine vector/color spreadsheet columns
Differential Revision: https://developer.blender.org/D11056
2021-04-26 09:09:50 +02:00
c63142ca8f Remove print during compilation. 2021-04-26 07:52:46 +02:00
e7c1702ccb Splines: Cleanup, add comments, formatting 2021-04-25 23:29:28 -05:00
a200a8cf59 Cleanup: inconsistent naming for screen editing variables & args 2021-04-26 13:47:04 +10:00
149078d580 Merge branch 'master' into geometry-nodes-curve-support 2021-04-25 21:45:33 -05:00
36c4b79c30 Merge branch 'blender-v2.93-release' 2021-04-25 21:45:02 -05:00
f2d70c02f8 Fix (unreported): Geometry nodes instance ids not copied or cleared
Though to my knowledge we haven't had a report about this yet, this
looks like a clear oversight-- the ids are just more data stored by the
instances component, and should be cleared and copied like other data.

This might have resulted in incorrect random IDs for instances in
renderers in some cases where the component had to be copied.
2021-04-25 21:42:14 -05:00
47b1f9a4d3 Splines: Cleanup, add comment 2021-04-25 21:37:04 -05:00
8709d0cd5d Splines: Rename DCurve to SplineGroup 2021-04-25 21:26:00 -05:00
Pratik Borhade
5341482064 Fix T87777: Fix typo in Prefs Scripts Dir tooltip
Correction to Prefs tooltip mention of 'add-ons' folder in Scripts Dir.

Differential Revision: https://developer.blender.org/D11064

Reviewed by Harley Acheson
2021-04-25 18:39:32 -07:00
c76141e425 UI: Adding Constraint to the Area Join Operations
Removing mid-operation area re-targeting for safety and predictability.

Differential Revision: https://developer.blender.org/D11066

Reviewed by Campbell Barton
2021-04-25 17:55:00 -07:00
db3b610040 Fix T87170 Multi-Input socket activation zone error with lots of inputs
Calculation of bounding rect for multi-input socket was wrong.

Reviewer: Hans Goudey (HooglyBoogly)

Differential Revision: https://developer.blender.org/D11077
2021-04-26 00:55:55 +02:00
c204e0c385 Fix T87170 Multi-Input socket activation zone error with lots of inputs
Calculation of bounding rect for multi-input socket was wrong.

 Reviewer: Hans Goudey (HooglyBoogly)

 Differential Revision: https://developer.blender.org/D11077
2021-04-25 23:32:41 +02:00
46bfc3ec46 Splines: Add bounding box node support 2021-04-25 15:50:46 -05:00
a29b9a73d2 Splines: Cleanup: Remove another unecessary change 2021-04-25 15:21:02 -05:00
39f21c2475 Splines: Cleanup, remove more unecessary changes 2021-04-25 15:10:17 -05:00
87036f5eeb Merge branch 'master' into geometry-nodes-curve-support 2021-04-25 15:07:48 -05:00
16b2b33d01 BLI: Add "first" method to MutableSpan and Vector
This is convenient because having a uniform interface is nice, and
because of the similarity to "last".

Differential Revision: https://developer.blender.org/D11076
2021-04-25 15:06:38 -05:00
9636e79c5a Splines: Reset unecessary geometry nodes changes 2021-04-25 15:01:19 -05:00
e1c04da36f Merge branch 'master' into geometry-nodes-curve-support 2021-04-25 13:40:03 -05:00
45bac491eb Splines: Fix curve lookup (for trim node) 2021-04-25 12:43:03 -05:00
20142b0823 Merge branch 'blender-v2.93-release' 2021-04-25 18:40:04 +02:00
Erik Abrahamsson
f1cf706758 Fix unreported "Convert Mesh to Gpencil" when fill material already exists
If an object named for example `Suzanne` is converted to Gpencil, a material called `Suzanne_Fill` will be created for the gpencil fill.
When this material already exists, the new material will be called `Suzanne_Fill.001` and the operator will not see that this material is already present the next iteration. This leads to a new material being created for every polygon.

This commit changes the code to search for a material starting with `ObjectName_Fill` instead of being equal to.

Reviewed By: filedescriptor, antoniov

Differential Revision: https://developer.blender.org/D11067
2021-04-25 18:31:36 +02:00
ae58db91a2 Fix T87799: Crash when switching to Info Editor
Use of uninitialized variable.

Problem introduced in 87a70801c6.
2021-04-25 10:41:28 -03:00
87a70801c6 Fix build issue due to previous commit
Revert in rB05dddb71b098 did a poor job and left some
rubbish.
2021-04-24 18:18:43 -06:00
05dddb71b0 Revert "Info Editor: move to c++"
This reverts commit 9cce18a585.

rB9cce18a5858c broke the build in unforeseen ways, not easily fixable.
revert for now, so master will at least build again.
2021-04-24 18:14:15 -06:00
837d630466 Splines: Halve the size of the bezier interpolation cache
Use floats to store the control point indices and the interpolation factors.
2021-04-24 15:02:06 -05:00
23eb05eba5 Merge branch 'master' into geometry-nodes-curve-support 2021-04-24 14:17:28 -05:00
3a6f481d5a Fix windows build: Don't use designated initializers in C++
Removes a cast in an argument followed by an initializer list, and
designated initializers, which are only part of the C++20 standard.
2021-04-24 14:15:57 -05:00
c96f9bd340 Fix T87682 Boolean Exact crash.
The triangulator I made (using CDT) doesn't work if the face
self-intersects. Fall back to the polyfill triangulator when
that happens.
2021-04-24 14:48:44 -04:00
f7afd78b80 Fix T87682 Boolean Exact crash.
The triangulator I made (using CDT) doesn't work if the face
self-intersects. Fall back to the polyfill triangulator when
that happens.
2021-04-24 14:46:48 -04:00
9cce18a585 Info Editor: move to c++
I'm currently doing some experiments in the info editor and
for that it is easier if the info editor is in c++ already.
2021-04-24 17:00:37 +02:00
82328797cf Cleanup: Remove unused flag 2021-04-24 11:46:41 -03:00
b6542f71e1 Cleanup/Refactor: Unify similar flags 2021-04-24 11:46:41 -03:00
f8d5d03ebc Cleanup/Refactor: Move FOREACH_TRANS_DATA_CONTAINER to outside of some functions
So we can reuse the same loop for different corrections.
2021-04-24 11:46:07 -03:00
b06d9069ad Cleanup: Add a common prefix for some transform functions 2021-04-24 11:46:06 -03:00
c2a0ca622b Cleanup: Move transform recaldata functions to their respective files 2021-04-24 11:35:05 -03:00
536a2fe7cc Cleanup: Add a common prefix for some transform functions 2021-04-24 11:35:05 -03:00
86ff35c7fc Merge branch 'blender-v2.93-release' 2021-04-24 23:52:15 +10:00
0f1ee611d4 Fix T85567: Crash accessing gizmo group on __del__
- Re-order freeing so an instances __del__ method runs before the
  `ExtensionRNA` has been freed.

- "remove" functions no longer free the gizmo/gizmo-group memory,
  needed so the identifier used when freeing the extension
  doesn't use the freed identifier.
2021-04-24 23:37:32 +10:00
17fca62fff Merge branch 'blender-v2.93-release' into master 2021-04-24 01:55:49 +05:30
aa95f8019e macOS: Fix unknown -Wsuggest-override warning
Added in rB7cef01b090c4c2d2703274edb91886ae37d3ce82
and rB87bfa2b207b90b5e34ebd835a23c2a82afbed878

Reviewed by: jbakker, #platform_macos
Differential Revision: https://developer.blender.org/D11012
2021-04-24 01:54:30 +05:30
12951aecf6 Merge branch 'master' into geometry-nodes-curve-support 2021-04-23 13:51:57 -05:00
2e513afe80 GPencil: Fix unreported reproject error
This bug was introduced in e1acefd45e
2021-04-23 17:42:56 +02:00
e4cebec647 GPencil: Add Multiframe support to Move to Layer
Reviewed By: mendio

Maniphest Tasks: T87426

Differential Revision: https://developer.blender.org/D11013
2021-04-23 17:27:27 +02:00
be68d00c36 GPencil: Change maximum value for Fill precission
The old value of 5 was not enough for some situations.
2021-04-23 16:58:40 +02:00
17afa86336 Merge branch 'blender-v2.93-release' 2021-04-23 09:56:50 -05:00
Leon Leno
c463675188 Fix T87749 (partially): Text jitter in buttons and spreadsheet editor
This patch fixes jittering text when resizing regions of the UI as
described in T87749. The jitter was caused by the text padding
being stored as an integer which lead to inconsistent rounding.

Most notably this patch fixes the jitter in the new spreadsheet editor,
but not all occurrences of jitter described in T87749 (e.g. in UI lists)
are addressed.

Differential Revision: https://developer.blender.org/D11060
2021-04-23 09:55:58 -05:00
8aa8537632 VSE: reset strip transform with "Set Render Size" operator
Reset the active strip offset and scale, in the "Set Render Size"
operator (`SEQUENCER_OT_rendersize`). This ensures that the active strip
will actually fit the new render size, which is what the operator is
intended to achieve.
2021-04-23 16:39:21 +02:00
5e509f966f Cleanup: VSE, reduce cognitive complexity of sequencer_rendersize_exec()
Reduce the cognitive complexity of the `sequencer_rendersize_exec()`
function by flipping some conditions and returning early.

No functional changes.
2021-04-23 16:28:37 +02:00
23185262ab Cleanup: clang-format 2021-04-24 00:03:14 +10:00
877711e9e4 Cleanup: use const arguments, reduce right shift 2021-04-24 00:02:44 +10:00
8adeac7c27 Merge branch 'blender-v2.93-release' 2021-04-23 14:35:40 +02:00
9afa738542 Fix bug/crash in ID bulk deletion code.
This is complex situation. Tagged ID deletion (used to delete several
data-blocks at once) removes IDs to be deleted from Main.

But when we remove deleted IDs' usages of other IDs (using
`BKE_libblock_relink_ex`), some specific post-process is required on
some types, like Collections. Those post-processes would in some cases
rely on data actually being in Main.

That failing condition would lead in existing code on missing processing
the very ID (collection) we were working on, leading to missing removing
some child collection pointers, leading to the crash (later on in
LayerCollection resync process).

For now we go with an optimization & fix that avoids processing all
collections in Main when we actually know which one we are working one
(case of `BKE_libblock_relink_ex`, but not of
`BKE_libblock_remap_locked`).

This is however yet another demonstration of the need to rework that
whole collection/layer resync process, since it is not only extremely
inneficient currently, but it also requires valid Main/ID state way too
deep into the remapping code.

NOTE: This fix may very well not catch/address all possible fail cases
here, dealing with the double parent/child relationships of collections
is challenging...

Issue reported by @eyecandy from the studio, thanks.
2021-04-23 14:34:47 +02:00
361579c86a Merge branch 'blender-v2.93-release' 2021-04-23 13:09:41 +02:00
5441f5fc90 Fix T87582: incorrect interpolation from edge to corner domain 2021-04-23 13:07:38 +02:00
4cb8438e08 Geometry Nodes: warn when output attribute has not been saved 2021-04-23 12:44:59 +02:00
f8e1526fa6 Fix T87661: Attribute Combine XYZ node can't overwrite existing attribute 2021-04-23 12:44:21 +02:00
ffa8563429 Merge branch 'blender-v2.93-release' 2021-04-23 10:17:25 +02:00
Jagannadhan Ravi
425e19bc1f Modifiers: Performance Simple Deformation
Use multiprocessing with simple deform modifiers.

Master 2.92 fps this patch 3.13 fps on Ryzen 1700X With Vega 64 GPU.

3970X: 2.85 fps -> 2.95 fps
3990X: 3.15 fps -> 3.41 fps
3995WX: 3.21 fps -> 3.38 fps

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D10609
2021-04-23 10:07:06 +02:00
Jeroen Bakker
2f6219c48d Fix T78845: Eevee wrong material selection.
In cases where the same node tree is used in different materials with
small changes, the wrong material could be selected.

Cause: Hair shaders GPU resources weren't updated and used the previous bound data.

Reviewed By: fclem

Differential Revision: https://developer.blender.org/D11036
2021-04-23 10:04:17 +02:00
dc1e98d8a0 Fix T86765: Custom properties are included in __dir__
Remove custom properties from `__dir__` method result since these
can't be accessed using `__getattr__`.

Introduced in the 2.5x Python API update.
2021-04-23 17:25:49 +10:00
cfa20ff03b Fix T86566: Do Not Close if File Cannot Be Saved
If saving a file using CloseSave dialog, do not close if there is an error doing so, like if read-only.

Differential Revision: https://developer.blender.org/D10722

Reviewed by Julian Eisel
2021-04-22 20:08:17 -07:00
8b049e4c2a UI: Join or Close Any Screen Area
Corner action zones allow joining any neighbors. New 'Area Close' operator. Improved Header Context Menu.

Differential Revision: https://developer.blender.org/D8084

Reviewed by Campbell Barton
2021-04-22 19:57:49 -07:00
5a33965627 Merge branch 'blender-v2.93-release' 2021-04-23 11:33:22 +10:00
0817763624 Fix for building with MSVC 2021-04-23 11:32:32 +10:00
76fcf58cdd Merge branch 'blender-v2.93-release' 2021-04-23 10:02:46 +10:00
a23e49c696 Fix T86170: Memory leak clearing the Python instance for COW id data
As Python can access COW ID's, ensure it's instance is kept on update.

This could happen when the "Use Self" argument was enabled for a driver.
2021-04-23 09:02:49 +10:00
3cb09f7a83 Cleanup: quiet clang-tidy warning 2021-04-23 09:02:46 +10:00
46a037707b Splines: Fix cache not recalculated error 2021-04-22 16:30:06 -05:00
f2948faf97 Splines: Reorganize class inheritence, evaluation 2021-04-22 16:14:23 -05:00
47892d6695 Fix too big grid plane for Add Object tool on Retina displays
The grid plane was drawn too big on retina displays compared to other screens,
because the factor was multiplied by the native pixel-size, which is 2 for
Retina displays.
2021-04-22 19:45:44 +02:00
6ebe0b8cf0 Assets: Correct name of own recently added BPY functions
Makes the functions (introduced in 557b3d2762) follow existing naming
conventions for the API.

Changes:
`bpy.types.ID.mark_asset` to `bpy.types.ID.asset_mark`
`bpy.types.ID.clear_asset` to `bpy.types.ID.asset_clear`
2021-04-22 19:26:09 +02:00
d7aa481231 Splines: Add comments 2021-04-22 12:24:52 -05:00
cd07fb6477 Splines: Continue cleanup 2021-04-22 12:15:18 -05:00
bbb52a462e Fix T87688: Crash entering valid text into number field
When the user entered a driver expression like `#frame` into a number
field, Blender would crash sometime after. This is because
e1a9ba94c5 did not handle this case properly and ignored the fact
that the user can enter text into the field.

The fix makes sure that we only cancel if the value is a *number* equal
to the previous value in the field.

Note that this could also be handled by `ui_but_string_set` which would
be a bit nicer potentially. However, I was unable to find a clean way of
passing `data->startvalue` into that function. `uiHandleButtonData` is
only known locally inside `interface_handlers.c`.

Reviewed By: Severin

Maniphest Tasks: T87688

Differential Revision: https://developer.blender.org/D11049
2021-04-22 19:02:48 +02:00
d0bdb2b48f Splines: Continue cleanup 2021-04-22 11:41:41 -05:00
35b0d177a2 Splines: Cleanup: Rename variables and types 2021-04-22 11:25:01 -05:00
441f8d98eb Splines: Cleanup 2021-04-22 11:15:37 -05:00
c21199a0ba Geometry Nodes Curves: Rename files 2021-04-22 09:37:53 -05:00
26d4864ee6 Merge branch 'master' into geometry-nodes-curve-support 2021-04-22 09:25:40 -05:00
d1ccc5b969 Geometry Nodes: Add initializer for attribute creation
Previously we always had to set attribute values after creating
the attribute. This patch adds an initializer argument to
`attribute_try_create` which can fill it in a few ways, which
are explained in code comments.

This fixes T87597.

Differential Revision: https://developer.blender.org/D11045
2021-04-22 09:20:03 -05:00
f240b5e5f7 Fix T87701: debug assert generating scene preview render 2021-04-22 15:23:48 +02:00
9fccbe2d13 Fix missing include 2021-04-22 10:09:15 -03:00
b9a7b40924 Geometry Nodes: Get attribute domain and type without allocation
Because we use virtual classes (and for other reasons), we had to do a
small allocation when simply retrieving the data type and domain of an
existing attribute. This happened quite a lot actually-- to determine
these values for result attributes.

This patch adds a simple function to retrieve this meta data without
building the virtual array. This should lower the overhead of every
attribute node, though the difference probably won't be noticible
unless a tree has very many nodes.

Differential Revision: https://developer.blender.org/D11047
2021-04-22 08:05:02 -05:00
e0852368bc Fix missing include 2021-04-22 10:04:59 -03:00
f6efacfec7 Merge branch 'blender-v2.93-release' 2021-04-22 09:58:52 -03:00
80536e8bae Fix T87615: No snapping to objects instanced by Geometry nodes
The code of the snapping system to interact the objects in the scene only
considers instances what comes from "DUPLI" objects.

This commit adds instances coming from Geometry nodes.

Differential Revision: https://developer.blender.org/D11020
2021-04-22 09:57:45 -03:00
c3701e2303 Merge branch 'blender-v2.93-release' 2021-04-22 14:26:29 +02:00
0ba0d27d36 Fix T87090: VSE scrubbing locks up blender
Speed effect caused, that some raw frames are re-used for multiple
final frames. When cached final frame is freed due to memory being
full, it tried to free also lower level cached frames that were used
during compositing. Some lower level cached frames were already freed
by different final frame and `BLI_ghash_remove()` failed.

Check if key still exists in hash or if linked keys were overwritten
before removing them.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D10909
2021-04-22 14:16:32 +02:00
8c146d5efc Merge branch 'blender-v2.93-release' 2021-04-22 12:49:34 +02:00
71db02ed88 Fix: geometry nodes logs incorrect preview data
Under some circumstances, modifiers are evaluated more than once.
One time to compute the actual output geometry and another time
with `MOD_APPLY_ORCO`. This design probably has to be revisited
at some point in the context of geometry nodes. However, that would
be much more involved than a bug fix.

The issue was that during the second evaluation, the node tree is
evaluated based on a slightly different input geometry. The data
generated during the second evaluation overwrote the cached
data from the first evaluation, resulting in incorrect data that is
shown in the spreadsheet.

The fix for now is to simply not log any data in the second evaluation.
2021-04-22 12:48:59 +02:00
1095d5964d Cleanup: doversion for 3.0 is using FileData 2021-04-22 12:19:01 +02:00
7192ab614b Animation: add "LocRotScaleCProp" keying set
Add a keying set that includes location, rotation, scale, and custom
properties.

The keying set is intentionally not based on the "Whole Character"
keying set. Instead, it is generic enough to be used in both object and
pose animation, making it possible to quickly switch between animating
characters and props without switching keying sets.

This is, according to @rikkert, what the Blender Studio animators need
99.9% of the time.
2021-04-22 10:33:07 +02:00
91c652e39a Fix: Deactivate audio settings when output is None 2021-04-22 10:23:02 +02:00
33d9a0c951 Merge branch 'blender-v2.93-release' 2021-04-22 16:33:37 +10:00
00ec99050e Fix T85051: Add bisect distance as a parameter to the mirror modifier
The `bisect_distance` in the mirror modifier was hard-coded to `0.001`.
This would result in some unexpected behavior like vertices close
to the mirror plane being deleted or merged.

The fix now adds a parameter to the mirror modifier to expose the
bisect distance to the user. The default is set to the previous
hard-coded value to not "change" previous files.

Ref D10201
2021-04-22 16:31:41 +10:00
a4bd0fcabf Merge branch 'blender-v2.93-release' 2021-04-22 08:25:40 +02:00
a43d644dec VSE: Fix movies are reloaded after invalidation
When cache is strip is invalidated, movie file was reloaded even if it
isn't necessary. This caused significant performance issues when strip
is being dragged under playhead.

This was caused by calling `SEQ_relations_sequence_free_anim()` and it
was introduced as fix for T36124.

When it is necessary to reload file because another API holds reference
to ImBuf, do this explicitly besides cache invalidation.

In `rna_ColorManagedColorspaceSettings_reload_update()` this was already
done, so no change is needed there.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11024
2021-04-22 08:16:52 +02:00
6944521d7e Fix T87337: Text strip draws white outline
Math for drawing font over byte buffer was incorrect. Effect can be seen
when target buffer is fully black and transparent - this results in font
color being effectively premultiplied, which causes problems when image
is composited further.

Use `blend_color_mix_byte()` and `blend_color_mix_float()` for blending.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11035
2021-04-22 08:16:16 +02:00
4554f27adf Fix T86682: Scene strip DOF not evaluated correctly
Camera object used for rendering reffered to original not evaluated data.

Use `DEG_get_evaluated_object()` to get evaluated camera.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D11039
2021-04-22 08:15:12 +02:00
da443d82ee Geometry Nodes Curves: Refactor NURBS sampling
This is basically a template for a similar thing I'll do to bezier
splines. The idea is that every spline type is responsible for the
mapping of values from orginal control points to the evaluated
points based on a given resolution, and then the base class will
handle the rest.

This commit leaves the branch in a half-refactored state.
2021-04-21 22:47:11 -05:00
48c5129d1f Fix errors in Buffer.dimensions setter
- Error accessing the length from a sequence.
- Error comparing a boolean to -1.

Issues introduced in 19360c2c1c
2021-04-22 12:34:26 +10:00
330c14c26c Merge branch 'master' into geometry-nodes-curve-support 2021-04-21 19:20:47 -05:00
932b23782a Merge branch 'blender-v2.93-release' 2021-04-21 17:15:46 -03:00
0f66dbea90 Fix wrong flag for 'GPUFrameBuffer.viewport_get'
`GPUFrameBuffer.viewport_get` has no args.
2021-04-21 17:15:13 -03:00
19360c2c1c Python GPU Buffer: Add a 'setter' to Buffer.dimensions
The attribute `Buffer.dimensions` does not need to be readonly.
2021-04-21 17:07:11 -03:00
ef9551afd1 Merge branch 'blender-v2.93-release' 2021-04-21 18:00:26 +02:00
aed9b6faee Fix some Cycles random walk SSS precision issues with small radius 2021-04-21 17:42:47 +02:00
3da74c1c18 Geometry Nodes: add method to get attribute by name and type
This is needed by the upcoming Attribute Transfer node. It changes
its behavior based on what domain the attribute is on.
2021-04-21 17:07:00 +02:00
1dd17726f2 Geometry Nodes: extract mesh surface sampling functions to separate file 2021-04-21 17:02:19 +02:00
d5309bf4cf Functions: add slice method for generic spans 2021-04-21 16:59:22 +02:00
b9cbf7fc80 Geometry Nodes: add utility to convert CPPType to static type 2021-04-21 16:57:43 +02:00
9fa9854e2a Merge branch 'blender-v2.93-release' 2021-04-21 16:22:40 +02:00
b1fcc8e6ba Fix T87659: crash when creating reroute loop in geometry nodes
This was a regression introduced in rB0ff3f96a1b0193d69dae5ca69e7c93d6cdf34558.
2021-04-21 16:21:54 +02:00
7aa6444a65 UI: Use proper close file dialog for "Restore Last Session"
Was showing a simple confirm dialog, even if the file didn't contain
unsaved changes. The confirm dialog would also show up when choosing
"Restore Last Session" from the splash screen right after startup, which
is weird.

Instead show the proper file closing dialog that allows saving, but only
if there are actually unsaved changes.
2021-04-21 15:18:05 +02:00
e9e2805573 Cleanup: Deduplicate file closing dialog logic
Was doing almost the same thing in two places, plus I need the same for
a third case. So better have a helper function for it.
2021-04-21 15:18:05 +02:00
0b458e8322 Cleanup: Create/use types for generic WM callbacks
Makes it less of a hassle to pass these callbacks around as function
arguments, and deduplicates their signature when doing so.
2021-04-21 15:18:05 +02:00
2e3f072d5d Merge branch 'blender-v2.93-release' 2021-04-21 13:14:26 +02:00
b72d9216f1 Fix T87677: Add primitive tools produce wrong sized objects after changing values in 'Adjust Last Operation'
Always use the defaults here (radius, depth etc), since desired bounds
have been set interactively, it does not make sense to use a different
value from a previous command.

The Cube tool has already seen a fix for this in rB26e5718e29a7, but
Cone/UVSphere/Cylinder/IcoSphere havent.

Maniphest Tasks: T87677

Differential Revision: https://developer.blender.org/D11038
2021-04-21 13:01:32 +02:00
183e3f6bb9 Fix T86968: Last UV factor in cyclic strokes
The UV factor of the last point of a cyclic stroke was using the factor of
the first point leading to unwanted scaling artifacts.

The fix sets the uv factor of the last point to the currect value (last UV
factor + length between last and first point).

Reviewed By: antoniov, fclem

Maniphest Tasks: T86968

Differential Revision: https://developer.blender.org/D10850
2021-04-21 12:16:32 +02:00
d17bff849d Merge branch 'blender-v2.93-release' 2021-04-21 11:19:57 +02:00
26ab90e49b Fix T87640: Grease Pencil layer visibility/locking not synchronized from Dope Sheet to Properties
Properties Editor is a bit more picky (compared to the Outliner for
example) when it comes to listening to grease pencil notifiers -- it
requires the action to be set.
So when adding the notifier in the dopesheet from the channels (done in
`achannel_setting_flush_widget_cb`), now add the `NA_EDITED` action.

Maniphest Tasks: T87640

Differential Revision: https://developer.blender.org/D11025
2021-04-21 11:15:53 +02:00
5fe3d2dc7d Merge branch 'blender-v2.93-release' 2021-04-21 11:05:40 +02:00
1a6b51e175 Curve modifiers: Disable 'Apply on Spline' option if not available
Some curve modifiers (namely Hook, SoftBody and MeshDeform) can only work
on pre-tesselated spline points.

Before the modifier UI refactor in rB9b099c86123f, users would get the
'Apply on Spline' option, but disabled and with a tip explaining why
this cant be changed. After rB9b099c86123f though, this button was
always enabled [but disfunctional] leaving the user without an
explanation why this has no effect.

Now restore this functionality since it is quite important information.
Additionally, this button now appears to be ON in these cases which
makes more sense from the user perspective (so it does not represent the
actual setting on the modifier -- this would internally be switched ON in
the modifier calculation anyways though, see
'curve_get_tessellate_point')

Differential Revision: https://developer.blender.org/D11029
2021-04-21 11:02:45 +02:00
79d2f2c2f9 Merge branch 'blender-v2.93-release' 2021-04-21 17:43:55 +10:00
f9867c1f11 Merge branch 'blender-v2.93-release' 2021-04-21 17:43:51 +10:00
a6cd20716e Fix T87562: Keymap preferences from 2.92x ignored
Renaming presets in 01d49d1542
caused them to be treated as separate key-maps.
2021-04-21 17:39:02 +10:00
06c682423a BLI_string: add a utility to replace strings using a table
Useful to simplify versioning code when identifiers need updating
in multiple places.
2021-04-21 17:34:18 +10:00
3003fbbecf Merge branch 'blender-v2.93-release' 2021-04-21 13:48:10 +10:00
5da3177190 Fix logging "bke.appdir" messages
Logging was initialized after BKE_appdir paths were initialized,
making it impossible to see paths which are checked on startup.
2021-04-21 13:44:23 +10:00
c5cabb6eb7 Correction to previous commit: Avoid unnecessary notifier
Not a big deal, but nice to avoid notifiers when no change was done.
2021-04-20 22:43:43 +02:00
557b3d2762 Assets: Add BPY function to mark and clear assets
Adds `mark_asset()` and `clear_asset()` to ID data-blocks, e.g.
`bpy.context.active_object.mark_asset()`. They essentially do the same as the
mark and clear asset operators.

Scripts are generally discouraged from using operators where possible, but we
need to provide API functions to use instead. In this case it means scripts
don't have to override context to pass an ID to the operator.
2021-04-20 22:36:48 +02:00
34542eede5 Merge branch 'master' into geometry-nodes-curve-support 2021-04-20 15:07:21 -05:00
2c73e16d1c Geometry Nodes Curves: Sample positions from NURBS curves 2021-04-20 14:38:46 -05:00
847579b422 Add support for building on Linux aarch64
Differential Revision: https://developer.blender.org/D10958
2021-04-20 18:45:46 +02:00
f2626f1420 Merge branch 'blender-v2.93-release' 2021-04-20 17:45:36 +02:00
3735986e87 Fix T87440 EEVEE: Specular Light Viewport Render Pass wrong behavior
This was caused by the closure refactor. The radiance being masked
for SSR, we need to not enable SSR when trying to render the specular
color pass.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D11028
2021-04-20 17:45:07 +02:00
f1b61f5e7d Core: Add do_versions file for 3.0
Differential Revision: https://developer.blender.org/D11030
2021-04-20 17:13:20 +02:00
e2af5030b9 Cleanup: comments
- Restore comment removed by accident in
  8c5c55b8c9
- Use doxygen syntax.
2021-04-21 00:51:27 +10:00
3d994b26ba EEVEE: Fix unreported memory leak when toggling AO on/off
The `filtered_radiance` is not a texture form the texture pool.
It needs manual freeing if not used.
2021-04-20 16:33:57 +02:00
2c1a2c9a99 Merge branch 'blender-v2.93-release' 2021-04-21 00:21:29 +10:00
fa7ddd0f43 Fix T86871: Crash with to_mesh() on tapered bezier curve 2021-04-21 00:20:47 +10:00
4cf60a2abf Fix T87369 EEVEE: Ambient Oclussion: Firefly caused by degenerated normal
This was caused by some sort of degenerated normals.
2021-04-20 16:07:45 +02:00
eca5cf1460 Cleanup: keying sets, move common code to mix-in class
Move code common to the Whole Character keying sets ("Whole Character" and
"Whole Character (Selected Bones Only)" into a mix-in class. This avoids
the need to use direct assignments like
`poll = BUILTIN_KSI_WholeCharacter.poll`.

No functional changes.
2021-04-20 15:52:43 +02:00
26d778cd8a Cleanup: keying sets, use self as self-parameter
Use `self` as self-parameter of methods, instead of the non-standard and
cryptic `ksi`.

No functional changes.
2021-04-20 15:52:43 +02:00
e1a9ba94c5 Fix T87637: Dragging button value cancel not working
Dragging a number button, then holding the value and pressing escape
would not reset the value correctly.
This was because eb06ccc324 assumed that `data->value` and
`data->startvalue` were set during dragging which they are not.

The fix moves the if statement into the section where we check if a
number was entered (number edit) making sure that we only cancel
if the button was in "string enter" mode and that the value entered
was the same as before.

Reviewed By: HooglyBoogly, Severin

Maniphest Tasks: T87637

Differential Revision: https://developer.blender.org/D11021
2021-04-20 15:46:52 +02:00
7834fcc67d Merge branch 'blender-v2.93-release' 2021-04-20 11:19:43 +02:00
2125ee4305 Geometry Nodes: skip attribute conversion if src and dst are the same 2021-04-20 11:17:35 +02:00
b2c6eb8640 Merge branch 'blender-v2.93-release' 2021-04-20 11:02:29 +02:00
27cfc1ea11 Fix T87541 EEVEE: AO causes black outline around objects and NaN pixels
It seems the pow result is unstable on some implementations.

Also avoid undefined behavior by clamping aoFactor to strict positive values.
2021-04-20 10:59:07 +02:00
2efd3509ee Merge branch 'blender-v2.93-release' 2021-04-20 10:06:30 +02:00
db0b1cab1f Fix T86809: Action Editor header not display the right action in certain scenarios
If a new action is created (e.g. by inserting keyframes), the header was
not garuanteed to display the right action.
Notifiers were fine here, redraw took place, the editors action was just
not set soon enough for the drawing.

Now make sure this is set correctly by ensuring the animation context is
right (same as the other editor regions do before drawing).

Maniphest Tasks: T86809

Differential Revision: https://developer.blender.org/D10796
2021-04-20 09:56:02 +02:00
8c0f7d1772 Geometry Nodes: support geometry nodes modifier on volume objects
Differential Revision: https://developer.blender.org/D11011
2021-04-20 09:30:53 +02:00
5d2ec2bc08 Cleanup: clang tidy readability-inconsistent-declaration-parameter-name 2021-04-20 09:25:15 +02:00
8c5c55b8c9 Cleanup: uiBut.flag had an internal flag out of the documented range
Increase range of internal flags & order UI_SEARCH_FILTER_NO_MATCH
within this range, so public button flags aren't accidentally added
that overlap with internal flags.
2021-04-20 15:44:21 +10:00
5e82e77112 Cleanup: remove redundant code/comments from BKE_mesh_nomain_to_mesh 2021-04-20 15:28:27 +10:00
b760481f8d Merge branch 'blender-v2.93-release' 2021-04-20 15:21:25 +10:00
72fec0f7c5 Fix T87055: Crash applying modifier on mesh with multires data 2021-04-20 15:17:48 +10:00
7ccd19fc12 PyAPI: update docs to include poll_message_set reference
Part of D9738
2021-04-20 12:29:04 +10:00
ebe04bd3ca PyAPI: support Operator.poll functions 'disabled' hint
Python scripts can now define the reason it's poll function fails using:

`Operator.poll_message_set(message, ...)`

This supports both regular text as well as delaying message creation
using a callback which should be used in situations where constructing
detailed messages is too much overhead for a poll function.

Ref D11001
2021-04-20 12:07:01 +10:00
985ccba77c Cleanup: add CTX_wm_operator_poll_msg_clear
Call this function instead of `CTX_wm_operator_poll_msg_set(C, NULL)`
2021-04-20 11:42:00 +10:00
dd886fcc6d Cleanup: Split spline implementations into separate files 2021-04-19 17:07:17 -05:00
6b44605cec Merge branch 'master' into geometry-nodes-curve-support 2021-04-19 16:57:02 -05:00
795f024558 Merge branch 'blender-v2.93-release' 2021-04-19 22:49:14 +02:00
0566ebdebe Fix crash with Alembic export after recent persistent data bugfix
We weren't clearing the recalc flags for that case.
2021-04-19 22:39:36 +02:00
2d1d6fbb23 Merge branch 'blender-v2.93-release' 2021-04-19 21:07:42 +02:00
d1fbf1599f Cycles: include more transparency and emission in fast GI approximation
For indirect light rays, don't assume any hit is opaque, rather if it has
transparency or emission do the shading but don't do any further bounces.

Naturally this is slower when there are transparent surfaces, however
without this cutout opacity doesn't give sensible results.

Differential Revision: https://developer.blender.org/D10985
2021-04-19 21:07:40 +02:00
b42454be8b Cleanup: move BVH utility functions into own file 2021-04-19 21:07:34 +02:00
68cbf0a2be Cleanup: Clang tidy, clang format 2021-04-19 13:48:25 -05:00
cedd8b8c56 Fix T87535, T87295: issues with new persistent data option
Some persistent data code was disable due to a deeper design issue, which
meant some updates were not communicated to renderers.

Dependency graph updates work in two passes, once where Blender scene
animation updates are done, then app handler scripts can run to make further
scene modifications, and then the depsgraph is updated again to take those
into account.

Previously the viewport would update renderers twice when such app handler
scripts were present. Now both viewport and persistent data rendering update
the renderers only once, accumulating updates from both passes.
2021-04-19 20:00:00 +02:00
c13b3dd168 Merge branch 'blender-v2.93-release' 2021-04-19 12:32:14 -05:00
d7caae56c4 Fix T87567: Crash adding item in empty attribute search
The function applying the search used the dummy search info for when
the item doesn't exist even when there was no UI data associated with
the node at all.

A fix is to only add the search menu when there is attribute info
stored for the node. This is something I wanted to do anyway, since
it makes it look more purposeful when there are no attribute info
for a node, less like a bug.

Differential Revision: https://developer.blender.org/D11016
2021-04-19 12:31:18 -05:00
2dc5961874 Merge branch 'blender-v2.93-release' 2021-04-19 12:25:45 -05:00
b8b7b47a00 Fix T85223: Some modifier panels can dissapear in old files
The problem is that each uiBlock needs to be assigned a unique name,
but when there can be multiple modifiers of the same type, we use the
panel sort order for the unique part of the string. However, the most
recent test file has 1200+ panels in the property editor, so 4
characters isn't enough for a unique string.

That's not a situation I expected, but it makes sense, because we don't
remove legacy panels with unused types when loading old files. So they
tend to accumulate a bunch of unused panels. That's why this works fine
with a new property editor, it doesn't all of the extra old panels.

These panels must be stored for the expansion status and order, but
arguably we could cull unused panels on save. However, simply increasing
the length of the unique panel string is a valid fix in this situation.
In the future, we can look into removing unused panels when saving.
2021-04-19 12:25:25 -05:00
458cbcbfea Merge branch 'blender-v2.93-release' 2021-04-19 11:12:42 -05:00
6f761c7110 Fix T87392: Copy modifier to selected does not do anything
This was caused by a stupid copy & paste error in rB9f323e9bf79fbf4157a.
2021-04-19 11:11:48 -05:00
8032bd98d8 GPencil: Add Layer Mask ordering buttons
Now the list of masks can be ordered as is done in Layers.

This can be used to organize list and in the future refquired for new masking options.
2021-04-19 17:44:40 +02:00
f99f884444 GPencil: Rename Draw" mode to Draw Mode` 2021-04-19 17:12:39 +02:00
feedf28549 Changes missed from 4402c2324b 2021-04-20 00:25:54 +10:00
4402c2324b Cleanup: use 'wmOperator.ptr' in draw functions
Draw functions used RNA_pointer_create to create the pointer,
however this already exists in the operator.
2021-04-20 00:14:37 +10:00
e1acefd45e GPencil: Add multiframe support to Reproject operator
Before only active frame was supported

Related to T87425
2021-04-19 15:58:41 +02:00
46a13482cb Cleanup: spelling 2021-04-19 23:56:12 +10:00
33440760a6 Merge branch 'blender-v2.93-release' 2021-04-19 23:54:06 +10:00
8245805ce3 Fix creating operator properties without an owner_id
Any dynamic enum access would not use the callback.
Always set the owner_id to avoid this causing problems.

Oversight in 919558854d
2021-04-19 23:52:59 +10:00
33f218fa3f Fix T86530: crash with Cycles OSL on Arm64
Bug in OSL itself, fix is submitted upstream.
2021-04-19 14:41:14 +02:00
2e76c3565d Geometry Nodes Curves: fix get_components_for_write 2021-04-19 13:16:59 +02:00
eb06ccc324 Fix T87448: Avoid uiBut update if value was same
Previously, clicking into a number field, changing nothing and then
clicking outside the field again would trigger an update (RNA prop
would be set to the same value again). This could potentially cause
an expensive operation (like a modifer) to run again, even if all the
parameters were identical.

The fix prevents this by treating unchanging values in the field as a
cancel operation.

Reviewed By: Severin

Maniphest Tasks: T87448

Differential Revision: https://developer.blender.org/D10976
2021-04-19 12:20:05 +02:00
c75b2019e1 Fix incorrect compositor denoise node message about SSE on macOS Arm
Ref T78710
2021-04-19 12:12:23 +02:00
Eitan
799f532f46 Geometry Nodes: new Switch node
This is a first iteration of a switch node. It can only switch between
two inputs values based on a boolean. A more sophisticated switch
node that has an integer selector will probably come later.

Currently, the geometry nodes evaluator does not support lazy evaluation
of individual inputs. Therefore, all inputs will be computed currently.
An improvement to the evaluator will be worked on separately.

Ref: T85374

Differential Revision: https://developer.blender.org/D10460
2021-04-19 10:38:50 +02:00
112fb77157 Merge branch 'blender-v2.93-release' 2021-04-19 17:36:59 +10:00
1d96493c2f Merge branch 'blender-v2.93-release' 2021-04-19 17:36:56 +10:00
969cc22a89 Merge branch 'blender-v2.93-release' 2021-04-19 17:36:54 +10:00
eae39a4973 Merge branch 'blender-v2.93-release' 2021-04-19 17:36:51 +10:00
6dffdb02fa Fix drag event leaving the gizmo not under the cursor highlighted
Prevent drag events from changing the highlighted gizmo
unless the drag event activates the gizmo.

This resolves a glitch where testing a drag event would highlight
at the point the drag was initiated even when the event was not handled.
2021-04-19 17:18:59 +10:00
3b4b231be5 Cleanup: re-order gizmo handling checks
Non-functional change in preparation for fix.
2021-04-19 17:18:57 +10:00
0b903755a9 Fix spin-gizmo not allowing click events to select vertices 2021-04-19 17:18:52 +10:00
9175911ffa Fix spin-gizmo button tool-tip placement
gizmo_button2d_bounds result wasn't valid when the gizmo was part
of a 3D gizmo group.

Regression in cf6d17a6aa
2021-04-19 17:17:49 +10:00
0af28f007f Cleanup: Rename variables to indicate what is evaluated
`ob` --> `ob_eval`
`me` --> `me_eval`
`em` --> `em_eval`
2021-04-19 00:24:43 -03:00
c3b5378978 Geometry Nodes Curves: Fix build after merging master 2021-04-18 18:50:48 -05:00
950d8360f8 Fix typo in previous commit
`has_loose_edge` -> `has_loose_vert`
2021-04-18 20:45:06 -03:00
645fa86b7d Merge branch 'master' into geometry-nodes-curve-support 2021-04-18 18:25:16 -05:00
e43428eba3 Geometry Nodes Curves: Add more structure for the sample points node 2021-04-18 18:20:24 -05:00
1825e67dea Transform Snap Object: Improve code that detects updates
The previous code had unclear hacks to avoid updating while transforming,
it was also duplicated in two functions causing an inconsistent
initialization of the looptris bvhtree (which could even generate
unpredictable snapping results).

Now, detection update and inicializatiom of common members are contained in
`snap_object_data_mesh_get` and `snap_object_data_editmesh_get`.

Also, the "Hack to avoid updating while transforming" is more evident.
2021-04-18 20:07:05 -03:00
c8dd684b65 Cleanup: Use utility to init userdata in 'transfrom_snap_object.c'
This deduplicates the code making it easier to edit.
2021-04-18 20:07:05 -03:00
6879868814 Merge branch 'blender-v2.93-release' 2021-04-18 20:06:32 -03:00
063c9938f1 Fix snap mixed with vertices ignored for meshes with vertices only
The logic assumed that there were only 3 basic snapping modes.
2021-04-18 20:04:58 -03:00
52cb657e91 Geometry Nodes Curves: Fixes for curve trim node
The remaining issue is interpolation and recalculation of bezier handles.
Other than that the node should work fine.
2021-04-18 17:14:34 -05:00
a80edb8865 Geometry nodes curves: Mostly working version of a curve trim node
This still needs some more fixes, but the basics are there now.
2021-04-18 13:20:21 -05:00
9a69653b42 Merge branch 'blender-v2.93-release' 2021-04-18 11:59:36 -05:00
06888a8970 Fix compile warning
The order of the two parameters was incorrect.
2021-04-18 11:58:44 -05:00
76eae59648 Cleanup: make format 2021-04-18 09:51:03 +02:00
01448ee7ce Cleanup: clang tidy readability-else-after-return 2021-04-18 09:49:14 +02:00
8d30a7a1cf Merge branch 'blender-v2.93-release' 2021-04-17 17:13:12 -03:00
4bce9c5283 Fix memory leak in the BLI_bitmap created in 'looptri_no_hidden_map_get' 2021-04-17 16:55:59 -03:00
1544bcfc37 Measure Tool: Snap to Cage Geometry
For Measure tool, it is more useful to snap to what is really visible.

So use the cage instead of the geometry that may be hidden with Bmesh.
2021-04-17 16:51:19 -03:00
4e1507bd3b Snap Gizmo Refactor: Implement options for the gizmo behavior
The Snap Gizmo now has options for occlusion, selection filter and edit
geometry.

It will be useful to implement in current tools.
2021-04-17 16:51:19 -03:00
1fc446a908 Fix recent snap refactor
It is important to check if editmesh eval cage is also bmesh.
2021-04-17 16:50:49 -03:00
aca9a1bac3 Transform Snap Refactor: Use enum for cage snap options
This allows the addition of the `SNAP_GEOM_CAGE` option.
Currently unused.
2021-04-17 15:45:08 -03:00
ebcf49fe1a Merge branch 'blender-v2.93-release' 2021-04-17 14:22:57 -04:00
2d5a715f44 Fix T86805 Inconsistent results for exact boolean.
The fast triangulator from Blenlib could leave a non-manifold mesh
after removing degenerate triangles. Switched to an exact triangulator.
2021-04-17 14:18:03 -04:00
3516ee5a14 Merge branch 'blender-v2.93-release' 2021-04-17 19:23:12 +02:00
ddbfae7a0d Cleanup: quiet compiler warning on macos 2021-04-17 19:19:46 +02:00
1a010450bc BLI: add unit tests for recently added methods 2021-04-17 19:06:48 +02:00
eff2b89446 BLI: support multiple parameters in Stack.push_as 2021-04-17 19:06:48 +02:00
686452fb1b BLI: add Vector.append_as method
This method is similar to `std::vector::emblace_back` in that it constructs
the new object inplace in the vector, removing the need for a move.

The `_as` suffix is consistent with similar behavior in Map and Set data structures.
2021-04-17 19:06:48 +02:00
e4990a5658 Fix wrong logic used in 'ED_view3d_depth_read_cached'
It is important to limit the pixels read on `BLI_array_iter_spiral_square`.

Fortunately `ED_view3d_depth_read_cached` was not being called with a
`margin` parameter.

Wrong logic introduced in rB44c76e4ce310.
2021-04-17 13:52:31 -03:00
fa2c00ae91 Fix wrong logic used in 'ED_view3d_depth_read_cached'
It is important to limit the pixels read on `BLI_array_iter_spiral_square`.

Fortunately `ED_view3d_depth_read_cached` was not being called with a
`margin` parameter.

Wrong logic introduced in rB44c76e4ce310.
2021-04-17 13:51:33 -03:00
d0d85742fc BLI: support multiple arguments for value in *_as methods of Map
This allows us to build more complex values in-place in the map.
Before those values had to be build separately and then moved into the map.

Existing calls to the Map API remain unchanged.
2021-04-17 18:39:35 +02:00
5cf6f570c6 Geometry Nodes: use virtual arrays in internal attribute api
A virtual array is a data structure that is similar to a normal array
in that its elements can be accessed by an index. However, a virtual
array does not have to be a contiguous array internally. Instead, its
elements can be layed out arbitrarily while element access happens
through a virtual function call. However, the virtual array data
structures are designed so that the virtual function call can be avoided
in cases where it could become a bottleneck.

Most commonly, a virtual array is backed by an actual array/span or
is a single value internally, that is the same for every index.
Besides those, there are many more specialized virtual arrays like the
ones that provides vertex positions based on the `MVert` struct or
vertex group weights.

Not all attributes used by geometry nodes are stored in simple contiguous
arrays. To provide uniform access to all kinds of attributes, the attribute
API has to provide virtual array functionality that hides the implementation
details of attributes.

Before this refactor, the attribute API provided its own virtual array
implementation as part of the `ReadAttribute` and `WriteAttribute` types.
That resulted in unnecessary code duplication with the virtual array system.
Even worse, it bound many algorithms used by geometry nodes to the specifics
of the attribute API, even though they could also use different data sources
(such as data from sockets, default values, later results of expressions, ...).

This refactor removes the `ReadAttribute` and `WriteAttribute` types and
replaces them with `GVArray` and `GVMutableArray` respectively. The `GV`
stands for "generic virtual". The "generic" means that the data type contained
in those virtual arrays is only known at run-time. There are the corresponding
statically typed types `VArray<T>` and `VMutableArray<T>` as well.

No regressions are expected from this refactor. It does come with one
improvement for users. The attribute API can convert the data type
on write now. This is especially useful when writing to builtin attributes
like `material_index` with e.g. the Attribute Math node (which usually
just writes to float attributes, while `material_index` is an integer attribute).

Differential Revision: https://developer.blender.org/D10994
2021-04-17 16:41:39 +02:00
4dca44086f Merge branch 'blender-v2.93-release' 2021-04-17 15:40:22 +02:00
d9224f64a1 Fix T87321: GPencil Arrange strokes not consistent with industry standards
Now if one stroke in the extremes of the stack is selected, other strokes are moved.

Reviewed By: pepeland, Dantti

Maniphest Tasks: T87321

Differential Revision: https://developer.blender.org/D10997
2021-04-17 15:38:36 +02:00
3608891282 Functions: extend virtual array functionality
This adds support for mutable virtual arrays and provides many utilities
for creating virtual arrays for various kinds of data. This commit is
preparation for D10994.
2021-04-17 15:13:20 +02:00
b500099e5c Geometry Nodes Curves: Add generic interpolattion to evaluated points 2021-04-16 15:33:15 -05:00
c9c295ef25 Merge branch 'master' into geometry-nodes-curve-support 2021-04-16 13:30:18 -05:00
d64dea8a7e Merge branch 'master' into geometry-nodes-curve-support 2021-04-16 12:59:47 -05:00
476006ae64 Geometry Nodes Curves: Refactor curve data storage
Each piece of data is now stored separately, not bunched up in a
control point class. This should help improve performance and
it's more aligned with the attribute approach.
2021-04-15 23:53:57 -05:00
51e72e4703 Geometry Nodes Curves: Cleanup 2021-04-15 15:04:12 -05:00
ba12548f24 Cleanup: Fix typo in NURBSpline class name 2021-04-15 13:55:13 -05:00
a6f2da1caf Geometry nodes curves: Transform curve from object info node 2021-04-15 13:52:18 -05:00
91b44ebb52 Geometry Nodes Curves: Support curves in the join geometry node 2021-04-15 13:20:15 -05:00
0d203862ca Geometry Nodes Curves: Add initial support for poly splines 2021-04-15 12:13:20 -05:00
371720cb6d Geometry nodes curves: Support instancing on control points 2021-04-15 11:47:22 -05:00
be1a41878c Geometry Nodes Curves: Implement copying a curve
This makes the spreadsheet pinning work as well.
2021-04-15 10:31:27 -05:00
53b2a1a0e3 Merge branch 'master' into geometry-nodes-curve-support 2021-04-15 09:18:53 -05:00
d9cb1fa7dc Merge branch 'master' into geometry-nodes-curve-support 2021-04-15 07:54:30 -05:00
e7516174c7 Cleanup: Use std::unique_ptr for spline vector 2021-04-14 15:32:59 -05:00
98c908d6f6 Merge branch 'master' into geometry-nodes-curve-support 2021-04-14 14:33:16 -05:00
fcb7d2fcba Geometry Nodes Curves: Add UI for curve sample points node 2021-04-12 23:45:02 -05:00
d0b2ddefa3 Merge branch 'master' into geometry-nodes-curve-support 2021-04-12 23:19:54 -05:00
bb04d1c2c8 Geometry Nodes Curves: Add structure for curve trim node
The node doesn't do anything yet because the last level needs to
be implemented, but the majority of the necessary changes are done.
2021-04-11 23:28:40 -05:00
839c796321 Geometry Nodes Curves: Fix profile radius transform 2021-04-11 18:54:05 -05:00
db731b0beb Geometry Nodes Curves: Add temporary node to test transform 2021-04-11 18:53:42 -05:00
6cf3878765 Geometry Nodes Curves: Add Z-Up normal calculation mode 2021-04-11 17:37:21 -05:00
1975dc96d1 Geometry Nodes Curves: Further refactor curve evaluation
This removes the need for some virtual functions
2021-04-09 23:28:19 -05:00
fa83dbda06 Geometry Nodes Curves: Initial read-only point attributes
Radius and position are included for now. The implementation is not at
all ideal, and writing to point attributes will be a bit trickier.
2021-04-09 22:50:44 -05:00
d0b0cb5982 Geometry Nodes Curves: Add updates while in curve is in edit mode 2021-04-09 22:49:51 -05:00
da3004a0f9 Merge branch 'master' into geometry-nodes-curve-support 2021-04-09 22:02:04 -05:00
ec090a215b WIP point attributes 2021-04-09 10:53:19 -05:00
797331db86 Geometry Nodes Curves: Make edges sharp in curve to mesh node 2021-04-08 23:17:09 -05:00
652ee81662 Geometry Nodes Curves: Switch order of generated edges 2021-04-08 22:28:37 -05:00
b5bd676a51 Geometry Nodes Curves: Apply scale instead of set scale 2021-04-08 17:01:22 -05:00
b490e0838c Geometry Nodes Curves: Fix radius interpolation 2021-04-08 16:33:49 -05:00
7c77d769dd Geometry Nodes Curves: Add "owns direct data" functions 2021-04-08 16:00:14 -05:00
48316b7e08 Merge branch 'master' into geometry-nodes-curve-support 2021-04-08 15:46:35 -05:00
ff34a3a612 Geometry Nodes Curves: Refactor, mapping from evaluated to control points
Also include some incomplete handling of radius in the curve to mesh node.
I'm not sure how much of the changes will stick around, but the reshuffling
of virtual vs. final methods will stay, and the separated caches probably
makes sense too.
2021-04-08 00:39:46 -05:00
583bc7a05d Merge branch 'master' into geometry-nodes-curve-support 2021-04-07 21:45:55 -05:00
2a96dc80b0 Geometry Nodes Curves: Small cleanup, remove debugging code 2021-04-06 00:04:11 -05:00
2cdec6cbf4 Geometry Nodes Curves: Generate faces in Curve to Mesh node 2021-04-05 23:59:33 -05:00
f8604f18e8 Geometry Nodes Curves: Further clean up forward differencing function 2021-04-05 22:55:31 -05:00
4f0e6c3aa3 Cleanup: Vectorize bezier forward differencing
It's a bit strange no one had done this already. This also solves the
problem of `BKE_curve_forward_diff_bezier` always affecting one
more points than you asked for, which was causing another problem.
2021-04-05 22:47:42 -05:00
f3775ca2cf Geometry Nodes Curves: Add edges output to mesh node 2021-04-05 17:24:25 -05:00
5e013d47a1 Geometry Nodes Curves: Fix writing to cyclic attribute 2021-04-05 17:23:34 -05:00
2f2bedafa1 Geometry Nodes Curves: Optimize evaluated points size 2021-04-05 17:23:18 -05:00
b90aeb0a44 Geometry Nodes Curves: Fix non-cylic mesh size calculation 2021-04-05 13:54:50 -05:00
29e24974a6 Geometry Nodes Curves: Fix of by one resolution issue 2021-04-05 13:54:30 -05:00
c752884d0d Geometry Nodes Curves: Fix memory leak 2021-04-05 13:43:58 -05:00
797ff2f064 Geometry Nodes Curves: Pass spline instead of position array 2021-04-05 13:36:04 -05:00
24d32a796a Geometry Nodes Curves: Add incomplete quaternion header 2021-04-05 13:15:09 -05:00
d1519df6a8 Geometry Nodes Curves: Expose cyclic attribute, fix cyclic sampling 2021-04-05 13:14:56 -05:00
457e1f9d1f Geometry Nodes Curves: Cleanup 2021-04-05 13:14:19 -05:00
85165058ef Geometry Nodes Curves: Fix clang tidy warnings 2021-04-05 13:13:16 -05:00
54188678b4 Merge branch 'master' into geometry-nodes-curve-support 2021-04-05 07:59:50 -05:00
073bf63a9d Geometry Nodes Curves: Cleanup, progres on evaluation for bezier splines 2021-04-04 23:09:20 -05:00
f91df2e3e3 Merge branch 'master' into geometry-nodes-curve-support 2021-04-04 16:58:55 -05:00
c95b2cefab Geometry Nodes Curves: More progress on curve to mesh node 2021-04-04 13:22:53 -05:00
53f92279cb Geometry Nodes Curves: Keep resolution attribute above 0 2021-04-04 13:22:29 -05:00
b3fc6ac07a Geometry Nodes Curves: Allow attribute nodes to run on curves 2021-04-04 09:30:35 -05:00
968977d684 Geometry Nodes Curves: Retrieve edit mode nurbs as well 2021-04-04 09:29:52 -05:00
f49a499129 Geometry Nodes Curves: Expose attributes in the spreadsheet 2021-04-03 09:40:11 -05:00
140828037b Geometry Nodes Curves: Initial support for spline attributes
Also: Move the evaluation cache to each spline, store spline pointers
instead of a separate list per spline type in `DCurve`, and also curve
to mesh node doesn't work now.
2021-04-03 09:39:56 -05:00
188e4d302c Geometry Nodes Curves: Rename spline domain 2021-04-03 09:38:22 -05:00
cc06b059c2 Cleanup: Reorder functions 2021-04-02 13:19:58 -05:00
0c64850e43 Geometry Nodes Curves: Fix the curve to mesh node 2021-04-02 13:13:03 -05:00
d64e149a4f Geometry Nodes Curves: Store splines a bit differently 2021-04-02 13:04:07 -05:00
09846cdce8 Merge branch 'master' into geometry-nodes-curve-support 2021-04-02 12:35:20 -05:00
a4ad4bc8d3 Create curve component with object info node 2021-04-02 11:20:45 -05:00
2622882d83 Add method to create geometry set from curve 2021-04-02 10:49:01 -05:00
b4beba7f1c Merge branch 'master' into geometry-nodes-curve-support 2021-04-02 10:41:55 -05:00
58cdb78ea0 Disfunctional support for geometry nodes on curve
Very hacky, doesn't work, but it's a base, and some components work.
I have a feeling that the curve modifier stack is just fundamentally broken.
2021-03-31 17:49:51 -05:00
25e95ccb1a Merge branch 'master' into geometry-nodes-curve-support 2021-03-31 11:41:58 -05:00
9e91aea40a Merge branch 'displist-to-cpp' into geometry-nodes-curve-support 2021-03-31 11:41:23 -05:00
5f03931fc4 Move displist.c to C++ 2021-03-31 09:11:33 -05:00
e1cef1b85d A bit of progress 2021-03-30 06:54:33 -05:00
362 changed files with 12371 additions and 4988 deletions

View File

@@ -1526,6 +1526,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_UNDEF -Wundef)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_FORMAT_SIGN -Wformat-signedness)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_RESTRICT -Wrestrict)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SUGGEST_OVERRIDE -Wno-suggest-override)
# gcc 4.2 gives annoying warnings on every file with this
if(NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "4.3")
@@ -1589,6 +1590,8 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_OVERLOADED_VIRTUAL -Wno-overloaded-virtual) # we get a lot of these, if its a problem a dev needs to look into it.
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SIGN_COMPARE -Wno-sign-compare)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_INVALID_OFFSETOF -Wno-invalid-offsetof)
# Apple Clang (tested on version 12) doesn't support this flag while LLVM Clang 11 does.
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SUGGEST_OVERRIDE -Wno-suggest-override)
# gives too many unfixable warnings
# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_MACROS -Wunused-macros)

View File

@@ -113,7 +113,7 @@ include(cmake/expat.cmake)
include(cmake/yamlcpp.cmake)
include(cmake/opencolorio.cmake)
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
if(BLENDER_PLATFORM_ARM)
include(cmake/sse2neon.cmake)
endif()

View File

@@ -18,6 +18,12 @@
set(BOOST_ADDRESS_MODEL 64)
if(BLENDER_PLATFORM_ARM)
set(BOOST_ARCHITECTURE arm)
else()
set(BOOST_ARCHITECTURE x86)
endif()
if(WIN32)
set(BOOST_TOOLSET toolset=msvc-14.1)
set(BOOST_COMPILER_STRING -vc141)
@@ -29,7 +35,6 @@ if(WIN32)
if(BUILD_MODE STREQUAL Release)
set(BOOST_HARVEST_CMD ${BOOST_HARVEST_CMD} && ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/include/boost-${BOOST_VERSION_NODOTS_SHORT}/ ${HARVEST_TARGET}/boost/include/)
endif()
elseif(APPLE)
set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh)
set(BOOST_BUILD_COMMAND ./b2)
@@ -93,7 +98,7 @@ ExternalProject_Add(external_boost
UPDATE_COMMAND ""
PATCH_COMMAND ${BOOST_PATCH_COMMAND}
CONFIGURE_COMMAND ${BOOST_CONFIGURE_COMMAND}
BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=x86 address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=${BOOST_ARCHITECTURE} address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
BUILD_IN_SOURCE 1
INSTALL_COMMAND "${BOOST_HARVEST_CMD}"
)

View File

@@ -47,7 +47,7 @@ else()
set(EMBREE_BUILD_DIR)
endif()
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
if(BLENDER_PLATFORM_ARM)
ExternalProject_Add(external_embree
GIT_REPOSITORY ${EMBREE_ARM_GIT}
GIT_TAG "blender-arm"

View File

@@ -25,19 +25,12 @@ else()
set(GMP_OPTIONS --enable-static --disable-shared )
endif()
if(APPLE)
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
set(GMP_OPTIONS
${GMP_OPTIONS}
--disable-assembly
)
else()
set(GMP_OPTIONS
${GMP_OPTIONS}
--with-pic
)
endif()
elseif(UNIX)
if(APPLE AND NOT BLENDER_PLATFORM_ARM)
set(GMP_OPTIONS
${GMP_OPTIONS}
--with-pic
)
elseif(UNIX AND NOT APPLE)
set(GMP_OPTIONS
${GMP_OPTIONS}
--with-pic
@@ -45,6 +38,13 @@ elseif(UNIX)
)
endif()
if(BLENDER_PLATFORM_ARM)
set(GMP_OPTIONS
${GMP_OPTIONS}
--disable-assembly
)
endif()
ExternalProject_Add(external_gmp
URL file://${PACKAGE_DIR}/${GMP_FILE}
DOWNLOAD_DIR ${DOWNLOAD_DIR}

View File

@@ -109,9 +109,9 @@ harvest(llvm/lib llvm/lib "libclang*.a")
if(APPLE)
harvest(openmp/lib openmp/lib "*")
harvest(openmp/include openmp/include "*.h")
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
harvest(sse2neon sse2neon "*.h")
endif()
endif()
if(BLENDER_PLATFORM_ARM)
harvest(sse2neon sse2neon "*.h")
endif()
harvest(ogg/lib ffmpeg/lib "*.a")
harvest(openal/include openal/include "*.h")

View File

@@ -16,7 +16,7 @@
#
# ***** END GPL LICENSE BLOCK *****
if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
if(BLENDER_PLATFORM_ARM)
set(LLVM_TARGETS AArch64$<SEMICOLON>ARM)
else()
set(LLVM_TARGETS X86)

View File

@@ -36,7 +36,7 @@ set(OPENCOLORIO_EXTRA_ARGS
-Dyaml-cpp_ROOT=${LIBDIR}/yamlcpp
)
if(APPLE AND NOT("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
if(BLENDER_PLATFORM_ARM)
set(OPENCOLORIO_EXTRA_ARGS
${OPENCOLORIO_EXTRA_ARGS}
-DOCIO_USE_SSE=OFF

View File

@@ -137,6 +137,10 @@ else()
endif()
set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk)
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
set(BLENDER_PLATFORM_ARM ON)
endif()
set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++ -arch ${CMAKE_OSX_ARCHITECTURES}")
set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
@@ -151,6 +155,10 @@ else()
-DCMAKE_OSX_SYSROOT:PATH=${OSX_SYSROOT}
)
else()
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
set(BLENDER_PLATFORM_ARM ON)
endif()
set(PLATFORM_CFLAGS "-fPIC")
set(PLATFORM_CXXFLAGS "-std=c++11 -fPIC")
set(PLATFORM_LDFLAGS)

View File

@@ -22,8 +22,8 @@ set(PNG_EXTRA_ARGS
-DPNG_STATIC=ON
)
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64")
if(BLENDER_PLATFORM_ARM)
set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR="aarch64")
endif()
ExternalProject_Add(external_png

View File

@@ -16,15 +16,13 @@
#
# ***** END GPL LICENSE BLOCK *****
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
ExternalProject_Add(external_sse2neon
GIT_REPOSITORY ${SSE2NEON_GIT}
GIT_TAG ${SSE2NEON_GIT_HASH}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
PREFIX ${BUILD_DIR}/sse2neon
CONFIGURE_COMMAND echo sse2neon - Nothing to configure
BUILD_COMMAND echo sse2neon - nothing to build
INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
INSTALL_DIR ${LIBDIR}/sse2neon
)
endif()
ExternalProject_Add(external_sse2neon
GIT_REPOSITORY ${SSE2NEON_GIT}
GIT_TAG ${SSE2NEON_GIT_HASH}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
PREFIX ${BUILD_DIR}/sse2neon
CONFIGURE_COMMAND echo sse2neon - Nothing to configure
BUILD_COMMAND echo sse2neon - nothing to build
INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
INSTALL_DIR ${LIBDIR}/sse2neon
)

View File

@@ -22,7 +22,9 @@ set(SSL_PATCH_CMD echo .)
if(APPLE)
set(SSL_OS_COMPILER "blender-darwin-${CMAKE_OSX_ARCHITECTURES}")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
if(BLENDER_PLATFORM_ARM)
set(SSL_OS_COMPILER "blender-linux-aarch64")
elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(SSL_EXTRA_ARGS enable-ec_nistp_64_gcc_128)
set(SSL_OS_COMPILER "blender-linux-x86_64")
else()

View File

@@ -8,6 +8,11 @@ my %targets = (
inherit_from => [ "linux-x86_64" ],
cflags => add("-fPIC"),
},
"blender-linux-aarch64" => {
inherit_from => [ "linux-aarch64" ],
cxxflags => add("-fPIC"),
cflags => add("-fPIC"),
},
"blender-darwin-x86_64" => {
inherit_from => [ "darwin64-x86_64-cc" ],
cflags => add("-fPIC"),

View File

@@ -21,6 +21,7 @@ if(WIN32)
-DTBB_BUILD_TBBMALLOC=On
-DTBB_BUILD_TBBMALLOC_PROXY=On
-DTBB_BUILD_STATIC=Off
-DTBB_BUILD_TESTS=Off
)
set(TBB_LIBRARY tbb)
set(TBB_STATIC_LIBRARY Off)
@@ -30,6 +31,7 @@ else()
-DTBB_BUILD_TBBMALLOC=On
-DTBB_BUILD_TBBMALLOC_PROXY=Off
-DTBB_BUILD_STATIC=On
-DTBB_BUILD_TESTS=Off
)
set(TBB_LIBRARY tbb_static)
set(TBB_STATIC_LIBRARY On)
@@ -42,7 +44,7 @@ ExternalProject_Add(external_tbb
URL_HASH ${TBB_HASH_TYPE}=${TBB_HASH}
PREFIX ${BUILD_DIR}/tbb
PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_tbb.txt ${BUILD_DIR}/tbb/src/external_tbb/CMakeLists.txt &&
${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/src/tbb/version_string.ver &&
${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/build/version_string.ver.in &&
${PATCH_CMD} -p 1 -d ${BUILD_DIR}/tbb/src/external_tbb < ${PATCH_DIR}/tbb.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/tbb ${DEFAULT_CMAKE_FLAGS} ${TBB_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/tbb

View File

@@ -152,7 +152,7 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0)
set(OPENCOLORIO_HASH_TYPE MD5)
set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz)
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
if(BLENDER_PLATFORM_ARM)
# Newer version required by ISPC with arm support.
set(LLVM_VERSION 11.0.1)
set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz)
@@ -398,11 +398,20 @@ set(LZMA_HASH 5117f930900b341493827d63aa910ff5e011e0b994197c3b71c08a20228a42df)
set(LZMA_HASH_TYPE SHA256)
set(LZMA_FILE xz-${LZMA_VERSION}.tar.bz2)
set(SSL_VERSION 1.1.1g)
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
set(SSL_HASH_TYPE SHA256)
set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
if(BLENDER_PLATFORM_ARM)
# Need at least 1.1.1i for aarch64 support (https://github.com/openssl/openssl/pull/13218)
set(SSL_VERSION 1.1.1i)
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
set(SSL_HASH e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242)
set(SSL_HASH_TYPE SHA256)
set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
else()
set(SSL_VERSION 1.1.1g)
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
set(SSL_HASH_TYPE SHA256)
set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
endif()
set(SQLITE_VERSION 3.31.1)
set(SQLITE_URI https://www.sqlite.org/2018/sqlite-src-3240000.zip)
@@ -453,7 +462,7 @@ set(XR_OPENXR_SDK_HASH 0df6b2fd6045423451a77ff6bc3e1a75)
set(XR_OPENXR_SDK_HASH_TYPE MD5)
set(XR_OPENXR_SDK_FILE OpenXR-SDK-${XR_OPENXR_SDK_VERSION}.tar.gz)
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
if(BLENDER_PLATFORM_ARM)
# Unreleased version with macOS arm support.
set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip)
set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d)

View File

@@ -20,24 +20,16 @@ if(WIN32)
set(X264_EXTRA_ARGS --enable-win32thread --cross-prefix=${MINGW_HOST}- --host=${MINGW_HOST})
endif()
if(APPLE)
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
set(X264_CONFIGURE_ENV echo .)
else()
set(X264_CONFIGURE_ENV
export AS=${LIBDIR}/nasm/bin/nasm
)
endif()
else()
set(X264_CONFIGURE_ENV echo .)
if(BLENDER_PLATFORM_ARM)
set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
endif()
if(UNIX AND NOT APPLE)
if((APPLE AND NOT BLENDER_PLATFORM_ARM) OR (UNIX AND NOT APPLE))
set(X264_CONFIGURE_ENV
export AS=${LIBDIR}/nasm/bin/nasm
)
else()
set(X264_CONFIGURE_ENV echo .)
endif()
ExternalProject_Add(external_x264

View File

@@ -1797,6 +1797,10 @@ compile_OCIO() {
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF"
cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF"
if [ $(uname -m) == "aarch64" ]; then
cmake_d="$cmake_d -D OCIO_USE_SSE=OFF"
fi
if file /bin/cp | grep -q '32-bit'; then
cflags="-fPIC -m32 -march=i686"
else
@@ -2059,7 +2063,10 @@ compile_OIIO() {
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D LINKSTATIC=OFF"
cmake_d="$cmake_d -D USE_SIMD=sse2"
if [ $(uname -m) != "aarch64" ]; then
cmake_d="$cmake_d -D USE_SIMD=sse2"
fi
cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
@@ -2079,7 +2086,7 @@ compile_OIIO() {
cmake_d="$cmake_d -D USE_OPENCV=OFF"
cmake_d="$cmake_d -D BUILD_TESTING=OFF"
cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON"
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF"
cmake_d="$cmake_d -D TXT2MAN="
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
@@ -2209,10 +2216,15 @@ compile_LLVM() {
mkdir build
cd build
LLVM_TARGETS="X86"
if [ $(uname -m) == "aarch64" ]; then
LLVM_TARGETS="AArch64"
fi
cmake_d="-D CMAKE_BUILD_TYPE=Release"
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D LLVM_ENABLE_FFI=ON"
cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=X86"
cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=$LLVM_TARGETS"
cmake_d="$cmake_d -D LLVM_ENABLE_TERMINFO=OFF"
if [ -d $_FFI_INCLUDE_DIR ]; then
@@ -2329,13 +2341,16 @@ compile_OSL() {
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
cmake_d="$cmake_d -D USE_SIMD=sse2"
cmake_d="$cmake_d -D USE_LLVM_BITCODE=OFF"
cmake_d="$cmake_d -D USE_PARTIO=OFF"
cmake_d="$cmake_d -D OSL_BUILD_MATERIALX=OFF"
cmake_d="$cmake_d -D USE_QT=OFF"
cmake_d="$cmake_d -D USE_PYTHON=OFF"
if [ $(uname -m) != "aarch64" ]; then
cmake_d="$cmake_d -D USE_SIMD=sse2"
fi
cmake_d="$cmake_d -D CMAKE_CXX_STANDARD=14"
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"

View File

@@ -1,5 +1,32 @@
cmake_minimum_required (VERSION 2.8)
project(tbb CXX)
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
if (POLICY CMP0048)
# cmake warns if loaded from a min-3.0-required parent dir, so silence the warning:
cmake_policy(SET CMP0048 NEW)
endif()
project (tbb CXX)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceRuns)
if(POLICY CMP0058)
cmake_policy(SET CMP0058 NEW)
endif()
if(POLICY CMP0068)
cmake_policy(SET CMP0068 NEW)
endif()
if (POLICY CMP0078)
# swig standard target names
cmake_policy(SET CMP0078 NEW)
endif ()
if (POLICY CMP0086)
# UseSWIG honors SWIG_MODULE_NAME via -module flag
cmake_policy(SET CMP0086 NEW)
endif ()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Release' as none was specified.")
@@ -8,12 +35,36 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
"MinSizeRel" "RelWithDebInfo")
endif()
include_directories(include src src/rml/include )
if(NOT TBB_INSTALL_RUNTIME_DIR)
set(TBB_INSTALL_RUNTIME_DIR bin)
endif()
if(NOT TBB_INSTALL_LIBRARY_DIR)
set(TBB_INSTALL_LIBRARY_DIR lib)
endif()
if(NOT TBB_INSTALL_ARCHIVE_DIR)
set(TBB_INSTALL_ARCHIVE_DIR lib)
endif()
if(NOT TBB_INSTALL_INCLUDE_DIR)
set(TBB_INSTALL_INCLUDE_DIR include)
endif()
if(NOT TBB_CMAKE_PACKAGE_INSTALL_DIR)
set(TBB_CMAKE_PACKAGE_INSTALL_DIR lib/cmake/tbb)
endif()
include_directories(include src src/rml/include ${CMAKE_CURRENT_BINARY_DIR})
option(TBB_BUILD_SHARED "Build TBB shared library" ON)
option(TBB_BUILD_STATIC "Build TBB static library" ON)
option(TBB_BUILD_TBBMALLOC "Build TBB malloc library" ON)
option(TBB_BUILD_TBBMALLOC_PROXY "Build TBB malloc proxy library" ON)
option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" ON)
option(TBB_NO_DATE "Do not save the configure date in the version string" OFF)
option(TBB_BUILD_PYTHON "Build TBB Python bindings" OFF)
option(TBB_SET_SOVERSION "Set the SOVERSION (shared library build version suffix)?" OFF)
# When this repository is part of a larger build system of a parent project
# we may not want TBB to set up default installation targets
option(TBB_INSTALL_TARGETS "Include build targets for 'make install'" ON)
if(APPLE)
set(CMAKE_MACOSX_RPATH ON)
@@ -39,66 +90,143 @@ set(tbbmalloc_proxy_src
src/tbbmalloc/proxy.cpp
src/tbbmalloc/tbb_function_replacement.cpp)
if (NOT APPLE)
add_definitions(-DDO_ITT_NOTIFY)
else()
# Disable annoying "has no symbols" warnings
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
add_library (tbb_interface INTERFACE)
add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i386|x86_64)")
if (NOT APPLE AND NOT MINGW)
target_compile_definitions(tbb_interface INTERFACE DO_ITT_NOTIFY)
endif()
endif()
if (APPLE)
# Disable annoying "has no symbols" warnings
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
endif()
macro(CHECK_CXX_COMPILER_AND_LINKER_FLAGS _RESULT _CXX_FLAGS _LINKER_FLAGS)
set(CMAKE_REQUIRED_FLAGS ${_CXX_FLAGS})
set(CMAKE_REQUIRED_LIBRARIES ${_LINKER_FLAGS})
set(CMAKE_REQUIRED_QUIET TRUE)
check_cxx_source_runs("#include <iostream>\nint main(int argc, char **argv) { std::cout << \"test\"; return 0; }" ${_RESULT})
set(CMAKE_REQUIRED_FLAGS "")
set(CMAKE_REQUIRED_LIBRARIES "")
endmacro()
# Prefer libc++ in conjunction with Clang
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+")
message(STATUS "TBB: using libc++.")
else()
CHECK_CXX_COMPILER_AND_LINKER_FLAGS(HAS_LIBCPP "-stdlib=libc++" "-stdlib=libc++")
if (HAS_LIBCPP)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -D_LIBCPP_VERSION")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
message(STATUS "TBB: using libc++.")
else()
message(STATUS "TBB: NOT using libc++.")
endif()
endif()
endif()
set (CMAKE_CXX_STANDARD 11)
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DUSE_PTHREAD")
if(NOT CMAKE_CXX_FLAGS MATCHES "-mno-rtm")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mrtm")
endif()
if (APPLE)
set(ARCH_PREFIX "mac")
else()
set(ARCH_PREFIX "lin")
endif()
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ARCH_PREFIX "${ARCH_PREFIX}64")
else()
set(ARCH_PREFIX "${ARCH_PREFIX}32")
endif()
set(ENABLE_RTTI "-frtti -fexceptions ")
set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
elseif(WIN32)
cmake_minimum_required (VERSION 3.1)
enable_language(ASM_MASM)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS- /Zc:wchar_t /Zc:forScope /DUSE_WINTHREAD")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_CRT_SECURE_NO_DEPRECATE /D_WIN32_WINNT=0x0600 /volatile:iso")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4800 /wd4146 /wd4244 /wd4018")
target_compile_definitions(tbb_interface INTERFACE USE_PTHREAD)
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1")
set(ARCH_PREFIX "win64")
else()
list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
list(APPEND tbbmalloc_src src/tbb/ia32-masm/atomic_support.asm)
set(ARCH_PREFIX "win32")
endif()
set(ENABLE_RTTI "/EHsc /GR ")
set(DISABLE_RTTI "/EHs- /GR- ")
check_cxx_compiler_flag ("-mrtm -Werror" SUPPORTS_MRTM)
if (SUPPORTS_MRTM)
target_compile_options(tbb_interface INTERFACE "-mrtm")
endif ()
elseif(WIN32)
target_compile_definitions(tbb_interface INTERFACE USE_WINTHREAD _WIN32_WINNT=0x0600)
if (MSVC)
enable_language(ASM_MASM)
target_compile_options(tbb_interface INTERFACE /GS- /Zc:wchar_t /Zc:forScope)
check_cxx_compiler_flag ("/volatile:iso" SUPPORTS_VOLATILE_FLAG)
if (SUPPORTS_VOLATILE_FLAG)
target_compile_options(tbb_interface INTERFACE /volatile:iso)
endif ()
target_compile_options(tbb_interface INTERFACE $<$<COMPILE_LANGUAGE:CXX>:/wd4267 /wd4800 /wd4146 /wd4244 /wd4577 /wd4018>)
if (NOT CMAKE_SIZEOF_VOID_P)
message(FATAL_ERROR "'CMAKE_SIZEOF_VOID_P' is undefined. Please delete your build directory and rerun CMake again!")
endif()
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1 ${CMAKE_ASM_MASM_FLAGS}")
else()
list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
# Enable SAFESEH feature for assembly (x86 builds only).
set(CMAKE_ASM_MASM_FLAGS "/safeseh ${CMAKE_ASM_MASM_FLAGS}")
endif()
elseif (MINGW)
target_compile_options(tbb_interface INTERFACE "-mthreads")
endif ()
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-flifetime-dse=1" SUPPORTS_FLIFETIME)
if (SUPPORTS_FLIFETIME)
add_definitions(-flifetime-dse=1)
if (MSVC)
set(ENABLE_RTTI "/EHsc /GR ")
set(DISABLE_RTTI "/EHs- /GR- ")
elseif (UNIX)
set(ENABLE_RTTI "-frtti -fexceptions ")
set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
endif ()
##--------
# - Added TBB_USE_GLIBCXX_VERSION macro to specify the version of GNU
# libstdc++ when it cannot be properly recognized, e.g. when used
# with Clang on Linux* OS. Inspired by a contribution from David A.
if (NOT TBB_USE_GLIBCXX_VERSION AND UNIX AND NOT APPLE)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# using Clang
string(REPLACE "." "0" TBB_USE_GLIBCXX_VERSION ${CMAKE_CXX_COMPILER_VERSION})
endif()
endif()
if (TBB_USE_GLIBCXX_VERSION)
target_compile_definitions(tbb_interface INTERFACE TBB_USE_GLIBCXX_VERSION=${TBB_USE_GLIBCXX_VERSION})
endif()
##-------
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
check_cxx_compiler_flag ("-flifetime-dse=1" SUPPORTS_FLIFETIME)
if (SUPPORTS_FLIFETIME)
target_compile_options(tbb_interface INTERFACE -flifetime-dse=1)
endif()
endif()
# Linker export definitions
if (WIN32)
if (APPLE)
set (ARCH_PREFIX "mac")
elseif(WIN32)
set (ARCH_PREFIX "win")
else()
set (ARCH_PREFIX "lin")
endif()
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ARCH_PREFIX "${ARCH_PREFIX}64")
else()
set(ARCH_PREFIX "${ARCH_PREFIX}32")
endif()
if (MINGW)
set (ARCH_PREFIX "${ARCH_PREFIX}-gcc")
# there's no win32-gcc-tbb-export.def, use lin32-tbb-export.def
execute_process (COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/lin32-tbb-export.def ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/win32-gcc-tbb-export.def)
endif()
if (MSVC)
add_custom_command(OUTPUT tbb.def
COMMAND ${CMAKE_CXX_COMPILER} /TC /EP ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include > tbb.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
@@ -110,18 +238,15 @@ if (WIN32)
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
COMMENT "Preprocessing tbbmalloc.def"
)
list(APPEND tbb_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/tbb_resource.rc)
list(APPEND tbbmalloc_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
list(APPEND tbbmalloc_proxy_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
else()
add_custom_command(OUTPUT tbb.def
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
COMMENT "Preprocessing tbb.def"
)
add_custom_command(OUTPUT tbbmalloc.def
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
COMMENT "Preprocessing tbbmalloc.def"
)
@@ -132,34 +257,80 @@ add_custom_target(tbb_def_files DEPENDS tbb.def tbbmalloc.def)
# TBB library
if (TBB_BUILD_STATIC)
add_library(tbb_static STATIC ${tbb_src})
set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
target_link_libraries(tbb_static PRIVATE tbb_interface)
target_include_directories(tbb_static INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
set_property(TARGET tbb_static APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
install(TARGETS tbb_static ARCHIVE DESTINATION lib)
if (TBB_INSTALL_TARGETS)
install(TARGETS tbb_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
endif()
target_compile_definitions(tbb_static
PRIVATE
-D__TBB_BUILD=1
-D__TBB_DYNAMIC_LOAD_ENABLED=0
-D__TBB_SOURCE_DIRECTLY_INCLUDED=1)
if (MSVC)
target_compile_definitions(tbb_static
PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
PRIVATE -D_CRT_SECURE_NO_WARNINGS)
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(tbb_static PUBLIC pthread dl)
endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbb SHARED ${tbb_src})
set_property(TARGET tbb APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
target_link_libraries(tbb PRIVATE tbb_interface)
target_include_directories(tbb INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
set_property(TARGET tbb APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
add_dependencies(tbb tbb_def_files)
if (APPLE)
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
elseif(UNIX)
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
elseif(WIN32)
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
if (TBB_SET_SOVERSION)
set_property(TARGET tbb PROPERTY SOVERSION 2)
endif ()
target_compile_definitions(tbb
PRIVATE -D__TBB_BUILD=1)
if (MSVC)
target_compile_definitions(tbb
PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
PRIVATE -D_CRT_SECURE_NO_WARNINGS)
endif()
install(TARGETS tbb DESTINATION lib)
if(WIN32)
set_target_properties(tbb PROPERTIES OUTPUT_NAME "tbb$<$<CONFIG:Debug>:_debug>")
add_dependencies(tbb tbb_def_files)
if (APPLE)
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
elseif (MSVC)
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
else ()
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
endif()
if (TBB_INSTALL_TARGETS)
install(TARGETS tbb EXPORT TBB
LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
if (MSVC)
install(FILES $<TARGET_PDB_FILE:tbb> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
endif()
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(tbb PUBLIC pthread dl)
endif()
endif()
if(CMAKE_COMPILER_IS_GNUCC)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# Quench a warning on GCC
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/governor.cpp COMPILE_FLAGS "-Wno-missing-field-initializers ")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# Quench a warning on Clang
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/itt_notify.cpp COMPILE_FLAGS "-Wno-varargs ")
elseif(MSVC)
# Quench a warning on MSVC
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/scheduler.cpp COMPILE_FLAGS "/wd4458 ")
@@ -169,24 +340,50 @@ if(TBB_BUILD_TBBMALLOC)
# TBB malloc library
if (TBB_BUILD_STATIC)
add_library(tbbmalloc_static STATIC ${tbbmalloc_static_src})
target_link_libraries(tbbmalloc_static PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
set_property(TARGET tbbmalloc_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
install(TARGETS tbbmalloc_static ARCHIVE DESTINATION lib)
if (MSVC)
target_compile_definitions(tbbmalloc_static PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
endif()
if (TBB_INSTALL_TARGETS)
install(TARGETS tbbmalloc_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbbmalloc SHARED ${tbbmalloc_src})
target_link_libraries(tbbmalloc PRIVATE tbb_interface)
set_property(TARGET tbbmalloc APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
if (TBB_SET_SOVERSION)
set_property(TARGET tbbmalloc PROPERTY SOVERSION 2)
endif ()
add_dependencies(tbbmalloc tbb_def_files)
if (APPLE)
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
elseif(UNIX)
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
elseif(WIN32)
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
elseif (MSVC)
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
else ()
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
endif()
if (MSVC)
target_compile_definitions(tbbmalloc PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
endif()
if (TBB_INSTALL_TARGETS)
install(TARGETS tbbmalloc EXPORT TBB
LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
if (MSVC)
install(FILES $<TARGET_PDB_FILE:tbbmalloc> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
endif()
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(tbbmalloc PUBLIC pthread dl)
endif()
install(TARGETS tbbmalloc DESTINATION lib)
endif()
endif()
@@ -194,19 +391,298 @@ if(TBB_BUILD_TBBMALLOC_PROXY)
# TBB malloc proxy library
if (TBB_BUILD_STATIC)
add_library(tbbmalloc_proxy_static STATIC ${tbbmalloc_proxy_src})
target_link_libraries(tbbmalloc_proxy_static PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
set_property(TARGET tbbmalloc_proxy_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
link_libraries(tbbmalloc_proxy_static tbbmalloc)
install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION lib)
if (TBB_INSTALL_TARGETS)
install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbbmalloc_proxy SHARED ${tbbmalloc_proxy_src})
target_link_libraries(tbbmalloc_proxy PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_proxy APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc_proxy APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
target_link_libraries(tbbmalloc_proxy tbbmalloc)
install(TARGETS tbbmalloc_proxy DESTINATION lib)
if (TBB_SET_SOVERSION)
set_property(TARGET tbbmalloc_proxy PROPERTY SOVERSION 2)
endif ()
target_link_libraries(tbbmalloc_proxy PUBLIC tbbmalloc)
if (TBB_INSTALL_TARGETS)
install(TARGETS tbbmalloc_proxy EXPORT TBB
LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
if (MSVC)
install(FILES $<TARGET_PDB_FILE:tbbmalloc_proxy> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
endif()
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(tbbmalloc_proxy PUBLIC pthread dl)
endif()
endif()
endif()
install(DIRECTORY include/tbb DESTINATION include)
if (TBB_INSTALL_TARGETS)
install(DIRECTORY include/tbb DESTINATION ${TBB_INSTALL_INCLUDE_DIR})
if (TBB_BUILD_SHARED)
install(EXPORT TBB DESTINATION ${TBB_CMAKE_PACKAGE_INSTALL_DIR} NAMESPACE TBB:: FILE TBBConfig.cmake)
endif()
endif()
# version file
if (TBB_INSTALL_TARGETS)
set (_VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/include/tbb/tbb_stddef.h)
file (STRINGS ${_VERSION_FILE} _VERSION_MAJOR_STRING REGEX ".*define[ ]+TBB_VERSION_MAJOR[ ]+[0-9]+.*")
file (STRINGS ${_VERSION_FILE} _VERSION_MINOR_STRING REGEX ".*define[ ]+TBB_VERSION_MINOR[ ]+[0-9]+.*")
string (REGEX REPLACE ".*TBB_VERSION_MAJOR[ ]+([0-9]+)" "\\1" TBB_MAJOR_VERSION ${_VERSION_MAJOR_STRING})
string (REGEX REPLACE ".*TBB_VERSION_MINOR[ ]+([0-9]+)" "\\1" TBB_MINOR_VERSION ${_VERSION_MINOR_STRING})
set (TBB_VERSION_STRING "${TBB_MAJOR_VERSION}.${TBB_MINOR_VERSION}")
include (CMakePackageConfigHelpers)
write_basic_package_version_file (TBBConfigVersion.cmake VERSION "${TBB_VERSION_STRING}" COMPATIBILITY AnyNewerVersion)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/TBBConfigVersion.cmake DESTINATION "${TBB_CMAKE_PACKAGE_INSTALL_DIR}")
endif()
# version_string.ver
if (UNIX AND NOT TBB_NO_DATE)
execute_process (COMMAND date "+%a, %d %b %Y %H:%M:%S %z"
OUTPUT_VARIABLE _configure_date
OUTPUT_STRIP_TRAILING_WHITESPACE)
elseif (WIN32 AND NOT TBB_NO_DATE)
execute_process (COMMAND cmd " /C date /T"
OUTPUT_VARIABLE _configure_date
OUTPUT_STRIP_TRAILING_WHITESPACE)
else ()
set (_configure_date "Unknown")
endif()
set (TBB_CONFIG_DATE "${_configure_date}" CACHE STRING "First time that TBB was configured")
set (_configure_date "${TBB_CONFIG_DATE}")
include_directories (${CMAKE_BINARY_DIR})
configure_file (build/version_string.ver.in version_string.ver @ONLY)
if (TBB_BUILD_TESTS)
enable_language (C)
enable_testing ()
find_library (LIBRT_LIBRARIES rt)
find_library (LIDL_LIBRARIES dl)
find_package (Threads)
if (NOT APPLE)
find_package (OpenMP)
endif()
macro (tbb_add_test testname)
set (full_testname tbb_test_${testname})
add_executable (${full_testname} src/test/test_${testname}.cpp)
target_link_libraries(${full_testname} PRIVATE tbb_interface)
if (TBB_BUILD_SHARED)
target_link_libraries (${full_testname} PRIVATE tbb tbbmalloc)
target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb)
else ()
target_link_libraries (${full_testname} PRIVATE tbb_static tbbmalloc_static)
target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb_static)
endif ()
if (LIBRT_LIBRARIES)
target_link_libraries (${full_testname} PRIVATE ${LIBRT_LIBRARIES})
endif ()
if (LIDL_LIBRARIES)
target_link_libraries (${full_testname} PRIVATE ${LIDL_LIBRARIES})
endif ()
if (Threads_FOUND)
target_link_libraries (${full_testname} PRIVATE ${CMAKE_THREAD_LIBS_INIT})
endif ()
if (OPENMP_FOUND AND "${testname}" MATCHES "openmp")
set_target_properties (${full_testname} PROPERTIES COMPILE_FLAGS "${OpenMP_CXX_FLAGS}")
set_target_properties (${full_testname} PROPERTIES LINK_FLAGS "${OpenMP_CXX_FLAGS}")
endif()
if (MINGW)
target_link_libraries (${full_testname} PRIVATE psapi)
endif ()
add_test (NAME ${full_testname} COMMAND ${full_testname})
endmacro ()
tbb_add_test (aggregator)
tbb_add_test (aligned_space)
tbb_add_test (assembly)
if (NOT WIN32)
tbb_add_test (async_msg) # msvc64/debug timeouts
endif()
tbb_add_test (async_node)
# tbb_add_test (atomic) # msvc64/debug timeouts: Compile-time initialization fails for static tbb::atomic variables
tbb_add_test (blocked_range2d)
tbb_add_test (blocked_range3d)
tbb_add_test (blocked_range)
tbb_add_test (broadcast_node)
tbb_add_test (buffer_node)
tbb_add_test (cache_aligned_allocator)
if (NOT WIN32)
tbb_add_test (cache_aligned_allocator_STL)
endif()
tbb_add_test (cilk_dynamic_load)
tbb_add_test (cilk_interop)
tbb_add_test (combinable)
tbb_add_test (composite_node)
tbb_add_test (concurrent_hash_map)
tbb_add_test (concurrent_lru_cache)
# tbb_add_test (concurrent_monitor) # too long
# tbb_add_test (concurrent_priority_queue)
if (NOT WIN32)
tbb_add_test (concurrent_queue) # msvc64/debug timeouts
endif()
# tbb_add_test (concurrent_queue_whitebox)
tbb_add_test (concurrent_unordered_map)
# tbb_add_test (concurrent_unordered_set)
tbb_add_test (concurrent_vector)
tbb_add_test (continue_node)
tbb_add_test (critical_section)
tbb_add_test (dynamic_link)
# tbb_add_test (eh_algorithms)
tbb_add_test (eh_flow_graph)
# tbb_add_test (eh_tasks)
tbb_add_test (enumerable_thread_specific)
tbb_add_test (examples_common_utility)
# tbb_add_test (fast_random)
tbb_add_test (flow_graph)
tbb_add_test (flow_graph_whitebox)
# tbb_add_test (fp) # mingw: harness_fp.h:66, assertion !checkConsistency || (ctl.mxcsr & SSE_RND_MODE_MASK) >> 3 == (ctl.x87cw & FE_RND_MODE_MASK): failed
# tbb_add_test (function_node) # mingw:random timeout
# tbb_add_test (global_control)
# tbb_add_test (global_control_whitebox)
tbb_add_test (halt)
tbb_add_test (handle_perror)
# tbb_add_test (hw_concurrency)
tbb_add_test (indexer_node)
tbb_add_test (inits_loop)
tbb_add_test (intrusive_list)
tbb_add_test (ittnotify)
# tbb_add_test (join_node) #msvc/64: fatal error C1128: number of sections exceeded object file format limit: compile with /bigob
tbb_add_test (lambda)
tbb_add_test (limiter_node)
# tbb_add_test (malloc_atexit)
# tbb_add_test (malloc_compliance) #mingw: Limits should be decreased for the test to work
tbb_add_test (malloc_init_shutdown)
# tbb_add_test (malloc_lib_unload)
# tbb_add_test (malloc_overload)
tbb_add_test (malloc_pools)
tbb_add_test (malloc_regression)
# tbb_add_test (malloc_used_by_lib)
# tbb_add_test (malloc_whitebox)
tbb_add_test (model_plugin)
# tbb_add_test (multifunction_node) # too long
tbb_add_test (mutex)
tbb_add_test (mutex_native_threads)
# tbb_add_test (opencl_node)
if (OPENMP_FOUND)
tbb_add_test (openmp)
endif ()
tbb_add_test (overwrite_node)
# tbb_add_test (parallel_do)
# This seems to fail on CI platforms (AppVeyor/Travis), perhaps because the VM exposes just 1 core?
tbb_add_test (parallel_for)
tbb_add_test (parallel_for_each)
tbb_add_test (parallel_for_vectorization)
tbb_add_test (parallel_invoke)
tbb_add_test (parallel_pipeline)
tbb_add_test (parallel_reduce)
tbb_add_test (parallel_scan)
tbb_add_test (parallel_sort)
tbb_add_test (parallel_while)
# tbb_add_test (partitioner_whitebox) # too long
tbb_add_test (pipeline)
# tbb_add_test (pipeline_with_tbf) # takes forever on appveyor
tbb_add_test (priority_queue_node)
tbb_add_test (queue_node)
tbb_add_test (reader_writer_lock)
# tbb_add_test (runtime_loader) # LINK : fatal error LNK1104: cannot open file 'tbbproxy.lib' [C:\projects\tbb\test_runtime_loader.vcxproj]
tbb_add_test (rwm_upgrade_downgrade)
# tbb_add_test (ScalableAllocator)
if (NOT WIN32)
tbb_add_test (ScalableAllocator_STL)
endif()
tbb_add_test (semaphore)
# tbb_add_test (sequencer_node) # msvc: timeout
tbb_add_test (source_node)
tbb_add_test (split_node)
tbb_add_test (static_assert)
tbb_add_test (std_thread)
tbb_add_test (tagged_msg)
# tbb_add_test (task_arena) # LINK : fatal error LNK1104: cannot open file '__TBB_LIB_NAME.lib' [C:\projects\tbb\test_task_arena.vcxproj]
# tbb_add_test (task_assertions)
tbb_add_test (task_auto_init)
tbb_add_test (task)
# tbb_add_test (task_enqueue) # too long
tbb_add_test (task_group)
# tbb_add_test (task_leaks)
# tbb_add_test (task_priority)
# tbb_add_test (task_scheduler_init) # msvc: test_task_scheduler_init.cpp:68, assertion !test_mandatory_parallelism || Harness::CanReachConcurrencyLevel(threads): failed
tbb_add_test (task_scheduler_observer)
tbb_add_test (task_steal_limit)
tbb_add_test (tbb_condition_variable)
tbb_add_test (tbb_fork)
# tbb_add_test (tbb_header)
tbb_add_test (tbb_thread)
# tbb_add_test (tbb_version)
tbb_add_test (tick_count)
tbb_add_test (tuple)
tbb_add_test (write_once_node)
tbb_add_test (yield)
endif ()
if (TBB_BUILD_PYTHON)
find_package(PythonInterp)
find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT)
find_package(SWIG 3)
if (PythonLibs_FOUND AND SWIG_FOUND AND TBB_BUILD_SHARED)
include (${SWIG_USE_FILE})
set_source_files_properties (python/tbb/api.i PROPERTIES CPLUSPLUS ON)
set (CMAKE_SWIG_FLAGS "-threads")
# swig_add_module is deprecated
if (CMAKE_VERSION VERSION_LESS 3.8)
swig_add_module (api python python/tbb/api.i)
else ()
swig_add_library (api LANGUAGE python SOURCES python/tbb/api.i)
endif ()
# UseSWIG generates now standard target names
if (CMAKE_VERSION VERSION_LESS 3.13)
set (module_target ${SWIG_MODULE_api_REAL_NAME})
else ()
set (module_target api)
endif ()
target_include_directories(${module_target} PRIVATE ${PYTHON_INCLUDE_DIRS})
target_link_libraries(${module_target} PRIVATE tbb)
if(WIN32)
target_link_libraries(${module_target} ${PYTHON_LIBRARIES})
elseif(APPLE)
set_target_properties(${module_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
endif()
if (WIN32)
set (PYTHON_SITE_PACKAGES Lib/site-packages)
else ()
set (PYTHON_SITE_PACKAGES lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
endif ()
if (TBB_INSTALL_TARGETS)
install(FILES python/TBB.py
DESTINATION ${PYTHON_SITE_PACKAGES})
install(FILES python/tbb/__init__.py python/tbb/pool.py python/tbb/test.py python/tbb/__main__.py ${CMAKE_CURRENT_BINARY_DIR}/api.py
DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
install(TARGETS ${module_target}
DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
endif()
if(UNIX AND NOT APPLE)
add_library(irml SHARED python/rml/ipc_server.cpp python/rml/ipc_utils.cpp src/tbb/cache_aligned_allocator.cpp src/tbb/dynamic_link.cpp src/tbb/tbb_misc_ex.cpp src/tbb/tbb_misc.cpp)
target_compile_definitions(irml PRIVATE DO_ITT_NOTIFY=0 USE_PTHREAD=1)
target_link_libraries(irml PRIVATE tbb)
set_target_properties(irml PROPERTIES VERSION 1)
if (TBB_INSTALL_TARGETS)
install(TARGETS irml DESTINATION ${TBB_INSTALL_LIBRARY_DIR})
endif()
endif ()
endif ()
endif ()

View File

@@ -63,3 +63,19 @@ diff -Naur org/CMakeLists.txt external_osl/CMakeLists.txt
set (OSL_NO_DEFAULT_TEXTURESYSTEM OFF CACHE BOOL "Do not use create a raw OIIO::TextureSystem")
if (OSL_NO_DEFAULT_TEXTURESYSTEM)
diff --git a/src/liboslexec/llvm_util.cpp b/src/liboslexec/llvm_util.cpp
index 445f6400..3d468de2 100644
--- a/src/liboslexec/llvm_util.cpp
+++ b/src/liboslexec/llvm_util.cpp
@@ -3430,8 +3430,9 @@ LLVM_Util::call_function (llvm::Value *func, cspan<llvm::Value *> args)
#endif
//llvm_gen_debug_printf (std::string("start ") + std::string(name));
#if OSL_LLVM_VERSION >= 110
- OSL_DASSERT(llvm::isa<llvm::Function>(func));
- llvm::Value *r = builder().CreateCall(llvm::cast<llvm::Function>(func), llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
+ llvm::Value* r = builder().CreateCall(
+ llvm::cast<llvm::FunctionType>(func->getType()->getPointerElementType()), func,
+ llvm::ArrayRef<llvm::Value*>(args.data(), args.size()));
#else
llvm::Value *r = builder().CreateCall (func, llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
#endif

View File

@@ -10,4 +10,15 @@ index 7a8d06a0..886699d8 100644
+ #if (__cplusplus >= 201402L && (!defined(_MSC_VER) || _MSC_VER >= 1920))
#define __TBB_DEPRECATED [[deprecated]]
#define __TBB_DEPRECATED_MSG(msg) [[deprecated(msg)]]
#elif _MSC_VER
#elif _MSC_VER
--- a/src/tbb/tools_api/ittnotify_config.h
+++ b/src/tbb/tools_api/ittnotify_config.h
@@ -162,7 +162,7 @@
# define ITT_ARCH ITT_ARCH_IA32E
# elif defined _M_IA64 || defined __ia64__
# define ITT_ARCH ITT_ARCH_IA64
-# elif defined _M_ARM || defined __arm__
+# elif defined _M_ARM || defined __arm__ || defined __aarch64__
# define ITT_ARCH ITT_ARCH_ARM
# elif defined __powerpc64__
# define ITT_ARCH ITT_ARCH_PPC64

View File

@@ -4,7 +4,7 @@
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 \
+ | aarch64 \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
@@ -12,7 +12,7 @@
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* \
+ | aarch64-* \
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \

View File

@@ -53,3 +53,147 @@ diff -ru USD-20.11/pxr/base/tf/pxrLZ4/lz4.cpp external_usd/pxr/base/tf/pxrLZ4/lz
/*-******************************
* Compression functions
From 442d087962f762deeb8b6e49a0955753fcf9aeb9 Mon Sep 17 00:00:00 2001
From: Tsahi Zidenberg <tsahee@amazon.com>
Date: Sun, 15 Nov 2020 15:18:24 +0000
Subject: [PATCH 1/2] stackTrace: support aarch64/linux
stacktrace calls syscall directly via assembler. Create compatible
aarch64 code.
---
pxr/base/arch/stackTrace.cpp | 30 ++++++++++++++++++++++++------
1 file changed, 24 insertions(+), 6 deletions(-)
diff --git a/pxr/base/arch/stackTrace.cpp b/pxr/base/arch/stackTrace.cpp
index dcc1dfd46..c11aabeb1 100644
--- a/pxr/base/arch/stackTrace.cpp
+++ b/pxr/base/arch/stackTrace.cpp
@@ -583,7 +583,6 @@ nonLockingLinux__execve (const char *file,
char *const argv[],
char *const envp[])
{
-#if defined(ARCH_BITS_64)
/*
* We make a direct system call here, because we can't find an
* execve which corresponds with the non-locking fork we call
@@ -594,7 +593,27 @@ nonLockingLinux__execve (const char *file,
* hangs in a threaded app. (We use the non-locking fork to get
* around problems with forking when we have had memory
* corruption.) whew.
- *
+ */
+
+ unsigned long result;
+
+#if defined (__aarch64__)
+ {
+ register long __file_result asm ("x0") = (long)file;
+ register char* const* __argv asm ("x1") = argv;
+ register char* const* __envp asm ("x2") = envp;
+ register long __num_execve asm ("x8") = 221;
+ __asm__ __volatile__ (
+ "svc 0"
+ : "=r" (__file_result)
+ : "r"(__num_execve), "r" (__file_result), "r" (__argv), "r" (__envp)
+ : "memory"
+ );
+ result = __file_result;
+ }
+#elif defined(ARCH_CPU_INTEL) && defined(ARCH_BITS_64)
+
+ /*
* %rdi, %rsi, %rdx, %rcx, %r8, %r9 are args 0-5
* syscall clobbers %rcx and %r11
*
@@ -603,7 +622,6 @@ nonLockingLinux__execve (const char *file,
* constraints to gcc.
*/
- unsigned long result;
__asm__ __volatile__ (
"mov %0, %%rdi \n\t"
"mov %%rcx, %%rsi \n\t"
@@ -614,6 +632,9 @@ nonLockingLinux__execve (const char *file,
: "0" (file), "c" (argv), "d" (envp)
: "memory", "cc", "r11"
);
+#else
+#error Unknown architecture
+#endif
if (result >= 0xfffffffffffff000) {
errno = -result;
@@ -621,9 +642,6 @@ nonLockingLinux__execve (const char *file,
}
return result;
-#else
-#error Unknown architecture
-#endif
}
#endif
From a1dffe02519bb3c6ccbbe8c6c58304da5db98995 Mon Sep 17 00:00:00 2001
From: Tsahi Zidenberg <tsahee@amazon.com>
Date: Sun, 15 Nov 2020 15:22:52 +0000
Subject: [PATCH 2/2] timing: support aarch64/linux
The aarch64 arch-timer is directly accessible to userspace via two
registers:
CNTVCT_EL0 - holds the current counter value
CNTFRQ_EL0 - holds the counter frequency (in Hz)
---
pxr/base/arch/timing.cpp | 6 ++++++
pxr/base/arch/timing.h | 6 +++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/pxr/base/arch/timing.cpp b/pxr/base/arch/timing.cpp
index 27ad58fed..9022950c1 100644
--- a/pxr/base/arch/timing.cpp
+++ b/pxr/base/arch/timing.cpp
@@ -59,6 +59,11 @@ ARCH_HIDDEN
void
Arch_InitTickTimer()
{
+#ifdef __aarch64__
+ uint64_t counter_hz;
+ __asm __volatile("mrs %0, CNTFRQ_EL0" : "=&r" (counter_hz));
+ Arch_NanosecondsPerTick = double(1e9) / double(counter_hz);
+#else
// NOTE: Normally ifstream would be cleaner, but it causes crashes when
// used in conjunction with DSOs and the Intel Compiler.
FILE *in;
@@ -135,6 +140,7 @@ Arch_InitTickTimer()
}
Arch_NanosecondsPerTick = double(1e9) / double(cpuHz);
+#endif
}
#elif defined(ARCH_OS_WINDOWS)
diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h
index 67ec0d15f..6dc3e85a0 100644
--- a/pxr/base/arch/timing.h
+++ b/pxr/base/arch/timing.h
@@ -36,7 +36,7 @@
/// \addtogroup group_arch_SystemFunctions
///@{
-#if defined(ARCH_OS_LINUX)
+#if defined(ARCH_OS_LINUX) && defined(ARCH_CPU_INTEL)
#include <x86intrin.h>
#elif defined(ARCH_OS_DARWIN)
#include <mach/mach_time.h>
@@ -69,6 +69,10 @@ ArchGetTickTime()
#elif defined(ARCH_CPU_INTEL)
// On Intel we'll use the rdtsc instruction.
return __rdtsc();
+#elif defined (__aarch64__)
+ uint64_t result;
+ __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (result));
+ return result;
#else
#error Unknown architecture.
#endif

View File

@@ -34,7 +34,7 @@ FIND_PATH(EMBREE_INCLUDE_DIR
include
)
IF(NOT (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")))
IF(NOT (("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") OR (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))))
SET(_embree_SIMD_COMPONENTS
embree_sse42
embree_avx

View File

@@ -50,7 +50,8 @@ you should be able to find the poll function with no knowledge of C.
Blender does have the functionality for poll functions to describe why they fail,
but its currently not used much, if you're interested to help improve the API
feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C)
where its not obvious why poll fails, e.g:
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into

View File

@@ -1088,8 +1088,6 @@ static int generate_file(AreaOrtho *ortho, AreaDiag *diag, const char *path, boo
return 1;
}
fprintf(stderr, "Generating %s\n", path);
if (tga)
write_tga(ortho, diag, fp, subsampling);
else if (raw)

View File

@@ -955,14 +955,21 @@ class OptiXDevice : public CUDADevice {
// Create OptiX denoiser handle on demand when it is first used
OptixDenoiserOptions denoiser_options = {};
assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3);
# if OPTIX_ABI_VERSION >= 47
denoiser_options.guideAlbedo = task.denoising.input_passes >= 2;
denoiser_options.guideNormal = task.denoising.input_passes >= 3;
check_result_optix_ret(optixDenoiserCreate(
context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser));
# else
denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>(
OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1));
# if OPTIX_ABI_VERSION < 28
# if OPTIX_ABI_VERSION < 28
denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3;
# endif
# endif
check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser));
check_result_optix_ret(
optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0));
# endif
// OptiX denoiser handle was created with the requested number of input passes
denoiser_input_passes = task.denoising.input_passes;
@@ -1032,10 +1039,34 @@ class OptiXDevice : public CUDADevice {
# endif
output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3;
# if OPTIX_ABI_VERSION >= 47
OptixDenoiserLayer image_layers = {};
image_layers.input = input_layers[0];
image_layers.output = output_layers[0];
OptixDenoiserGuideLayer guide_layers = {};
guide_layers.albedo = input_layers[1];
guide_layers.normal = input_layers[2];
# endif
// Finally run denonising
OptixDenoiserParams params = {}; // All parameters are disabled/zero
# if OPTIX_ABI_VERSION >= 47
check_result_optix_ret(optixDenoiserInvoke(denoiser,
0,
NULL,
&params,
denoiser_state.device_pointer,
scratch_offset,
&guide_layers,
&image_layers,
1,
overlap_offset.x,
overlap_offset.y,
denoiser_state.device_pointer + scratch_offset,
scratch_size));
# else
check_result_optix_ret(optixDenoiserInvoke(denoiser,
NULL,
&params,
denoiser_state.device_pointer,
scratch_offset,
@@ -1046,6 +1077,7 @@ class OptiXDevice : public CUDADevice {
output_layers,
denoiser_state.device_pointer + scratch_offset,
scratch_size));
# endif
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
void *output_args[] = {&input_ptr,

View File

@@ -93,6 +93,7 @@ set(SRC_BVH_HEADERS
bvh/bvh_local.h
bvh/bvh_traversal.h
bvh/bvh_types.h
bvh/bvh_util.h
bvh/bvh_volume.h
bvh/bvh_volume_all.h
bvh/bvh_embree.h

View File

@@ -29,9 +29,10 @@
# include "kernel/bvh/bvh_embree.h"
#endif
CCL_NAMESPACE_BEGIN
#include "kernel/bvh/bvh_types.h"
#include "kernel/bvh/bvh_util.h"
CCL_NAMESPACE_BEGIN
#ifndef __KERNEL_OPTIX__
@@ -533,97 +534,4 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg,
}
#endif /* __VOLUME_RECORD_ALL__ */
/* Ray offset to avoid self intersection.
*
* This function should be used to compute a modified ray start position for
* rays leaving from a surface. */
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
{
#ifdef __INTERSECTION_REFINE__
const float epsilon_f = 1e-5f;
/* ideally this should match epsilon_f, but instancing and motion blur
* precision makes it problematic */
const float epsilon_test = 1.0f;
const int epsilon_i = 32;
float3 res;
/* x component */
if (fabsf(P.x) < epsilon_test) {
res.x = P.x + Ng.x * epsilon_f;
}
else {
uint ix = __float_as_uint(P.x);
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
res.x = __uint_as_float(ix);
}
/* y component */
if (fabsf(P.y) < epsilon_test) {
res.y = P.y + Ng.y * epsilon_f;
}
else {
uint iy = __float_as_uint(P.y);
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
res.y = __uint_as_float(iy);
}
/* z component */
if (fabsf(P.z) < epsilon_test) {
res.z = P.z + Ng.z * epsilon_f;
}
else {
uint iz = __float_as_uint(P.z);
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
res.z = __uint_as_float(iz);
}
return res;
#else
const float epsilon_f = 1e-4f;
return P + epsilon_f * Ng;
#endif
}
#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
/* ToDo: Move to another file? */
ccl_device int intersections_compare(const void *a, const void *b)
{
const Intersection *isect_a = (const Intersection *)a;
const Intersection *isect_b = (const Intersection *)b;
if (isect_a->t < isect_b->t)
return -1;
else if (isect_a->t > isect_b->t)
return 1;
else
return 0;
}
#endif
#if defined(__SHADOW_RECORD_ALL__)
ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
{
# ifdef __KERNEL_GPU__
/* Use bubble sort which has more friendly memory pattern on GPU. */
bool swapped;
do {
swapped = false;
for (int j = 0; j < num_hits - 1; ++j) {
if (hits[j].t > hits[j + 1].t) {
struct Intersection tmp = hits[j];
hits[j] = hits[j + 1];
hits[j + 1] = tmp;
swapped = true;
}
}
--num_hits;
} while (swapped);
# else
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
# endif
}
#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
CCL_NAMESPACE_END

View File

@@ -180,25 +180,10 @@ ccl_device_inline
/* todo: optimize so primitive visibility flag indicates if
* the primitive has a transparent shadow shader? */
int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
int shader = 0;
#ifdef __HAIR__
if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
#endif
{
shader = kernel_tex_fetch(__tri_shader, prim);
}
#ifdef __HAIR__
else {
float4 str = kernel_tex_fetch(__curves, prim);
shader = __float_as_int(str.z);
}
#endif
int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
const int flags = intersection_get_shader_flags(kg, isect_array);
/* if no transparent shadows, all light is blocked */
if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) {
return true;
}
/* if maximum number of hits reached, block all light */

View File

@@ -0,0 +1,162 @@
/*
* Copyright 2011-2013 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
CCL_NAMESPACE_BEGIN
/* Ray offset to avoid self intersection.
*
* This function should be used to compute a modified ray start position for
* rays leaving from a surface. */
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
{
#ifdef __INTERSECTION_REFINE__
const float epsilon_f = 1e-5f;
/* ideally this should match epsilon_f, but instancing and motion blur
* precision makes it problematic */
const float epsilon_test = 1.0f;
const int epsilon_i = 32;
float3 res;
/* x component */
if (fabsf(P.x) < epsilon_test) {
res.x = P.x + Ng.x * epsilon_f;
}
else {
uint ix = __float_as_uint(P.x);
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
res.x = __uint_as_float(ix);
}
/* y component */
if (fabsf(P.y) < epsilon_test) {
res.y = P.y + Ng.y * epsilon_f;
}
else {
uint iy = __float_as_uint(P.y);
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
res.y = __uint_as_float(iy);
}
/* z component */
if (fabsf(P.z) < epsilon_test) {
res.z = P.z + Ng.z * epsilon_f;
}
else {
uint iz = __float_as_uint(P.z);
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
res.z = __uint_as_float(iz);
}
return res;
#else
const float epsilon_f = 1e-4f;
return P + epsilon_f * Ng;
#endif
}
#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
/* ToDo: Move to another file? */
ccl_device int intersections_compare(const void *a, const void *b)
{
const Intersection *isect_a = (const Intersection *)a;
const Intersection *isect_b = (const Intersection *)b;
if (isect_a->t < isect_b->t)
return -1;
else if (isect_a->t > isect_b->t)
return 1;
else
return 0;
}
#endif
#if defined(__SHADOW_RECORD_ALL__)
ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
{
kernel_assert(num_hits > 0);
# ifdef __KERNEL_GPU__
/* Use bubble sort which has more friendly memory pattern on GPU. */
bool swapped;
do {
swapped = false;
for (int j = 0; j < num_hits - 1; ++j) {
if (hits[j].t > hits[j + 1].t) {
struct Intersection tmp = hits[j];
hits[j] = hits[j + 1];
hits[j + 1] = tmp;
swapped = true;
}
}
--num_hits;
} while (swapped);
# else
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
# endif
}
#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
/* Utility to quickly get a shader flags from an intersection. */
ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg,
const Intersection *isect)
{
const int prim = kernel_tex_fetch(__prim_index, isect->prim);
int shader = 0;
#ifdef __HAIR__
if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
#endif
{
shader = kernel_tex_fetch(__tri_shader, prim);
}
#ifdef __HAIR__
else {
float4 str = kernel_tex_fetch(__curves, prim);
shader = __float_as_int(str.z);
}
#endif
return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
}
ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg,
const Intersection *isect)
{
const int prim = kernel_tex_fetch(__prim_index, isect->prim);
int shader = 0;
#ifdef __HAIR__
if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
#endif
{
shader = kernel_tex_fetch(__tri_shader, prim);
}
#ifdef __HAIR__
else {
float4 str = kernel_tex_fetch(__curves, prim);
shader = __float_as_int(str.z);
}
#endif
return shader & SHADER_MASK;
}
CCL_NAMESPACE_END

View File

@@ -65,7 +65,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg,
uint visibility = path_state_ray_visibility(kg, state);
if (path_state_ao_bounce(kg, state)) {
visibility = PATH_RAY_SHADOW;
ray->t = kernel_data.background.ao_distance;
}
@@ -416,7 +415,13 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
break;
}
else if (path_state_ao_bounce(kg, state)) {
break;
if (intersection_get_shader_flags(kg, &isect) &
(SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
}
else {
break;
}
}
/* Setup shader data. */
@@ -554,7 +559,13 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
break;
}
else if (path_state_ao_bounce(kg, state)) {
break;
if (intersection_get_shader_flags(kg, &isect) &
(SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
}
else {
break;
}
}
/* Setup shader data. */

View File

@@ -605,6 +605,13 @@ ccl_device_noinline
if (hit) {
t = ray->t;
}
else if (bounce == 0) {
/* Restore original position if nothing was hit after the first bounce.
* Otherwise if the ray_offset() to avoid self-intersection is relatively
* large compared to the scattering radius, we go never backup high enough
* to exit the surface. */
ray->P = sd->P;
}
/* Advance to new scatter location. */
ray->P += t * ray->D;

View File

@@ -895,6 +895,8 @@ enum ShaderDataFlag {
SD_HAS_CONSTANT_EMISSION = (1 << 27),
/* Needs to access attributes for volume rendering */
SD_NEED_VOLUME_ATTRIBUTES = (1 << 28),
/* Shader has emission */
SD_HAS_EMISSION = (1 << 29),
SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME |
SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR |

View File

@@ -528,6 +528,8 @@ void ShaderManager::device_update_common(Device *device,
if (shader->get_use_mis())
flag |= SD_USE_MIS;
if (shader->has_surface_emission)
flag |= SD_HAS_EMISSION;
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
flag |= SD_HAS_TRANSPARENT_SHADOW;
if (shader->has_volume) {

View File

@@ -124,7 +124,7 @@ static struct StepTy {
template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const type &a)
{
if (i0 == i1 && i0 == i2 && i0 == i3) {
return vdupq_laneq_s32(a, i0);
return type(vdupq_laneq_s32(int32x4_t(a), i0));
}
static const uint8_t tbl[16] = {(i0 * 4) + 0,
(i0 * 4) + 1,
@@ -143,7 +143,7 @@ template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const typ
(i3 * 4) + 2,
(i3 * 4) + 3};
return vqtbl1q_s8(int8x16_t(a), *(int8x16_t *)tbl);
return type(vqtbl1q_s8(int8x16_t(a), *(uint8x16_t *)tbl));
}
template<class type, int i0, int i1, int i2, int i3>
@@ -167,7 +167,7 @@ type shuffle_neon(const type &a, const type &b)
(i3 * 4) + 2,
(i3 * 4) + 3};
return vqtbl1q_s8(int8x16_t(b), *(int8x16_t *)tbl);
return type(vqtbl1q_s8(int8x16_t(b), *(uint8x16_t *)tbl));
}
else {
@@ -188,7 +188,7 @@ type shuffle_neon(const type &a, const type &b)
(i3 * 4) + 2 + 16,
(i3 * 4) + 3 + 16};
return vqtbl2q_s8((int8x16x2_t){a, b}, *(int8x16_t *)tbl);
return type(vqtbl2q_s8((int8x16x2_t){int8x16_t(a), int8x16_t(b)}, *(uint8x16_t *)tbl));
}
}
#endif /* __KERNEL_NEON */

View File

@@ -283,7 +283,7 @@ __forceinline uint32_t popcnt(const sseb &a)
{
# if defined(__KERNEL_NEON__)
const int32x4_t mask = {1, 1, 1, 1};
int32x4_t t = vandq_s32(a.m128, mask);
int32x4_t t = vandq_s32(vreinterpretq_s32_m128(a.m128), mask);
return vaddvq_s32(t);
# else
return _mm_popcnt_u32(_mm_movemask_ps(a));
@@ -299,7 +299,7 @@ __forceinline uint32_t popcnt(const sseb &a)
__forceinline bool reduce_and(const sseb &a)
{
# if defined(__KERNEL_NEON__)
return vaddvq_s32(a.m128) == -4;
return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) == -4;
# else
return _mm_movemask_ps(a) == 0xf;
# endif
@@ -307,7 +307,7 @@ __forceinline bool reduce_and(const sseb &a)
__forceinline bool reduce_or(const sseb &a)
{
# if defined(__KERNEL_NEON__)
return vaddvq_s32(a.m128) != 0x0;
return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) != 0x0;
# else
return _mm_movemask_ps(a) != 0x0;
# endif
@@ -315,7 +315,7 @@ __forceinline bool reduce_or(const sseb &a)
__forceinline bool all(const sseb &b)
{
# if defined(__KERNEL_NEON__)
return vaddvq_s32(b.m128) == -4;
return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == -4;
# else
return _mm_movemask_ps(b) == 0xf;
# endif
@@ -323,7 +323,7 @@ __forceinline bool all(const sseb &b)
__forceinline bool any(const sseb &b)
{
# if defined(__KERNEL_NEON__)
return vaddvq_s32(b.m128) != 0x0;
return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) != 0x0;
# else
return _mm_movemask_ps(b) != 0x0;
# endif
@@ -331,7 +331,7 @@ __forceinline bool any(const sseb &b)
__forceinline bool none(const sseb &b)
{
# if defined(__KERNEL_NEON__)
return vaddvq_s32(b.m128) == 0x0;
return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == 0x0;
# else
return _mm_movemask_ps(b) == 0x0;
# endif

View File

@@ -596,7 +596,7 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssef shuffle(const ssef &b)
{
# ifdef __KERNEL_NEON__
return shuffle_neon<ssef, i0, i1, i2, i3>(b.m128);
return shuffle_neon<float32x4_t, i0, i1, i2, i3>(b.m128);
# else
return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(b), _MM_SHUFFLE(i3, i2, i1, i0)));
# endif
@@ -625,7 +625,7 @@ __forceinline const ssef shuffle(const ssef &a, const ssef &b)
template<size_t i0> __forceinline const ssef shuffle(const ssef &a, const ssef &b)
{
# ifdef __KERNEL_NEON__
return shuffle<float32x4_t, i0, i0, i0, i0>(a, b);
return shuffle_neon<float32x4_t, i0, i0, i0, i0>(a, b);
# else
return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i0, i0, i0, i0));
# endif

View File

@@ -446,7 +446,8 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssei shuffle(const ssei &a)
{
# ifdef __KERNEL_NEON__
return shuffle_neon<ssei, i0, i1, i2, i3>(a);
int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a));
return vreinterpretq_m128i_s32(result);
# else
return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
# endif
@@ -456,7 +457,9 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssei shuffle(const ssei &a, const ssei &b)
{
# ifdef __KERNEL_NEON__
return shuffle_neon<ssei, i0, i1, i2, i3>(a, b);
int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a),
vreinterpretq_s32_m128i(b));
return vreinterpretq_m128i_s32(result);
# else
return _mm_castps_si128(
_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0)));
@@ -514,7 +517,7 @@ __forceinline const ssei vreduce_add(const ssei &v)
__forceinline int reduce_min(const ssei &v)
{
# ifdef __KERNEL_NEON__
return vminvq_s32(v);
return vminvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_min(v));
# endif
@@ -522,7 +525,7 @@ __forceinline int reduce_min(const ssei &v)
__forceinline int reduce_max(const ssei &v)
{
# ifdef __KERNEL_NEON__
return vmaxvq_s32(v);
return vmaxvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_max(v));
# endif
@@ -530,7 +533,7 @@ __forceinline int reduce_max(const ssei &v)
__forceinline int reduce_add(const ssei &v)
{
# ifdef __KERNEL_NEON__
return vaddvq_s32(v);
return vaddvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_add(v));
# endif

View File

@@ -166,12 +166,33 @@ static void __cpuid(int data[4], int selector)
string system_cpu_brand_string()
{
#if !defined(WIN32) && !defined(__x86_64__) && !defined(__i386__)
FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
if (cpuinfo != nullptr) {
char cpuinfo_buf[513] = "";
fread(cpuinfo_buf, sizeof(cpuinfo_buf) - 1, 1, cpuinfo);
fclose(cpuinfo);
char *modelname = strstr(cpuinfo_buf, "model name");
if (modelname != nullptr) {
modelname = strchr(modelname, ':');
if (modelname != nullptr) {
modelname += 2;
char *modelname_end = strchr(modelname, '\n');
if (modelname_end != nullptr) {
*modelname_end = '\0';
return modelname;
}
}
}
}
#else
char buf[49] = {0};
int result[4] = {0};
__cpuid(result, 0x80000000);
if (result[0] >= (int)0x80000004) {
if (result[0] != 0 && result[0] >= (int)0x80000004) {
__cpuid((int *)(buf + 0), 0x80000002);
__cpuid((int *)(buf + 16), 0x80000003);
__cpuid((int *)(buf + 32), 0x80000004);
@@ -183,7 +204,7 @@ string system_cpu_brand_string()
return brand;
}
#endif
return "Unknown CPU";
}

View File

@@ -923,7 +923,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
# Perform a "natural sort", so 20 comes after 3 (for example).
files.sort(
key=lambda file_path:
tuple(int(t) if t.isdigit() else t for t in re.split("(\d+)", file_path[0].lower())),
tuple(int(t) if t.isdigit() else t for t in re.split(r"(\d+)", file_path[0].lower())),
)
col = layout.column(align=True)

View File

@@ -219,6 +219,40 @@ def RKS_GEN_scaling(_ksi, _context, ks, data):
else:
ks.paths.add(id_block, path)
# Custom Properties
def RKS_GEN_custom_props(_ksi, _context, ks, data):
# get id-block and path info
id_block, base_path, grouping = get_transform_generators_base_info(data)
# Only some RNA types can be animated.
prop_type_compat = {bpy.types.BoolProperty,
bpy.types.IntProperty,
bpy.types.FloatProperty}
# When working with a pose, 'id_block' is the armature object (which should
# get the animation data), whereas 'data' is the bone being keyed.
for cprop_name in data.keys():
# ignore special "_RNA_UI" used for UI editing
if cprop_name == "_RNA_UI":
continue
prop_path = '["%s"]' % bpy.utils.escape_identifier(cprop_name)
try:
rna_property = data.path_resolve(prop_path, False)
except ValueError as ex:
# This happens when a custom property is set to None. In that case it cannot
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
continue
if rna_property.rna_type not in prop_type_compat:
continue
path = "%s%s" % (base_path, prop_path)
if grouping:
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
else:
ks.paths.add(id_block, path)
# ------

View File

@@ -821,6 +821,12 @@ class GreasePencilLayerMasksPanel:
col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="")
col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="")
col2.separator()
sub = col2.column(align=True)
sub.operator("gpencil.layer_mask_move", icon='TRIA_UP', text="").type = 'UP'
sub.operator("gpencil.layer_mask_move", icon='TRIA_DOWN', text="").type = 'DOWN'
class GreasePencilLayerRelationsPanel:

View File

@@ -286,19 +286,17 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
mat = context.material
lineart = mat.lineart
layout.prop(lineart, "use_transparency")
if lineart.use_transparency:
layout.label(text="Transparency Masks:")
row = layout.row(align=True)
for i in range(8):
row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
row = layout.row(align=True, heading="Masks")
row.active = lineart.use_transparency
for i in range(8):
row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
classes = (

View File

@@ -329,7 +329,9 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
row = layout.row(heading="Override Crease")
row.prop(lineart, "use_crease_override", text="")
row.prop(lineart, "crease_threshold", slider=True, text="")
subrow = row.row()
subrow.active = lineart.use_crease_override
subrow.prop(lineart, "crease_threshold", slider=True, text="")
class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel):

View File

@@ -92,16 +92,15 @@ class INFO_MT_area(Menu):
layout.separator()
layout.operator("screen.area_dupli", icon='WINDOW')
layout.separator()
layout.operator("screen.screen_full_area")
layout.operator(
"screen.screen_full_area",
text="Toggle Fullscreen Area",
icon='FULLSCREEN_ENTER',
).use_hide_panels = True
text="Toggle Fullscreen Area").use_hide_panels = True
layout.operator("screen.area_dupli")
layout.separator()
layout.operator("screen.area_close")
class INFO_MT_context_menu(Menu):

View File

@@ -570,7 +570,7 @@ class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel):
layout.prop(system, "audio_device", expand=False)
sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
sub.active = system.audio_device not in {'NONE', 'Null'}
sub.active = system.audio_device not in {'NONE', 'None'}
sub.prop(system, "audio_channels", text="Channels")
sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
sub.prop(system, "audio_sample_rate", text="Sample Rate")

View File

@@ -44,6 +44,7 @@ ANIM_KS_LOCATION_ID = "Location"
ANIM_KS_ROTATION_ID = "Rotation"
ANIM_KS_SCALING_ID = "Scaling"
ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale"
ANIM_KS_LOC_ROT_SCALE_CPROP_ID = "LocRotScaleCProp"
ANIM_KS_AVAILABLE_ID = "Available"
ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter"
ANIM_KS_WHOLE_CHARACTER_SELECTED_ID = "WholeCharacterSelected"
@@ -159,6 +160,22 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
# LocRotScaleCProp
class BUILTIN_KSI_LocRotScaleCProp(KeyingSetInfo):
"""Key location/rotation/scale as well as custom properties"""
bl_idname = ANIM_KS_LOC_ROT_SCALE_CPROP_ID
bl_label = "Location, Rotation, Scale & Custom Properties"
poll = keyingsets_utils.RKS_POLL_selected_items
iterator = keyingsets_utils.RKS_ITER_selected_item
def generate(self, context, ks, data):
keyingsets_utils.RKS_GEN_location(self, context, ks, data)
keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
keyingsets_utils.RKS_GEN_custom_props(self, context, ks, data)
# RotScale
class BUILTIN_KSI_RotScale(KeyingSetInfo):
"""Insert a keyframe on each of the rotation and scale channels"""
@@ -350,7 +367,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
bl_label = "Available"
# poll - selected objects or selected object with animation data
def poll(ksi, context):
def poll(self, context):
ob = context.active_object
if ob:
# TODO: this fails if one animation-less object is active, but many others are selected
@@ -366,14 +383,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
###############################
# All properties that are likely to get animated in a character rig
class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(useful when blocking out a shot)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
bl_label = "Whole Character"
class WholeCharacterMixin:
# these prefixes should be avoided, as they are not really bones
# that animators should be touching (or need to touch)
badBonePrefixes = (
@@ -387,38 +397,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
)
# poll - pose-mode on active object only
def poll(ksi, context):
def poll(self, context):
return ((context.active_object) and (context.active_object.pose) and
(context.active_object.mode == 'POSE'))
# iterator - all bones regardless of selection
def iterator(ksi, context, ks):
def iterator(self, context, ks):
for bone in context.active_object.pose.bones:
if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
ksi.generate(context, ks, bone)
if not bone.name.startswith(self.badBonePrefixes):
self.generate(context, ks, bone)
# generator - all unlocked bone transforms + custom properties
def generate(ksi, context, ks, bone):
def generate(self, context, ks, bone):
# loc, rot, scale - only include unlocked ones
if not bone.bone.use_connect:
ksi.doLoc(ks, bone)
self.doLoc(ks, bone)
if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
ksi.doRot4d(ks, bone)
self.doRot4d(ks, bone)
else:
ksi.doRot3d(ks, bone)
ksi.doScale(ks, bone)
self.doRot3d(ks, bone)
self.doScale(ks, bone)
# bbone properties?
ksi.doBBone(context, ks, bone)
self.doBBone(context, ks, bone)
# custom props?
ksi.doCustomProps(ks, bone)
self.doCustomProps(ks, bone)
# ----------------
# helper to add some bone's property to the Keying Set
def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
def addProp(self, ks, bone, prop, index=-1, use_groups=True):
# add the property name to the base path
id_path = bone.path_from_id()
id_block = bone.id_data
@@ -439,16 +448,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# ----------------
# location properties
def doLoc(ksi, ks, bone):
def doLoc(self, ks, bone):
if bone.lock_location == (False, False, False):
ksi.addProp(ks, bone, "location")
self.addProp(ks, bone, "location")
else:
for i in range(3):
if not bone.lock_location[i]:
ksi.addProp(ks, bone, "location", i)
self.addProp(ks, bone, "location", i)
# rotation properties
def doRot4d(ksi, ks, bone):
def doRot4d(self, ks, bone):
# rotation mode affects the property used
if bone.rotation_mode == 'QUATERNION':
prop = "rotation_quaternion"
@@ -459,40 +468,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
if bone.lock_rotations_4d:
# can check individually
if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False):
ksi.addProp(ks, bone, prop)
self.addProp(ks, bone, prop)
else:
if bone.lock_rotation_w is False:
ksi.addProp(ks, bone, prop, 0) # w = 0
self.addProp(ks, bone, prop, 0) # w = 0
for i in range(3):
if not bone.lock_rotation[i]:
ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
elif True not in bone.lock_rotation:
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
# other than all open unless we keyframe the whole lot
ksi.addProp(ks, bone, prop)
self.addProp(ks, bone, prop)
def doRot3d(ksi, ks, bone):
def doRot3d(self, ks, bone):
if bone.lock_rotation == (False, False, False):
ksi.addProp(ks, bone, "rotation_euler")
self.addProp(ks, bone, "rotation_euler")
else:
for i in range(3):
if not bone.lock_rotation[i]:
ksi.addProp(ks, bone, "rotation_euler", i)
self.addProp(ks, bone, "rotation_euler", i)
# scale properties
def doScale(ksi, ks, bone):
def doScale(self, ks, bone):
if bone.lock_scale == (0, 0, 0):
ksi.addProp(ks, bone, "scale")
self.addProp(ks, bone, "scale")
else:
for i in range(3):
if not bone.lock_scale[i]:
ksi.addProp(ks, bone, "scale", i)
self.addProp(ks, bone, "scale", i)
# ----------------
# bendy bone properties
def doBBone(ksi, context, ks, pchan):
def doBBone(self, context, ks, pchan):
bone = pchan.bone
# This check is crude, but is the best we can do for now
@@ -500,12 +509,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# (and the bone is a control bone). This may lead to some
# false positives...
if bone.bbone_segments > 1:
keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan)
keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan)
# ----------------
# custom properties
def doCustomProps(ksi, ks, bone):
def doCustomProps(self, ks, bone):
prop_type_compat = {bpy.types.BoolProperty,
bpy.types.IntProperty,
@@ -528,39 +537,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
continue
if rna_property.rna_type in prop_type_compat:
ksi.addProp(ks, bone, prop_path)
self.addProp(ks, bone, prop_path)
elif prop_rna.is_animatable:
ksi.addProp(ks, bone, prop)
# All properties that are likely to get animated in a character rig, only selected bones.
self.addProp(ks, bone, prop)
class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo):
class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(useful when blocking out a shot)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
bl_label = "Whole Character"
class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(only selected bones)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID
bl_label = "Whole Character (Selected Bones Only)"
# iterator - all bones regardless of selection
def iterator(ksi, context, ks):
def iterator(self, context, ks):
# Use either the selected bones, or all of them if none are selected.
bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones
for bone in bones:
if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
if bone.name.startswith(self.badBonePrefixes):
continue
ksi.generate(context, ks, bone)
self.generate(context, ks, bone)
# Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter.
poll = BUILTIN_KSI_WholeCharacter.poll
generate = BUILTIN_KSI_WholeCharacter.generate
addProp = BUILTIN_KSI_WholeCharacter.addProp
doLoc = BUILTIN_KSI_WholeCharacter.doLoc
doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d
doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d
doScale = BUILTIN_KSI_WholeCharacter.doScale
doBBone = BUILTIN_KSI_WholeCharacter.doBBone
doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps
###############################
@@ -578,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
def generate(ksi, context, ks, data):
def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -604,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
def generate(ksi, context, ks, data):
def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -638,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
def generate(ksi, context, ks, data):
def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -664,6 +668,7 @@ classes = (
BUILTIN_KSI_Scaling,
BUILTIN_KSI_LocRot,
BUILTIN_KSI_LocRotScale,
BUILTIN_KSI_LocRotScaleCProp,
BUILTIN_KSI_LocScale,
BUILTIN_KSI_RotScale,
BUILTIN_KSI_DeltaLocation,

View File

@@ -504,6 +504,12 @@ geometry_node_categories = [
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
NodeItem("GeometryNodeCurveToMesh"),
NodeItem("GeometryNodeTransformTest"),
NodeItem("GeometryNodeCurveTrim"),
NodeItem("GeometryNodeCurveSamplePoints"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
NodeItem("GeometryNodeBoundBox"),
NodeItem("GeometryNodeTransform"),
@@ -550,6 +556,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
NodeItem("GeometryNodeSwitch"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeSeparateXYZ"),

View File

@@ -41,6 +41,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
@@ -640,18 +641,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
(size_t)buf_info->ch);
float *fbuf = buf_info->fbuf + buf_ofs;
if (a >= 1.0f) {
fbuf[0] = b_col_float[0];
fbuf[1] = b_col_float[1];
fbuf[2] = b_col_float[2];
fbuf[3] = 1.0f;
}
else {
fbuf[0] = (b_col_float[0] * a) + (fbuf[0] * (1.0f - a));
fbuf[1] = (b_col_float[1] * a) + (fbuf[1] * (1.0f - a));
fbuf[2] = (b_col_float[2] * a) + (fbuf[2] * (1.0f - a));
fbuf[3] = MIN2(fbuf[3] + a, 1.0f); /* clamp to 1.0 */
}
float font_pixel[4];
font_pixel[0] = b_col_float[0] * a;
font_pixel[1] = b_col_float[1] * a;
font_pixel[2] = b_col_float[2] * a;
font_pixel[3] = a;
blend_color_mix_float(fbuf, fbuf, font_pixel);
}
}
@@ -677,19 +672,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
(size_t)buf_info->ch);
unsigned char *cbuf = buf_info->cbuf + buf_ofs;
if (a >= 1.0f) {
cbuf[0] = b_col_char[0];
cbuf[1] = b_col_char[1];
cbuf[2] = b_col_char[2];
cbuf[3] = 255;
}
else {
cbuf[0] = (unsigned char)((b_col_char[0] * a) + (cbuf[0] * (1.0f - a)));
cbuf[1] = (unsigned char)((b_col_char[1] * a) + (cbuf[1] * (1.0f - a)));
cbuf[2] = (unsigned char)((b_col_char[2] * a) + (cbuf[2] * (1.0f - a)));
/* clamp to 255 */
cbuf[3] = (unsigned char)MIN2((int)cbuf[3] + (int)(a * 255), 255);
}
uchar font_pixel[4];
font_pixel[0] = b_col_char[0];
font_pixel[1] = b_col_char[1];
font_pixel[2] = b_col_char[2];
font_pixel[3] = unit_float_to_uchar_clamp(a);
blend_color_mix_byte(cbuf, cbuf, font_pixel);
}
}

View File

@@ -20,6 +20,7 @@
#include "FN_cpp_type.hh"
#include "FN_generic_span.hh"
#include "FN_generic_virtual_array.hh"
#include "BKE_attribute.h"
@@ -30,6 +31,10 @@
namespace blender::bke {
using fn::CPPType;
using fn::GVArray;
using fn::GVArrayPtr;
using fn::GVMutableArray;
using fn::GVMutableArrayPtr;
const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
@@ -37,112 +42,97 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
/**
* This class offers an indirection for reading an attribute.
* This is useful for the following reasons:
* - Blender does not store all attributes the same way.
* The simplest case are custom data layers with primitive types.
* A bit more complex are mesh attributes like the position of vertices,
* which are embedded into the MVert struct.
* Even more complex to access are vertex weights.
* - Sometimes attributes are stored on one domain, but we want to access
* the attribute on a different domain. Therefore, we have to interpolate
* between the domains.
* Used when looking up a "plain attribute" based on a name for reading from it.
*/
class ReadAttribute {
protected:
const AttributeDomain domain_;
const CPPType &cpp_type_;
const CustomDataType custom_data_type_;
const int64_t size_;
struct ReadAttributeLookup {
/* The virtual array that is used to read from this attribute. */
GVArrayPtr varray;
/* Domain the attribute lives on in the geometry. */
AttributeDomain domain;
/* Protects the span below, so that no two threads initialize it at the same time. */
mutable std::mutex span_mutex_;
/* When it is not null, it points to the attribute array or a temporary array that contains all
* the attribute values. */
mutable void *array_buffer_ = nullptr;
/* Is true when the buffer above is owned by the attribute accessor. */
mutable bool array_is_temporary_ = false;
public:
ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
: domain_(domain),
cpp_type_(cpp_type),
custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
size_(size)
/* Convenience function to check if the attribute has been found. */
operator bool() const
{
return this->varray.get() != nullptr;
}
virtual ~ReadAttribute();
AttributeDomain domain() const
{
return domain_;
}
const CPPType &cpp_type() const
{
return cpp_type_;
}
CustomDataType custom_data_type() const
{
return custom_data_type_;
}
int64_t size() const
{
return size_;
}
void get(const int64_t index, void *r_value) const
{
BLI_assert(index < size_);
this->get_internal(index, r_value);
}
/* Get a span that contains all attribute values. */
fn::GSpan get_span() const;
template<typename T> Span<T> get_span() const
{
return this->get_span().typed<T>();
}
protected:
/* r_value is expected to be uninitialized. */
virtual void get_internal(const int64_t index, void *r_value) const = 0;
virtual void initialize_span() const;
};
/**
* This exists for similar reasons as the ReadAttribute class, except that
* it does not deal with interpolation between domains.
* Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
*/
class WriteAttribute {
protected:
const AttributeDomain domain_;
const CPPType &cpp_type_;
const CustomDataType custom_data_type_;
const int64_t size_;
struct WriteAttributeLookup {
/* The virtual array that is used to read from and write to the attribute. */
GVMutableArrayPtr varray;
/* Domain the attributes lives on in the geometry. */
AttributeDomain domain;
/* When not null, this points either to the attribute array or to a temporary array. */
void *array_buffer_ = nullptr;
/* True, when the buffer points to a temporary array. */
bool array_is_temporary_ = false;
/* This helps to protect against forgetting to apply changes done to the array. */
bool array_should_be_applied_ = false;
/* Convenience function to check if the attribute has been found. */
operator bool() const
{
return this->varray.get() != nullptr;
}
};
/**
* An output attribute allows writing to an attribute (and optionally reading as well). It adds
* some convenience features on top of `GVMutableArray` that are very commonly used.
*
* Supported convenience features:
* - Implicit type conversion when writing to builtin attributes.
* - Supports simple access to a span containing the attribute values (that avoids the use of
* VMutableArray_Span in many cases).
* - An output attribute can live side by side with an existing attribute with a different domain
* or data type. The old attribute will only be overwritten when the #save function is called.
*/
class OutputAttribute {
public:
using SaveFn = std::function<void(OutputAttribute &)>;
private:
GVMutableArrayPtr varray_;
AttributeDomain domain_;
SaveFn save_;
std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
bool ignore_old_values_ = false;
bool save_has_been_called_ = false;
public:
WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
: domain_(domain),
cpp_type_(cpp_type),
custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
size_(size)
OutputAttribute() = default;
OutputAttribute(GVMutableArrayPtr varray,
AttributeDomain domain,
SaveFn save,
const bool ignore_old_values)
: varray_(std::move(varray)),
domain_(domain),
save_(std::move(save)),
ignore_old_values_(ignore_old_values)
{
}
virtual ~WriteAttribute();
OutputAttribute(OutputAttribute &&other) = default;
~OutputAttribute();
operator bool() const
{
return varray_.get() != nullptr;
}
GVMutableArray &operator*()
{
return *varray_;
}
GVMutableArray *operator->()
{
return varray_.get();
}
GVMutableArray &varray()
{
return *varray_;
}
AttributeDomain domain() const
{
@@ -151,168 +141,94 @@ class WriteAttribute {
const CPPType &cpp_type() const
{
return cpp_type_;
return varray_->type();
}
CustomDataType custom_data_type() const
{
return custom_data_type_;
return cpp_type_to_custom_data_type(this->cpp_type());
}
int64_t size() const
fn::GMutableSpan as_span()
{
return size_;
if (!optional_span_varray_.has_value()) {
const bool materialize_old_values = !ignore_old_values_;
optional_span_varray_.emplace(*varray_, materialize_old_values);
}
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
return span_varray;
}
void get(const int64_t index, void *r_value) const
template<typename T> MutableSpan<T> as_span()
{
BLI_assert(index < size_);
this->get_internal(index, r_value);
return this->as_span().typed<T>();
}
void set(const int64_t index, const void *value)
{
BLI_assert(index < size_);
this->set_internal(index, value);
}
/* Get a span that new attribute values can be written into. When all values have been changed,
* #apply_span has to be called. */
fn::GMutableSpan get_span();
/* The span returned by this method might not contain the current attribute values. */
fn::GMutableSpan get_span_for_write_only();
/* Write the changes to the span into the actual attribute, if they aren't already. */
void apply_span();
template<typename T> MutableSpan<T> get_span()
{
return this->get_span().typed<T>();
}
template<typename T> MutableSpan<T> get_span_for_write_only()
{
return this->get_span_for_write_only().typed<T>();
}
protected:
virtual void get_internal(const int64_t index, void *r_value) const = 0;
virtual void set_internal(const int64_t index, const void *value) = 0;
virtual void initialize_span(const bool write_only);
virtual void apply_span_if_necessary();
void save();
};
using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
/* This provides type safe access to an attribute.
* The underlying ReadAttribute is owned optionally. */
template<typename T> class TypedReadAttribute {
/**
* Same as OutputAttribute, but should be used when the data type is known at compile time.
*/
template<typename T> class OutputAttribute_Typed {
private:
std::unique_ptr<const ReadAttribute> owned_attribute_;
const ReadAttribute *attribute_;
OutputAttribute attribute_;
std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
VMutableArray<T> *varray_ = nullptr;
public:
TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
owned_attribute_ = std::move(attribute);
BLI_assert(owned_attribute_);
if (attribute_) {
optional_varray_.emplace(attribute_.varray());
varray_ = &**optional_varray_;
}
}
TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
operator bool() const
{
BLI_assert(attribute_->cpp_type().is<T>());
return varray_ != nullptr;
}
int64_t size() const
VMutableArray<T> &operator*()
{
return attribute_->size();
return *varray_;
}
T operator[](const int64_t index) const
VMutableArray<T> *operator->()
{
BLI_assert(index < attribute_->size());
T value;
value.~T();
attribute_->get(index, &value);
return value;
return varray_;
}
/* Get a span to that contains all attribute values for faster and more convenient access. */
Span<T> get_span() const
VMutableArray<T> &varray()
{
return attribute_->get_span().template typed<T>();
return *varray_;
}
AttributeDomain domain() const
{
return attribute_.domain();
}
const CPPType &cpp_type() const
{
return CPPType::get<T>();
}
CustomDataType custom_data_type() const
{
return cpp_type_to_custom_data_type(this->cpp_type());
}
MutableSpan<T> as_span()
{
return attribute_.as_span<T>();
}
void save()
{
attribute_.save();
}
};
/* This provides type safe access to an attribute.
* The underlying WriteAttribute is owned optionally. */
template<typename T> class TypedWriteAttribute {
private:
std::unique_ptr<WriteAttribute> owned_attribute_;
WriteAttribute *attribute_;
public:
TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
{
owned_attribute_ = std::move(attribute);
BLI_assert(owned_attribute_);
}
TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
{
BLI_assert(attribute_->cpp_type().is<T>());
}
int64_t size() const
{
return attribute_->size();
}
T operator[](const int64_t index) const
{
BLI_assert(index < attribute_->size());
T value;
value.~T();
attribute_->get(index, &value);
return value;
}
void set(const int64_t index, const T &value)
{
attribute_->set(index, &value);
}
/* Get a span that new values can be written into. Once all values have been updated #apply_span
* has to be called. */
MutableSpan<T> get_span()
{
return attribute_->get_span().typed<T>();
}
/* The span returned by this method might not contain the current attribute values. */
MutableSpan<T> get_span_for_write_only()
{
return attribute_->get_span_for_write_only().typed<T>();
}
/* Write back all changes to the actual attribute, if necessary. */
void apply_span()
{
attribute_->apply_span();
}
};
using BooleanReadAttribute = TypedReadAttribute<bool>;
using FloatReadAttribute = TypedReadAttribute<float>;
using Float2ReadAttribute = TypedReadAttribute<float2>;
using Float3ReadAttribute = TypedReadAttribute<float3>;
using Int32ReadAttribute = TypedReadAttribute<int>;
using Color4fReadAttribute = TypedReadAttribute<Color4f>;
using BooleanWriteAttribute = TypedWriteAttribute<bool>;
using FloatWriteAttribute = TypedWriteAttribute<float>;
using Float2WriteAttribute = TypedWriteAttribute<float2>;
using Float3WriteAttribute = TypedWriteAttribute<float3>;
using Int32WriteAttribute = TypedWriteAttribute<int>;
using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
} // namespace blender::bke

View File

@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"
@@ -21,13 +23,17 @@
#include "DNA_customdata_types.h"
#include "FN_cpp_type.hh"
namespace blender::attribute_math {
using fn::CPPType;
/**
* Utility function that simplifies calling a templated function based on a custom data type.
*/
template<typename Func>
void convert_to_static_type(const CustomDataType data_type, const Func &func)
inline void convert_to_static_type(const CustomDataType data_type, const Func &func)
{
switch (data_type) {
case CD_PROP_FLOAT:
@@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
}
}
template<typename Func>
inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
{
if (cpp_type.is<float>()) {
func(float());
}
else if (cpp_type.is<float2>()) {
func(float2());
}
else if (cpp_type.is<float3>()) {
func(float3());
}
else if (cpp_type.is<int>()) {
func(int());
}
else if (cpp_type.is<bool>()) {
func(bool());
}
else if (cpp_type.is<Color4f>()) {
func(Color4f());
}
else {
BLI_assert_unreachable();
}
}
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*

View File

@@ -112,7 +112,9 @@ bool BKE_scene_collections_object_remove(struct Main *bmain,
struct Object *object,
const bool free_us);
void BKE_collections_object_remove_nulls(struct Main *bmain);
void BKE_collections_child_remove_nulls(struct Main *bmain, struct Collection *old_collection);
void BKE_collections_child_remove_nulls(struct Main *bmain,
struct Collection *parent_collection,
struct Collection *child_collection);
/* Dependencies. */

View File

@@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
void CTX_wm_region_set(bContext *C, struct ARegion *region);
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
/**
* Values to create the message that describes the reason poll failed.
*
* \note This must be called in the same context as the poll function that created it.
*/
struct bContextPollMsgDyn_Params {
/** The result is allocated . */
char *(*get_fn)(bContext *C, void *user_data);
/** Optionally free the user-data. */
void (*free_fn)(bContext *C, void *user_data);
void *user_data;
};
const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
const struct bContextPollMsgDyn_Params *params);
void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*

View File

@@ -36,6 +36,7 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);

View File

@@ -34,11 +34,13 @@
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
struct SplineGroup;
struct Collection;
struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
struct SplineGroup;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -55,60 +57,6 @@ class ComponentAttributeProviders;
class GeometryComponent;
/**
* An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
* destination yet. Therefore, once the attribute has been filled with data, the #save method has
* to be called, to store the attribute where it belongs (possibly by replacing an existing
* attribute with the same name).
*
* This is useful for example in the Attribute Color Ramp node, when the same attribute name is
* used as input and output. Typically the input is a float attribute, and the output is a color.
* Those two attributes cannot exist at the same time, due to a name collision. To handle this
* situation well, first the output colors have to be computed before the input floats are deleted.
* Therefore, the outputs have to be written to a temporary buffer that replaces the existing
* attribute once all computations are done.
*/
class OutputAttributePtr {
private:
blender::bke::WriteAttributePtr attribute_;
public:
OutputAttributePtr() = default;
OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
OutputAttributePtr(GeometryComponent &component,
AttributeDomain domain,
std::string name,
CustomDataType data_type);
~OutputAttributePtr();
/* Returns false, when this wrapper is empty. */
operator bool() const
{
return static_cast<bool>(attribute_);
}
/* Get a reference to the underlying #WriteAttribute. */
blender::bke::WriteAttribute &get()
{
BLI_assert(attribute_);
return *attribute_;
}
blender::bke::WriteAttribute &operator*()
{
return *attribute_;
}
blender::bke::WriteAttribute *operator->()
{
return attribute_.get();
}
void save();
void apply_span_and_save();
};
/**
* Contains information about an attribute in a geometry component.
* More information can be added in the future. E.g. whether the attribute is builtin and how it is
@@ -123,6 +71,65 @@ struct AttributeMetaData {
using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
const AttributeMetaData &meta_data)>;
/**
* Base class for the attribute intializer types described below.
*/
struct AttributeInit {
enum class Type {
Default,
VArray,
MoveArray,
};
Type type;
AttributeInit(const Type type) : type(type)
{
}
};
/**
* Create an attribute using the default value for the data type.
* The default values may depend on the attribute provider implementation.
*/
struct AttributeInitDefault : public AttributeInit {
AttributeInitDefault() : AttributeInit(Type::Default)
{
}
};
/**
* Create an attribute by copying data from an existing virtual array. The virtual array
* must have the same type as the newly created attribute.
*
* Note that this can be used to fill the new attribute with the default
*/
struct AttributeInitVArray : public AttributeInit {
const blender::fn::GVArray *varray;
AttributeInitVArray(const blender::fn::GVArray *varray)
: AttributeInit(Type::VArray), varray(varray)
{
}
};
/**
* Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
* Sometimes data is created before a geometry component is available. In that case, it's
* preferable to move data directly to the created attribute to avoid a new allocation and a copy.
*
* Note that this will only have a benefit for attributes that are stored directly as contiguous
* arrays, so not for some built-in attributes.
*
* The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
* can't be used directly, and that is generally how Blender expects custom data to be allocated.
*/
struct AttributeInitMove : public AttributeInit {
void *data = nullptr;
AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
{
}
};
/**
* This is the base class for specialized geometry component types.
*/
@@ -156,26 +163,34 @@ class GeometryComponent {
/* Return true when any attribute with this name exists, including built in attributes. */
bool attribute_exists(const blender::StringRef attribute_name) const;
/* Return the data type and domain of an attribute with the given name if it exists. */
std::optional<AttributeMetaData> attribute_get_meta_data(
const blender::StringRef attribute_name) const;
/* Returns true when the geometry component supports this attribute domain. */
bool attribute_domain_supported(const AttributeDomain domain) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
bool attribute_is_builtin(const blender::StringRef attribute_name) const;
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
blender::bke::ReadAttributePtr attribute_try_get_for_read(
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
blender::bke::WriteAttributePtr attribute_try_get_for_write(
blender::bke::WriteAttributeLookup attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const;
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::StringRef attribute_name);
@@ -183,82 +198,104 @@ class GeometryComponent {
/* Returns true when the attribute has been created. */
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type);
const CustomDataType data_type,
const AttributeInit &initializer);
/* Try to create the builtin attribute with the given name. No data type or domain has to be
* provided, because those are fixed for builtin attributes. */
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
const AttributeInit &initializer);
blender::Set<std::string> attribute_names() const;
bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
/* Get a read-only attribute for the given domain and data type.
* Returns null when it does not exist. */
blender::bke::ReadAttributePtr attribute_try_get_for_read(
/* Get a virtual array to read the data of an attribute on the given domain and data type.
* Returns null when the attribute does not exist or cannot be converted to the requested domain
* and data type. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const;
/* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
* Returns null when the attribute does not exist. */
blender::bke::ReadAttributePtr attribute_try_get_for_read(
/* Get a virtual array to read the data of an attribute on the given domain. The data type is
* left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
* requested domain. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name, const AttributeDomain domain) const;
/* Get a read-only attribute for the given domain and data type.
* Returns a constant attribute based on the default value if the attribute does not exist.
* Never returns null. */
blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const;
/* Get a virtual array to read data of an attribute with the given data type. The domain is
* left unchanged. Returns null when the attribute does not exist or cannot be converted to the
* requested data type. */
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name, const CustomDataType data_type) const;
/* Get a typed read-only attribute for the given domain and type. */
template<typename T>
blender::bke::TypedReadAttribute<T> attribute_get_for_read(
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
* virtual array will contain a default value. This never returns null. */
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
}
const CustomDataType data_type,
const void *default_value = nullptr) const;
/* Get a read-only dummy attribute that always returns the same value. */
blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
const CustomDataType data_type,
const void *value) const;
/* Create a read-only dummy attribute that always returns the same value.
* The given value is converted to the correct type if necessary. */
blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
const AttributeDomain domain,
const CustomDataType in_data_type,
const CustomDataType out_data_type,
const void *value) const;
/* Get a read-only dummy attribute that always returns the same value. */
/* Should be used instead of the method above when the requested data type is known at compile
* time for better type safety. */
template<typename T>
blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
const T &value) const
blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
const AttributeDomain domain,
const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_get_constant_for_read(domain, type, &value);
std::unique_ptr varray = this->attribute_get_for_read(
attribute_name, domain, type, &default_value);
return blender::fn::GVArray_Typed<T>(std::move(varray));
}
/**
* If an attribute with the given params exist, it is returned.
* If no attribute with the given name exists, create it and
* fill it with the default value if it is provided.
* If an attribute with the given name but different domain or type exists, a temporary attribute
* is created that has to be saved after the output has been computed. This avoids deleting
* another attribute, before a computation is finished.
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
* used convince features. The returned output attribute might be empty if requested attribute
* cannot exist on the geometry.
*
* This might return no attribute when the attribute cannot exist on the component.
* The included convenience features are:
* - Implicit type conversion when writing to builtin attributes.
* - If the attribute name exists already, but has a different type/domain, a temporary attribute
* is created that will overwrite the existing attribute in the end.
*/
OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr);
blender::bke::OutputAttribute attribute_try_get_for_output(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr);
/* Same as attribute_try_get_for_output, but should be used when the original values in the
* attributes are not read, i.e. the attribute is used only for output. Since values are not read
* from this attribute, no default value is necessary. */
blender::bke::OutputAttribute attribute_try_get_for_output_only(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type);
/* Statically typed method corresponding to the equally named generic one. */
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
}
/* Statically typed method corresponding to the equally named generic one. */
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
const blender::StringRef attribute_name, const AttributeDomain domain)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
}
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
@@ -328,23 +365,34 @@ struct GeometrySet {
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_curve(
SplineGroup *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
bool has_volume() const;
bool has_curve() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
const Volume *get_volume_for_read() const;
const SplineGroup *get_curve_for_read() const;
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
Volume *get_volume_for_write();
SplineGroup *get_curve_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_volume(Volume *volume,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_curve(SplineGroup *mesh,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
@@ -377,8 +425,10 @@ class MeshComponent : public GeometryComponent {
Mesh *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
blender::bke::ReadAttributePtr attribute_try_adapt_domain(
blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
bool is_empty() const final;
@@ -424,6 +474,38 @@ class PointCloudComponent : public GeometryComponent {
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
/** A geometry component that stores curve data, in other words, a group of splines. */
class CurveComponent : public GeometryComponent {
private:
SplineGroup *curve_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
CurveComponent();
~CurveComponent();
GeometryComponent *copy() const override;
void clear();
bool has_curve() const;
void replace(SplineGroup *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
SplineGroup *release();
const SplineGroup *get_for_read() const;
SplineGroup *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:

View File

@@ -0,0 +1,55 @@
/*
* 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.
*/
#pragma once
/** \file
* \ingroup bke
*/
#include "FN_generic_virtual_array.hh"
#include "BLI_float3.hh"
#include "BKE_attribute.h"
struct Mesh;
namespace blender::bke::mesh_surface_sample {
using fn::CPPType;
using fn::GMutableSpan;
using fn::GSpan;
using fn::GVArray;
void sample_point_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
const GVArray &data_in,
GMutableSpan data_out);
void sample_corner_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
const GVArray &data_in,
GMutableSpan data_out);
void sample_face_attribute(const Mesh &mesh,
Span<int> looptri_indices,
const GVArray &data_in,
GMutableSpan data_out);
} // namespace blender::bke::mesh_surface_sample

View File

@@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo {
const struct ModifierEvalContext *ctx,
struct GeometrySet *geometry_set);
struct Volume *(*modifyVolume)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Volume *volume);
/********************* Optional functions *********************/
/**

View File

@@ -1413,6 +1413,11 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
#define GEO_NODE_SWITCH 1043
#define GEO_NODE_CURVE_TO_MESH 1044
#define GEO_NODE_CURVE_TRANSFORM_TEST 1045
#define GEO_NODE_CURVE_TRIM 1046
#define GEO_NODE_CURVE_SAMPLE_POINTS 1047
/** \} */

View File

@@ -152,6 +152,7 @@ void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bma
void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain);
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph);
void BKE_scene_graph_update_for_newframe_ex(struct Depsgraph *depsgraph, const bool clear_recalc);
void BKE_scene_view_layer_graph_evaluated_ensure(struct Main *bmain,
struct Scene *scene,

View File

@@ -0,0 +1,434 @@
/*
* 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.
*/
#pragma once
/** \file
* \ingroup bke
*/
#include <mutex>
#include "FN_generic_virtual_array.hh"
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
#include "BLI_vector.hh"
#include "BKE_attribute_math.hh"
struct Curve;
class Spline;
using SplinePtr = std::unique_ptr<Spline>;
/**
* A spline is an abstraction of a single branchless curve section, its evaluation methods,
* and data. The spline data itself is just control points and a set of attributes.
*
* Any derived class of Spline has to manage two things:
* 1. Evaluating the positions based on the stored control point data.
* 2. Interpolating arbitrary attribute data from the control points to evaluated points.
*
* Beyond that, everything else is the bas class's responsibility, with minor exceptions. Futher
* evaluation happens in a layer on top of the evaluated points generated by the derived types.
* There are a few methods to evaluate a spline:
*
* Common evaluated data is stored in caches on the spline itself. That way operations on splines
* don't need to worry about taking ownership of evaluated data when they don't need to.
*/
class Spline {
public:
enum Type {
Bezier,
NURBS,
Poly,
};
private:
Type type_;
public:
bool is_cyclic = false;
enum NormalCalculationMode {
ZUp,
Minimum,
Tangent,
};
NormalCalculationMode normal_mode;
protected:
mutable bool tangent_cache_dirty_ = true;
mutable std::mutex tangent_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
mutable bool normal_cache_dirty_ = true;
mutable std::mutex normal_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_normals_cache_;
mutable bool length_cache_dirty_ = true;
mutable std::mutex length_cache_mutex_;
mutable blender::Vector<float> evaluated_lengths_cache_;
public:
virtual ~Spline() = default;
Spline(const Type type) : type_(type){};
Spline(Spline &other)
: type_(other.type_), is_cyclic(other.is_cyclic), normal_mode(other.normal_mode)
{
}
virtual SplinePtr copy() const = 0;
Spline::Type type() const;
virtual int size() const = 0;
int segments_size() const;
virtual int resolution() const = 0;
virtual void set_resolution(const int value) = 0;
virtual void drop_front(const int count) = 0;
virtual void drop_back(const int count) = 0;
virtual blender::MutableSpan<blender::float3> positions() = 0;
virtual blender::Span<blender::float3> positions() const = 0;
virtual blender::MutableSpan<float> radii() = 0;
virtual blender::Span<float> radii() const = 0;
virtual blender::MutableSpan<float> tilts() = 0;
virtual blender::Span<float> tilts() const = 0;
/**
* Mark all caches for recomputation. This must be called after any operation that would
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
*/
virtual void mark_cache_invalid() = 0;
virtual int evaluated_points_size() const = 0;
int evaluated_edges_size() const;
float length() const;
virtual blender::Span<blender::float3> evaluated_positions() const = 0;
blender::Span<float> evaluated_lengths() const;
blender::Span<blender::float3> evaluated_tangents() const;
blender::Span<blender::float3> evaluated_normals() const;
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
struct LookupResult {
/*
* The index of the evaluated point before the result location.
* In other words, the index of the edge that the result lies on.
*/
int evaluated_index;
/*
* The index of the evaluated point after the result location,
* accounting for wrapping when the spline is cyclic.
*/
int next_evaluated_index;
/**
* The portion of the way from the evaluated point at #evaluated_index to the next point.
*/
float factor;
};
LookupResult lookup_evaluated_factor(const float factor) const;
LookupResult lookup_evaluated_length(const float length) const;
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const = 0;
protected:
virtual void correct_end_tangents() const = 0;
};
/**
* A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
* by constraining the alignment of curve handles. Evaluation stores the positions and a map of
* factors and indices in a list of floats, which is then used to interpolate any other data.
*/
class BezierSpline final : public Spline {
public:
enum HandleType {
Free,
Auto,
Vector,
Align,
};
private:
blender::Vector<HandleType> handle_types_start_;
blender::Vector<blender::float3> handle_positions_start_;
blender::Vector<blender::float3> positions_;
blender::Vector<HandleType> handle_types_end_;
blender::Vector<blender::float3> handle_positions_end_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
int resolution_;
mutable bool offset_cache_dirty_ = true;
mutable std::mutex offset_cache_mutex_;
mutable blender::Vector<int> offset_cache_;
mutable bool position_cache_dirty_ = true;
mutable std::mutex position_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_position_cache_;
mutable bool mapping_cache_dirty_ = true;
mutable std::mutex mapping_cache_mutex_;
mutable blender::Vector<float> evaluated_mapping_cache_;
public:
virtual SplinePtr copy() const final;
BezierSpline() : Spline(Type::Bezier){};
BezierSpline(const BezierSpline &other)
: Spline((Spline &)other),
handle_types_start_(other.handle_types_start_),
handle_positions_start_(other.handle_positions_start_),
positions_(other.positions_),
handle_types_end_(other.handle_types_end_),
handle_positions_end_(other.handle_positions_end_),
radii_(other.radii_),
tilts_(other.tilts_),
resolution_(other.resolution_)
{
}
int size() const final;
int resolution() const final;
void set_resolution(const int value) final;
void add_point(const blender::float3 position,
const HandleType handle_type_start,
const blender::float3 handle_position_start,
const HandleType handle_type_end,
const blender::float3 handle_position_end,
const float radius,
const float tilt);
void drop_front(const int count) final;
void drop_back(const int count) final;
blender::MutableSpan<blender::float3> positions() final;
blender::Span<blender::float3> positions() const final;
blender::MutableSpan<float> radii() final;
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
blender::Span<HandleType> handle_types_start() const;
blender::MutableSpan<HandleType> handle_types_start();
blender::Span<blender::float3> handle_positions_start() const;
blender::MutableSpan<blender::float3> handle_positions_start();
blender::Span<HandleType> handle_types_end() const;
blender::MutableSpan<HandleType> handle_types_end();
blender::Span<blender::float3> handle_positions_end() const;
blender::MutableSpan<blender::float3> handle_positions_end();
bool point_is_sharp(const int index) const;
bool handle_start_is_automatic(const int index) const;
bool handle_end_is_automatic(const int index) const;
void move_control_point(const int index, const blender::float3 new_position);
void mark_cache_invalid() final;
int evaluated_points_size() const final;
blender::Span<int> control_point_offsets() const;
blender::Span<float> evaluated_mappings() const;
blender::Span<blender::float3> evaluated_positions() const final;
struct InterpolationData {
int control_point_index;
int next_control_point_index;
float factor;
};
InterpolationData interpolation_data_from_map(const float map) const;
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const;
private:
void correct_end_tangents() const final;
bool segment_is_vector(const int start_index) const;
void evaluate_bezier_segment(const int index,
const int next_index,
blender::MutableSpan<blender::float3> positions) const;
blender::Array<int> evaluated_point_offsets() const;
};
/**
* Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
* influenced by a vector of knots, weights for each point, and the order of the spline. Every
* mapping of data to evaluated points is handled the same way, but the posistions are cached in
* the spline.
*/
class NURBSpline final : public Spline {
public:
enum KnotsMode {
Normal,
EndPoint,
Bezier,
};
KnotsMode knots_mode;
struct BasisCache {
blender::Vector<float> weights;
int start_index;
};
private:
blender::Vector<blender::float3> positions_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
blender::Vector<float> weights_;
int resolution_;
uint8_t order_;
mutable bool knots_dirty_ = true;
mutable std::mutex knots_mutex_;
mutable blender::Vector<float> knots_;
mutable bool position_cache_dirty_ = true;
mutable std::mutex position_cache_mutex_;
mutable blender::Vector<blender::float3> evaluated_position_cache_;
mutable bool basis_cache_dirty_ = true;
mutable std::mutex basis_cache_mutex_;
mutable blender::Vector<BasisCache> basis_cache_;
public:
SplinePtr copy() const final;
NURBSpline() : Spline(Type::NURBS){};
NURBSpline(const NURBSpline &other)
: Spline((Spline &)other),
positions_(other.positions_),
radii_(other.radii_),
tilts_(other.tilts_),
weights_(other.weights_),
resolution_(other.resolution_),
order_(other.order_)
{
}
int size() const final;
int resolution() const final;
void set_resolution(const int value) final;
uint8_t order() const;
void set_order(const uint8_t value);
void add_point(const blender::float3 position,
const float radius,
const float tilt,
const float weight);
void drop_front(const int count) final;
void drop_back(const int count) final;
bool check_valid_size_and_order() const;
int knots_size() const;
blender::MutableSpan<blender::float3> positions() final;
blender::Span<blender::float3> positions() const final;
blender::MutableSpan<float> radii() final;
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
blender::Span<float> knots() const;
blender::MutableSpan<float> weights();
blender::Span<float> weights() const;
void mark_cache_invalid() final;
int evaluated_points_size() const final;
blender::Span<blender::float3> evaluated_positions() const final;
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const final;
protected:
void correct_end_tangents() const final;
void calculate_knots() const;
void calculate_basis_cache() const;
};
/**
* A poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
* the two is for reduced complexity and increased performance, since interpolating data to control
* points does not change it.
*/
class PolySpline final : public Spline {
public:
blender::Vector<blender::float3> positions_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
private:
public:
SplinePtr copy() const final;
PolySpline() : Spline(Type::Bezier){};
PolySpline(const PolySpline &other)
: Spline((Spline &)other),
positions_(other.positions_),
radii_(other.radii_),
tilts_(other.tilts_)
{
}
int size() const final;
int resolution() const final;
void set_resolution(const int value) final;
void add_point(const blender::float3 position, const float radius, const float tilt);
void drop_front(const int count) final;
void drop_back(const int count) final;
blender::MutableSpan<blender::float3> positions() final;
blender::Span<blender::float3> positions() const final;
blender::MutableSpan<float> radii() final;
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
void mark_cache_invalid() final;
int evaluated_points_size() const final;
blender::Span<blender::float3> evaluated_positions() const final;
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const final;
protected:
void correct_end_tangents() const final;
};
/**
* A #SplineGroup corresponds to the #Curve object data. The name is different for clarity, since
* more of the data is stored in the splines, but also just to be different than the name in DNA.
*/
class SplineGroup {
public:
blender::Vector<SplinePtr> splines;
SplineGroup *copy();
void translate(const blender::float3 translation);
void transform(const blender::float4x4 &matrix);
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
};
SplineGroup *dcurve_from_dna_curve(const Curve &curve);

View File

@@ -117,7 +117,7 @@ set(SRC
intern/customdata_file.c
intern/data_transfer.c
intern/deform.c
intern/displist.c
intern/displist.cc
intern/displist_tangent.c
intern/dynamicpaint.c
intern/editlattice.c
@@ -133,6 +133,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
intern/geometry_component_curve.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -190,6 +191,7 @@ set(SRC
intern/mesh_remap.c
intern/mesh_remesh_voxel.c
intern/mesh_runtime.c
intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_validate.c
intern/mesh_validate.cc
@@ -240,6 +242,11 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
intern/spline_base.cc
intern/spline_bezier.cc
intern/spline_group.cc
intern/spline_nurbs.cc
intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -321,6 +328,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
BKE_spline.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h
@@ -379,6 +387,7 @@ set(SRC
BKE_mesh_remap.h
BKE_mesh_remesh_voxel.h
BKE_mesh_runtime.h
BKE_mesh_sample.hh
BKE_mesh_tangent.h
BKE_mesh_types.h
BKE_mesh_wrapper.h

File diff suppressed because it is too large Load Diff

View File

@@ -24,166 +24,8 @@
namespace blender::bke {
class ConstantReadAttribute final : public ReadAttribute {
private:
void *value_;
public:
ConstantReadAttribute(AttributeDomain domain,
const int64_t size,
const CPPType &type,
const void *value)
: ReadAttribute(domain, type, size)
{
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
type.copy_to_uninitialized(value, value_);
}
~ConstantReadAttribute() override
{
this->cpp_type_.destruct(value_);
MEM_freeN(value_);
}
void get_internal(const int64_t UNUSED(index), void *r_value) const override
{
this->cpp_type_.copy_to_uninitialized(value_, r_value);
}
void initialize_span() const override
{
const int element_size = cpp_type_.size();
array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
array_is_temporary_ = true;
cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
}
};
template<typename T> class ArrayReadAttribute final : public ReadAttribute {
private:
Span<T> data_;
public:
ArrayReadAttribute(AttributeDomain domain, Span<T> data)
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
new (r_value) T(data_[index]);
}
void initialize_span() const override
{
/* The data will not be modified, so this const_cast is fine. */
array_buffer_ = const_cast<T *>(data_.data());
array_is_temporary_ = false;
}
};
template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
private:
Array<T> data_;
public:
OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
{
}
void get_internal(const int64_t index, void *r_value) const override
{
new (r_value) T(data_[index]);
}
void initialize_span() const override
{
/* The data will not be modified, so this const_cast is fine. */
array_buffer_ = const_cast<T *>(data_.data());
array_is_temporary_ = false;
}
};
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
class DerivedArrayReadAttribute final : public ReadAttribute {
private:
Span<StructT> data_;
public:
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
: ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
const StructT &struct_value = data_[index];
const ElemT value = GetFunc(struct_value);
new (r_value) ElemT(value);
}
};
template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
private:
MutableSpan<T> data_;
public:
ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
: WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
new (r_value) T(data_[index]);
}
void set_internal(const int64_t index, const void *value) override
{
data_[index] = *reinterpret_cast<const T *>(value);
}
void initialize_span(const bool UNUSED(write_only)) override
{
array_buffer_ = data_.data();
array_is_temporary_ = false;
}
void apply_span_if_necessary() override
{
/* Do nothing, because the span contains the attribute itself already. */
}
};
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
void (*SetFunc)(StructT &, const ElemT &)>
class DerivedArrayWriteAttribute final : public WriteAttribute {
private:
MutableSpan<StructT> data_;
public:
DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
const StructT &struct_value = data_[index];
const ElemT value = GetFunc(struct_value);
new (r_value) ElemT(value);
}
void set_internal(const int64_t index, const void *value) override
{
StructT &struct_value = data_[index];
const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
SetFunc(struct_value, typed_value);
}
};
using fn::GVArrayPtr;
using fn::GVMutableArrayPtr;
/**
* Utility to group together multiple functions that are used to access custom data on geometry
@@ -244,10 +86,11 @@ class BuiltinAttributeProvider {
{
}
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const = 0;
virtual bool exists(const GeometryComponent &component) const = 0;
StringRefNull name() const
@@ -272,15 +115,16 @@ class BuiltinAttributeProvider {
*/
class DynamicAttributesProvider {
public:
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const = 0;
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const = 0;
virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const = 0;
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
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 StringRef UNUSED(attribute_name),
const AttributeDomain UNUSED(domain),
const CustomDataType UNUSED(data_type)) const
const CustomDataType UNUSED(data_type),
const AttributeInit &UNUSED(initializer)) const
{
/* Some providers should not create new attributes. */
return false;
@@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
{
}
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final;
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final;
WriteAttributePtr try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final;
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const final;
const CustomDataType data_type,
const AttributeInit &initializer) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
template<typename T>
ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const
{
return std::make_unique<ArrayReadAttribute<T>>(
domain_, Span(static_cast<const T *>(layer.data), domain_size));
return {std::make_unique<fn::GVArray_For_Span<T>>(
Span(static_cast<const T *>(layer.data), domain_size)),
domain_};
}
template<typename T>
WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
const int domain_size) const
{
return std::make_unique<ArrayWriteAttribute<T>>(
domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
MutableSpan(static_cast<T *>(layer.data), domain_size)),
domain_};
}
bool type_is_supported(CustomDataType data_type) const
@@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
{
}
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final;
WriteAttributePtr try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final;
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final;
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* the #MVert struct, but is exposed as float3 attribute.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
@@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
{
}
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
bool try_create(GeometryComponent &component) const final;
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;
};

View File

@@ -1555,6 +1555,10 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
bvh_cache_type,
bvh_cache_p,
mesh_eval_mutex);
if (looptri_mask != NULL) {
MEM_freeN(looptri_mask);
}
}
else {
/* Setup BVHTreeFromMesh */

View File

@@ -1150,6 +1150,8 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
BKE_main_collection_sync(bmain);
}
DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
return true;
}
@@ -1201,6 +1203,8 @@ bool BKE_collection_object_remove(Main *bmain,
BKE_main_collection_sync(bmain);
}
DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
return true;
}
@@ -1302,41 +1306,50 @@ static void collection_missing_parents_remove(Collection *collection)
*
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
* \param collection: may be \a NULL,
* \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
* \param child_collection: The collection that was remapped to another pointer. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
*/
void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection)
void BKE_collections_child_remove_nulls(Main *bmain,
Collection *parent_collection,
Collection *child_collection)
{
if (collection == NULL) {
/* We need to do the checks in two steps when more than one collection may be involved,
* otherwise we can miss some cases...
* Also, master collections are not in bmain, so we also need to loop over scenes.
*/
for (collection = bmain->collections.first; collection != NULL;
collection = collection->id.next) {
collection_null_children_remove(collection);
if (child_collection == NULL) {
if (parent_collection != NULL) {
collection_null_children_remove(parent_collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_null_children_remove(scene->master_collection);
else {
/* We need to do the checks in two steps when more than one collection may be involved,
* otherwise we can miss some cases...
* Also, master collections are not in bmain, so we also need to loop over scenes.
*/
for (child_collection = bmain->collections.first; child_collection != NULL;
child_collection = child_collection->id.next) {
collection_null_children_remove(child_collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_null_children_remove(scene->master_collection);
}
}
for (collection = bmain->collections.first; collection != NULL;
collection = collection->id.next) {
collection_missing_parents_remove(collection);
for (child_collection = bmain->collections.first; child_collection != NULL;
child_collection = child_collection->id.next) {
collection_missing_parents_remove(child_collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_missing_parents_remove(scene->master_collection);
}
}
else {
for (CollectionParent *parent = collection->parents.first, *parent_next; parent;
for (CollectionParent *parent = child_collection->parents.first, *parent_next; parent;
parent = parent_next) {
parent_next = parent->next;
collection_null_children_remove(parent->collection);
if (!collection_find_child(parent->collection, collection)) {
BLI_freelinkN(&collection->parents, parent);
if (!collection_find_child(parent->collection, child_collection)) {
BLI_freelinkN(&child_collection->parents, parent);
}
}
}

View File

@@ -80,7 +80,17 @@ struct bContext {
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
const char *operator_poll_msg; /* reason for poll failing */
/* Operator poll. */
/**
* Store the reason the poll function fails (static string, not allocated).
* For more advanced formatting use `operator_poll_msg_dyn_params`.
*/
const char *operator_poll_msg;
/**
* Store values to dynamically to create the string (called when a tool-tip is shown).
*/
struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
@@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
{
bContext *newC = MEM_dupallocN((void *)C);
memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
return newC;
}
void CTX_free(bContext *C)
{
/* This may contain a dynamically allocated message, free. */
CTX_wm_operator_poll_msg_clear(C);
MEM_freeN(C);
}
@@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
C->wm.gizmo_group = gzgroup;
}
void CTX_wm_operator_poll_msg_clear(bContext *C)
{
struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
if (params->free_fn != NULL) {
params->free_fn(C, params->user_data);
}
params->get_fn = NULL;
params->free_fn = NULL;
params->user_data = NULL;
C->wm.operator_poll_msg = NULL;
}
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
{
CTX_wm_operator_poll_msg_clear(C);
C->wm.operator_poll_msg = msg;
}
const char *CTX_wm_operator_poll_msg_get(bContext *C)
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
const struct bContextPollMsgDyn_Params *params)
{
CTX_wm_operator_poll_msg_clear(C);
C->wm.operator_poll_msg_dyn_params = *params;
}
const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
{
struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
if (params->get_fn != NULL) {
char *msg = params->get_fn(C, params->user_data);
if (msg != NULL) {
*r_free = true;
}
return msg;
}
*r_free = false;
return IFACE_(C->wm.operator_poll_msg);
}

View File

@@ -21,9 +21,9 @@
* \ingroup bke
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <cstdio>
#include <cstring>
#include "MEM_guardedalloc.h"
@@ -46,6 +46,7 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_font.h"
#include "BKE_geometry_set.hh"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
@@ -54,6 +55,7 @@
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_spline.hh"
#include "BLI_sys_types.h" // for intptr_t support
@@ -82,7 +84,7 @@ void BKE_displist_free(ListBase *lb)
{
DispList *dl;
while ((dl = BLI_pophead(lb))) {
while ((dl = (DispList *)BLI_pophead(lb))) {
BKE_displist_elem_free(dl);
}
}
@@ -95,7 +97,7 @@ DispList *BKE_displist_find_or_create(ListBase *lb, int type)
}
}
DispList *dl = MEM_callocN(sizeof(DispList), "find_disp");
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "find_disp");
dl->type = type;
BLI_addtail(lb, dl);
@@ -110,7 +112,7 @@ DispList *BKE_displist_find(ListBase *lb, int type)
}
}
return NULL;
return nullptr;
}
bool BKE_displist_has_faces(const ListBase *lb)
@@ -129,11 +131,11 @@ void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
BKE_displist_free(lbn);
LISTBASE_FOREACH (const DispList *, dl, lb) {
DispList *dln = MEM_dupallocN(dl);
DispList *dln = (DispList *)MEM_dupallocN(dl);
BLI_addtail(lbn, dln);
dln->verts = MEM_dupallocN(dl->verts);
dln->nors = MEM_dupallocN(dl->nors);
dln->index = MEM_dupallocN(dl->index);
dln->verts = (float *)MEM_dupallocN(dl->verts);
dln->nors = (float *)MEM_dupallocN(dl->nors);
dln->index = (int *)MEM_dupallocN(dl->index);
}
}
@@ -146,8 +148,8 @@ void BKE_displist_normals_add(ListBase *lb)
LISTBASE_FOREACH (DispList *, dl, lb) {
if (dl->type == DL_INDEX3) {
if (dl->nors == NULL) {
dl->nors = MEM_callocN(sizeof(float[3]), "dlnors");
if (dl->nors == nullptr) {
dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors");
if (dl->flag & DL_BACK_CURVE) {
dl->nors[2] = -1.0f;
@@ -158,8 +160,8 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
else if (dl->type == DL_SURF) {
if (dl->nors == NULL) {
dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
if (dl->nors == nullptr) {
dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
vdata = dl->verts;
ndata = dl->nors;
@@ -338,9 +340,9 @@ static void curve_to_displist(const Curve *cu,
* and resolution > 1. */
const bool use_cyclic_sample = is_cyclic && (samples_len != 2);
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
/* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */
dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = samples_len;
@@ -393,8 +395,8 @@ static void curve_to_displist(const Curve *cu,
}
else if (nu->type == CU_NURBS) {
const int len = (resolution * SEGMENTSU(nu));
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -402,12 +404,12 @@ static void curve_to_displist(const Curve *cu,
dl->charidx = nu->charidx;
dl->type = is_cyclic ? DL_POLY : DL_SEGM;
BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3]));
BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3]));
}
else if (nu->type == CU_POLY) {
const int len = nu->pntsu;
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -435,7 +437,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const float normal_proj[3],
const bool flip_normal)
{
if (dispbase == NULL) {
if (dispbase == nullptr) {
return;
}
if (BLI_listbase_is_empty(dispbase)) {
@@ -471,14 +473,14 @@ void BKE_displist_fill(const ListBase *dispbase,
sf_ctx.poly_nr++;
/* Make verts and edges. */
ScanFillVert *sf_vert = NULL;
ScanFillVert *sf_vert_last = NULL;
ScanFillVert *sf_vert_new = NULL;
ScanFillVert *sf_vert = nullptr;
ScanFillVert *sf_vert_last = nullptr;
ScanFillVert *sf_vert_new = nullptr;
for (int i = 0; i < dl->nr; i++) {
sf_vert_last = sf_vert;
sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]);
totvert++;
if (sf_vert_last == NULL) {
if (sf_vert_last == nullptr) {
sf_vert_new = sf_vert;
}
else {
@@ -486,7 +488,7 @@ void BKE_displist_fill(const ListBase *dispbase,
}
}
if (sf_vert != NULL && sf_vert_new != NULL) {
if (sf_vert != nullptr && sf_vert_new != nullptr) {
BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new);
}
}
@@ -503,7 +505,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj);
if (totvert != 0 && triangles_len != 0) {
DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist");
DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist");
dlnew->type = DL_INDEX3;
dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE));
dlnew->rt = (dl_rt_accum & CU_SMOOTH);
@@ -511,8 +513,8 @@ void BKE_displist_fill(const ListBase *dispbase,
dlnew->nr = totvert;
dlnew->parts = triangles_len;
dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
/* vert data */
int i;
@@ -551,16 +553,16 @@ void BKE_displist_fill(const ListBase *dispbase,
static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
ListBase front = {NULL, NULL};
ListBase back = {NULL, NULL};
ListBase front = {nullptr, nullptr};
ListBase back = {nullptr, nullptr};
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
if (dl->type == DL_SURF) {
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) {
if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) {
DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&front, dlnew);
dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -577,9 +579,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
}
}
if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) {
DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&back, dlnew);
dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -634,16 +636,16 @@ static float displist_calc_taper(Depsgraph *depsgraph,
Object *taperobj,
float fac)
{
DispList *dl;
if (taperobj == NULL || taperobj->type != OB_CURVE) {
if (taperobj == nullptr || taperobj->type != OB_CURVE) {
return 1.0;
}
dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL;
if (dl == NULL) {
DispList *dl = taperobj->runtime.curve_cache ?
(DispList *)taperobj->runtime.curve_cache->disp.first :
nullptr;
if (dl == nullptr) {
BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false);
dl = taperobj->runtime.curve_cache->disp.first;
dl = (DispList *)taperobj->runtime.curve_cache->disp.first;
}
if (dl) {
float minx, dx, *fp;
@@ -693,7 +695,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
BKE_displist_free(&(ob->runtime.curve_cache->disp));
}
else {
ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall");
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
"CurveCache for MBall");
}
BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
@@ -738,9 +741,9 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene,
required_mode |= eModifierMode_Editmode;
}
pretessellatePoint = NULL;
pretessellatePoint = nullptr;
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -777,22 +780,22 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
Curve *cu = ob->data;
Curve *cu = (Curve *)ob->data;
int numElems = 0, numVerts = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
ModifierApplyFlag apply_flag = 0;
float(*deformedVerts)[3] = NULL;
float *keyVerts = NULL;
ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
float(*deformedVerts)[3] = nullptr;
float *keyVerts = nullptr;
int required_mode;
bool modified = false;
BKE_modifiers_clear_errors(ob);
if (editmode) {
apply_flag |= MOD_APPLY_USECACHE;
apply_flag = MOD_APPLY_USECACHE;
}
if (for_render) {
apply_flag |= MOD_APPLY_RENDER;
apply_flag = MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -823,7 +826,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
if (pretessellatePoint) {
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -836,7 +839,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
}
mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts);
modified = true;
if (md == pretessellatePoint) {
@@ -869,7 +872,7 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
*r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr;
}
allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
allverts = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__);
fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
@@ -892,27 +895,72 @@ static void displist_vert_coords_apply(ListBase *dispbase, const float (*allvert
}
}
static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
const ModifierEvalContext &mectx,
const Curve *curve,
Object *ob,
Mesh *input_mesh,
GeometrySet &geometry_set)
{
Mesh *mesh_output = nullptr;
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->modifyGeometrySet == nullptr) {
mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh);
}
else {
/* Adds a new mesh component to the geometry set based on the #input_mesh. */
BLI_assert(!geometry_set.has<MeshComponent>());
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(input_mesh, GeometryOwnershipType::Editable);
mesh_component.copy_vertex_group_names_from_object(*ob);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
curve_component.replace(dcurve_from_dna_curve(*curve));
/* Let the modifier change the geometry set. */
mti->modifyGeometrySet(md, &mectx, &geometry_set);
/* Release the mesh from the geometry set again. */
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_output = mesh_component.release();
geometry_set.remove<MeshComponent>();
}
/* Return an empty mesh instead of null. */
if (mesh_output == nullptr) {
mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
BKE_mesh_copy_settings(mesh_output, input_mesh);
}
}
return mesh_output;
}
static void curve_calc_modifiers_post(Depsgraph *depsgraph,
const Scene *scene,
Object *ob,
ListBase *dispbase,
Mesh **r_final,
const bool for_render,
const bool force_mesh_conversion)
const bool force_mesh_conversion,
GeometrySet **r_geometry_set)
{
GeometrySet geometry_set_final;
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
const Curve *cu = ob->data;
const Curve *cu = (const Curve *)ob->data;
int required_mode = 0, totvert = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
Mesh *modified = NULL, *mesh_applied;
float(*vertCos)[3] = NULL;
Mesh *modified = nullptr, *mesh_applied;
float(*vertCos)[3] = nullptr;
int useCache = !for_render;
ModifierApplyFlag apply_flag = 0;
ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
if (for_render) {
apply_flag |= MOD_APPLY_RENDER;
apply_flag = MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -920,9 +968,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
const ModifierEvalContext mectx_deform = {
depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
const ModifierEvalContext mectx_apply = {
depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
depsgraph, ob, useCache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
@@ -935,22 +983,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
if (r_final && *r_final) {
BKE_id_free(NULL, *r_final);
BKE_id_free(nullptr, *r_final);
}
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
}
/* If we need normals, no choice, have to convert to mesh now. */
bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md);
bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md);
/* XXX 2.8 : now that batch cache is stored inside the ob->data
* we need to create a Mesh for each curve that uses modifiers. */
if (modified == NULL /* && need_normal */) {
if (vertCos != NULL) {
if (modified == nullptr /* && need_normal */) {
if (vertCos != nullptr) {
displist_vert_coords_apply(dispbase, vertCos);
}
@@ -976,7 +1024,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (!vertCos) {
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
}
mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert);
mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert);
}
}
else {
@@ -991,8 +1039,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (modified) {
if (vertCos) {
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
BKE_id_free(NULL, modified);
nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1013,19 +1061,21 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
/* Vertex coordinates were applied to necessary data, could free it */
MEM_freeN(vertCos);
vertCos = NULL;
vertCos = nullptr;
}
if (need_normal) {
BKE_mesh_ensure_normals(modified);
}
mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
mesh_applied = modifier_modify_mesh_and_geometry_set(
md, mectx_apply, cu, ob, modified, geometry_set_final);
if (mesh_applied) {
/* Modifier returned a new derived mesh */
if (modified && modified != mesh_applied) { /* Modifier */
BKE_id_free(NULL, modified);
BKE_id_free(nullptr, modified);
}
modified = mesh_applied;
}
@@ -1034,8 +1084,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
if (modified) {
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
BKE_id_free(NULL, modified);
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1046,7 +1097,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
displist_vert_coords_apply(dispbase, vertCos);
MEM_freeN(vertCos);
vertCos = NULL;
vertCos = nullptr;
}
}
@@ -1081,18 +1132,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
MEM_SAFE_FREE(modified->mat);
/* Set flag which makes it easier to see what's going on in a debugger. */
modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
modified->mat = MEM_dupallocN(cu->mat);
modified->mat = (Material **)MEM_dupallocN(cu->mat);
modified->totcol = cu->totcol;
(*r_final) = modified;
}
else {
(*r_final) = NULL;
(*r_final) = nullptr;
}
}
else if (modified != NULL) {
else if (modified != nullptr) {
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
BKE_id_free(NULL, modified);
BKE_id_free(nullptr, modified);
}
if (r_geometry_set) {
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
}
}
@@ -1103,8 +1158,8 @@ static void displist_surf_indices(DispList *dl)
dl->totindex = 0;
index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
"index array nurbs");
index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
"index array nurbs");
for (a = 0; a < dl->parts; a++) {
@@ -1136,8 +1191,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
const bool for_render,
const bool for_orco)
{
ListBase nubase = {NULL, NULL};
Curve *cu = ob->data;
ListBase nubase = {nullptr, nullptr};
Curve *cu = (Curve *)ob->data;
DispList *dl;
float *data;
int len;
@@ -1174,8 +1229,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (nu->pntsv == 1) {
len = SEGMENTSU(nu) * resolu;
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
@@ -1195,13 +1250,13 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
dl->type = DL_SEGM;
}
BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3]));
BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
}
else {
len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->col = nu->mat_nr;
@@ -1233,7 +1288,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (!for_orco) {
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
curve_calc_modifiers_post(
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion);
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion, nullptr);
}
BKE_nurbList_free(&nubase);
@@ -1258,7 +1313,7 @@ static void rotateBevelPiece(const Curve *cu,
vec[1] = fp[2];
vec[2] = 0.0;
if (nbevp == NULL) {
if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
copy_qt_qt(quat, bevp->quat);
}
@@ -1276,7 +1331,7 @@ static void rotateBevelPiece(const Curve *cu,
else {
float sina, cosa;
if (nbevp == NULL) {
if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
sina = bevp->sina;
cosa = bevp->cosa;
@@ -1307,8 +1362,8 @@ static void fillBevelCap(const Nurb *nu,
{
DispList *dl;
dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2");
dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr);
dl->type = DL_POLY;
@@ -1467,9 +1522,10 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
ListBase *dispbase,
const bool for_render,
const bool for_orco,
Mesh **r_final)
Mesh **r_final,
GeometrySet **r_geometry_set)
{
Curve *cu = ob->data;
Curve *cu = (Curve *)ob->data;
/* we do allow duplis... this is only displist on curve level */
if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
@@ -1481,7 +1537,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
else if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
ListBase dlbev;
ListBase nubase = {NULL, NULL};
ListBase nubase = {nullptr, nullptr};
bool force_mesh_conversion = false;
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
@@ -1494,7 +1550,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
ob->runtime.curve_cache->anim_path_accum_length = NULL;
ob->runtime.curve_cache->anim_path_accum_length = nullptr;
}
if (ob->type == OB_FONT) {
@@ -1520,8 +1576,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
else {
const float widfac = cu->width - 1.0f;
BevList *bl = ob->runtime.curve_cache->bev.first;
Nurb *nu = nubase.first;
BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first;
Nurb *nu = (Nurb *)nubase.first;
for (; bl && nu; bl = bl->next, nu = nu->next) {
float *data;
@@ -1532,8 +1588,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
/* exception handling; curve without bevel or extrude, with width correction */
if (BLI_listbase_is_empty(&dlbev)) {
DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev");
dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
BLI_addtail(dispbase, dl);
if (bl->poly != -1) {
@@ -1565,8 +1621,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
}
else {
ListBase bottom_capbase = {NULL, NULL};
ListBase top_capbase = {NULL, NULL};
ListBase bottom_capbase = {nullptr, nullptr};
ListBase top_capbase = {nullptr, nullptr};
float bottom_no[3] = {0.0f};
float top_no[3] = {0.0f};
float first_blend = 0.0f, last_blend = 0.0f;
@@ -1585,8 +1641,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
/* for each part of the bevel use a separate displblock */
DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1");
dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
BLI_addtail(dispbase, dl);
dl->type = DL_SURF;
@@ -1616,7 +1672,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
float radius_factor = 1.0;
float *cur_data = data;
if (cu->taperobj == NULL) {
if (cu->taperobj == nullptr) {
radius_factor = bevp->radius;
}
else {
@@ -1666,7 +1722,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
}
else {
rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data);
rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data);
}
if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
@@ -1707,8 +1763,14 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
curve_calc_modifiers_post(
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion);
curve_calc_modifiers_post(depsgraph,
scene,
ob,
dispbase,
r_final,
for_render,
force_mesh_conversion,
r_geometry_set);
}
if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
@@ -1737,16 +1799,25 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
BKE_object_free_derived_caches(ob);
if (!ob->runtime.curve_cache) {
ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types");
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
}
dispbase = &(ob->runtime.curve_cache->disp);
Mesh *mesh_eval = NULL;
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
Mesh *mesh_eval = nullptr;
GeometrySet *geometry_set_eval = nullptr;
do_makeDispListCurveTypes(
depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval, &geometry_set_eval);
if (mesh_eval != NULL) {
if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
/* Add the final mesh as read-only non-owning component to the geometry set. */
BLI_assert(!geometry_set_eval->has<MeshComponent>());
MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly);
ob->runtime.geometry_set_eval = geometry_set_eval;
}
boundbox_displist_object(ob);
@@ -1759,11 +1830,12 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
Mesh **r_final,
const bool for_orco)
{
if (ob->runtime.curve_cache == NULL) {
ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
if (ob->runtime.curve_cache == nullptr) {
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
"CurveCache for Curve");
}
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final, nullptr);
}
void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
@@ -1797,8 +1869,8 @@ static void boundbox_displist_object(Object *ob)
*/
/* object's BB is calculated from final displist */
if (ob->runtime.bb == NULL) {
ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
}
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);

View File

@@ -0,0 +1,403 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_spline.hh"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
#include "BKE_geometry_set.hh"
#include "attribute_access_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
{
}
CurveComponent::~CurveComponent()
{
this->clear();
}
GeometryComponent *CurveComponent::copy() const
{
CurveComponent *new_component = new CurveComponent();
if (curve_ != nullptr) {
new_component->curve_ = curve_->copy();
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void CurveComponent::clear()
{
BLI_assert(this->is_mutable());
if (curve_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_;
}
curve_ = nullptr;
}
}
bool CurveComponent::has_curve() const
{
return curve_ != nullptr;
}
/* Clear the component and replace it with the new curve. */
void CurveComponent::replace(SplineGroup *curve, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
curve_ = curve;
ownership_ = ownership;
}
SplineGroup *CurveComponent::release()
{
BLI_assert(this->is_mutable());
SplineGroup *curve = curve_;
curve_ = nullptr;
return curve;
}
const SplineGroup *CurveComponent::get_for_read() const
{
return curve_;
}
SplineGroup *CurveComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
curve_ = curve_->copy();
ownership_ = GeometryOwnershipType::Owned;
}
return curve_;
}
bool CurveComponent::is_empty() const
{
return curve_ == nullptr;
}
bool CurveComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void CurveComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
curve_ = curve_->copy();
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Access
* \{ */
int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
{
if (curve_ == nullptr) {
return 0;
}
if (domain == ATTR_DOMAIN_POINT) {
int total = 0;
for (const SplinePtr &spline : curve_->splines) {
total += spline->size();
}
return total;
}
if (domain == ATTR_DOMAIN_CURVE) {
return curve_->splines.size();
}
return 0;
}
namespace blender::bke {
class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArrayPtr (*)(const SplineGroup &data);
using AsWriteAttribute = GVMutableArrayPtr (*)(SplineGroup &data);
using UpdateOnWrite = void (*)(Spline &spline);
const AsReadAttribute as_read_attribute_;
const AsWriteAttribute as_write_attribute_;
public:
BuiltinSplineAttributeProvider(std::string attribute_name,
const CustomDataType attribute_type,
const WritableEnum writable,
const AsReadAttribute as_read_attribute,
const AsWriteAttribute as_write_attribute)
: BuiltinAttributeProvider(std::move(attribute_name),
ATTR_DOMAIN_CURVE,
attribute_type,
BuiltinAttributeProvider::NonCreatable,
writable,
BuiltinAttributeProvider::NonDeletable),
as_read_attribute_(as_read_attribute),
as_write_attribute_(as_write_attribute)
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const SplineGroup *curve = curve_component.get_for_read();
if (curve == nullptr) {
return {};
}
return as_read_attribute_(*curve);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
{
if (writable_ != Writable) {
return {};
}
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
SplineGroup *curve = curve_component.get_for_write();
if (curve == nullptr) {
return {};
}
return as_write_attribute_(*curve);
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
{
return false;
}
bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
bool exists(const GeometryComponent &component) const final
{
return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
}
};
static int get_spline_resolution(const SplinePtr &spline)
{
return spline->resolution();
}
static void set_spline_resolution(SplinePtr &spline, const int resolution)
{
spline->set_resolution(std::max(resolution, 1));
}
static GVArrayPtr make_resolution_read_attribute(const SplineGroup &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
curve.splines.as_span());
}
static GVMutableArrayPtr make_resolution_write_attribute(SplineGroup &curve)
{
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
int,
get_spline_resolution,
set_spline_resolution>>(
curve.splines.as_mutable_span());
}
static float get_spline_length(const SplinePtr &spline)
{
return spline->length();
}
static GVArrayPtr make_length_attribute(const SplineGroup &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, float, get_spline_length>>(
curve.splines.as_span());
}
static bool get_cyclic_value(const SplinePtr &spline)
{
return spline->is_cyclic;
}
static void set_cyclic_value(SplinePtr &spline, const bool value)
{
if (spline->is_cyclic != value) {
spline->is_cyclic = value;
spline->mark_cache_invalid();
}
}
static GVArrayPtr make_cyclic_read_attribute(const SplineGroup &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
curve.splines.as_span());
}
static GVMutableArrayPtr make_cyclic_write_attribute(SplineGroup &curve)
{
return std::make_unique<
fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
curve.splines.as_mutable_span());
}
/**
* \note Currently this uses an inefficient method, copying data from each spline into a single
* array and then passing that as the attribute. Also, currently attributes are only read-only.
*/
class BuiltinPointAttributeProvider final : public BuiltinAttributeProvider {
using GetSplineData = void (*)(const Spline &spline, fn::GMutableSpan r_data);
using SetSplineData = void (*)(Spline &spline, fn::GSpan data);
const GetSplineData get_spline_data_;
const SetSplineData set_spline_data_;
public:
BuiltinPointAttributeProvider(std::string attribute_name,
const CustomDataType attribute_type,
const WritableEnum writable,
const GetSplineData get_spline_data,
const SetSplineData set_spline_data)
: BuiltinAttributeProvider(std::move(attribute_name),
ATTR_DOMAIN_POINT,
attribute_type,
BuiltinAttributeProvider::NonCreatable,
writable,
BuiltinAttributeProvider::NonDeletable),
get_spline_data_(get_spline_data),
set_spline_data_(set_spline_data)
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const SplineGroup *curve = curve_component.get_for_read();
if (curve == nullptr) {
return {};
}
GVArrayPtr varray;
attribute_math::convert_to_static_type(data_type_, [&](auto dummy) {
using T = decltype(dummy);
Array<T> values(curve_component.attribute_domain_size(ATTR_DOMAIN_POINT));
int offset = 0;
for (const SplinePtr &spline : curve->splines) {
const int points_len = spline->size();
MutableSpan<T> spline_data = values.as_mutable_span().slice(offset, points_len);
fn::GMutableSpan generic_spline_data(spline_data);
get_spline_data_(*spline, generic_spline_data);
offset += points_len;
}
varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
return varray;
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
{
return false;
}
bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
bool exists(const GeometryComponent &component) const final
{
return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
}
};
static void get_spline_radius_data(const Spline &spline, fn::GMutableSpan r_data)
{
MutableSpan<float> r_span = r_data.typed<float>();
r_span.copy_from(spline.radii());
}
static void get_spline_position_data(const Spline &spline, fn::GMutableSpan r_data)
{
MutableSpan<float3> r_span = r_data.typed<float3>();
r_span.copy_from(spline.positions());
}
/**
* In this function all the attribute providers for a curve component are created. Most data
* in this function is statically allocated, because it does not change over time.
*/
static ComponentAttributeProviders create_attribute_providers_for_curve()
{
static BuiltinSplineAttributeProvider resolution("resolution",
CD_PROP_INT32,
BuiltinAttributeProvider::Writable,
make_resolution_read_attribute,
make_resolution_write_attribute);
static BuiltinSplineAttributeProvider length(
"length", CD_PROP_FLOAT, BuiltinAttributeProvider::Readonly, make_length_attribute, nullptr);
static BuiltinSplineAttributeProvider cyclic("cyclic",
CD_PROP_BOOL,
BuiltinAttributeProvider::Writable,
make_cyclic_read_attribute,
make_cyclic_write_attribute);
static BuiltinPointAttributeProvider position("position",
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Readonly,
get_spline_position_data,
nullptr);
static BuiltinPointAttributeProvider radius("radius",
CD_PROP_FLOAT,
BuiltinAttributeProvider::Readonly,
get_spline_radius_data,
nullptr);
return ComponentAttributeProviders({&resolution, &length, &cyclic, &position, &radius}, {});
}
} // namespace blender::bke
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
{
static blender::bke::ComponentAttributeProviders providers =
blender::bke::create_attribute_providers_for_curve();
return &providers;
}
/** \} */

View File

@@ -44,6 +44,7 @@ GeometryComponent *InstancesComponent::copy() const
InstancesComponent *new_component = new InstancesComponent();
new_component->transforms_ = transforms_;
new_component->instanced_data_ = instanced_data_;
new_component->ids_ = ids_;
return new_component;
}
@@ -51,6 +52,7 @@ void InstancesComponent::clear()
{
instanced_data_.clear();
transforms_.clear();
ids_.clear();
}
void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)

View File

@@ -32,7 +32,7 @@
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
using blender::bke::ReadAttributePtr;
using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@@ -201,14 +201,14 @@ namespace blender::bke {
template<typename T>
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
const TypedReadAttribute<T> &attribute,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int loop_index : IndexRange(mesh.totloop)) {
const T value = attribute[loop_index];
const T value = old_values[loop_index];
const MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
@@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
* iterate over all loops anyway. */
Array<T> values(mesh.totvert);
adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
const TypedReadAttribute<T> &attribute,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const int vertex_index = mesh.mloop[loop_index].v;
r_values[loop_index] = attribute[vertex_index];
r_values[loop_index] = old_values[vertex_index];
}
}
static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
@@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
* when an algorithm only accesses very few of the corner values. However, for the algorithms
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
Array<T> values(mesh.totloop);
adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
std::move(values));
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
return new_attribute;
return new_varray;
}
/**
@@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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));
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
}
}
static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
/**
@@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
/**
@@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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));
adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -547,7 +529,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
/* For every corner, mix the values from the adjacent edges on the face. */
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const int loop_index_prev = (loop_index - 1) % poly.totloop;
const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * 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]);
@@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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));
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
template<typename T>
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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));
adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
/**
@@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
const Span<T> old_values,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
ReadAttributePtr attribute)
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
ReadAttributePtr new_attribute;
const CustomDataType data_type = attribute->custom_data_type();
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_attribute;
return new_varray;
}
} // namespace blender::bke
ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
const AttributeDomain new_domain) const
blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
blender::fn::GVArrayPtr varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
if (!attribute) {
if (!varray) {
return {};
}
if (attribute->size() == 0) {
if (varray->size() == 0) {
return {};
}
const AttributeDomain old_domain = attribute->domain();
if (old_domain == new_domain) {
return attribute;
if (from_domain == to_domain) {
return varray;
}
switch (old_domain) {
switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
switch (new_domain) {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
switch (new_domain) {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
switch (new_domain) {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
switch (new_domain) {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
default:
break;
}
@@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
AttributeDomain Domain>
static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
Domain, Span<StructT>((const StructT *)data, domain_size));
return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
Span<StructT>((const StructT *)data, domain_size));
}
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
void (*SetFunc)(StructT &, const ElemT &),
AttributeDomain Domain>
static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
void (*SetFunc)(StructT &, ElemT)>
static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
{
return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
Domain, MutableSpan<StructT>((StructT *)data, domain_size));
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
MutableSpan<StructT>((StructT *)data, domain_size));
}
static float3 get_vertex_position(const MVert &vert)
@@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert)
return float3(vert.co);
}
static void set_vertex_position(MVert &vert, const float3 &position)
static void set_vertex_position(MVert &vert, float3 position)
{
copy_v3_v3(vert.co, position);
}
@@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly)
return static_cast<int>(mpoly.mat_nr);
}
static void set_material_index(MPoly &mpoly, const int &index)
static void set_material_index(MPoly &mpoly, int index)
{
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
}
@@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly)
return mpoly.flag & ME_SMOOTH;
}
static void set_shade_smooth(MPoly &mpoly, const bool &value)
static void set_shade_smooth(MPoly &mpoly, bool value)
{
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
}
@@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv)
return float2(uv.uv);
}
static void set_loop_uv(MLoopUV &uv, const float2 &co)
static void set_loop_uv(MLoopUV &uv, float2 co)
{
copy_v2_v2(uv.uv, co);
}
@@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col)
return linear_color;
}
static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
static void set_loop_color(MLoopCol &col, Color4f linear_color)
{
linearrgb_to_srgb_uchar4(&col.r, linear_color);
}
@@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge)
return edge.crease / 255.0f;
}
static void set_crease(MEdge &edge, const float &value)
static void set_crease(MEdge &edge, float value)
{
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
}
class VertexWeightWriteAttribute final : public WriteAttribute {
class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
: WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
dverts_(dverts),
dvert_index_(dvert_index)
VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
: VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
void get_internal(const int64_t index, void *r_value) const override
float get_impl(const int64_t index) const override
{
get_internal(dverts_, dvert_index_, index, r_value);
return get_internal(dverts_, dvert_index_, index);
}
void set_internal(const int64_t index, const void *value) override
void set_impl(const int64_t index, const float value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
weight->weight = *reinterpret_cast<const float *>(value);
weight->weight = value;
}
static void get_internal(const MDeformVert *dverts,
const int dvert_index,
const int64_t index,
void *r_value)
static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
{
if (dverts == nullptr) {
*(float *)r_value = 0.0f;
return;
return 0.0f;
}
const MDeformVert &dvert = dverts[index];
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
if (weight.def_nr == dvert_index) {
*(float *)r_value = weight.weight;
return;
return weight.weight;
}
}
*(float *)r_value = 0.0f;
return 0.0f;
}
};
class VertexWeightReadAttribute final : public ReadAttribute {
class VArray_For_VertexWeights final : public VArray<float> {
private:
const MDeformVert *dverts_;
const int dvert_index_;
public:
VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
: ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
dverts_(dverts),
dvert_index_(dvert_index)
VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
: VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
void get_internal(const int64_t index, void *r_value) const override
float get_impl(const int64_t index) const override
{
VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
}
};
@@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
*/
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
@@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
if (mesh == nullptr || mesh->dvert == nullptr) {
static const float default_value = 0.0f;
return std::make_unique<ConstantReadAttribute>(
ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
return {std::make_unique<fn::GVArray_For_SingleValueRef>(
CPPType::get<float>(), mesh->totvert, &default_value),
ATTR_DOMAIN_POINT};
}
return std::make_unique<VertexWeightReadAttribute>(
mesh->dvert, mesh->totvert, vertex_group_index);
return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
ATTR_DOMAIN_POINT};
}
WriteAttributePtr try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
@@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
}
return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
mesh->dvert, mesh->totvert, vertex_group_index);
return {
std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
@@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
{
}
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
@@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
return std::make_unique<ArrayReadAttribute<float3>>(
ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
return std::make_unique<fn::GVArray_For_Span<float3>>(
Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
@@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
@@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return false;
}
bool try_create(GeometryComponent &UNUSED(component)) const final
bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
@@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
make_derived_write_attribute<MVert,
float3,
get_vertex_position,
set_vertex_position,
ATTR_DOMAIN_POINT>,
make_derived_read_attribute<MVert, float3, get_vertex_position>,
make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
tag_normals_dirty_when_writing_position);
static NormalAttributeProvider normal;
@@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
make_derived_write_attribute<MPoly,
int,
get_material_index,
set_material_index,
ATTR_DOMAIN_FACE>,
make_derived_read_attribute<MPoly, int, get_material_index>,
make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
nullptr);
static BuiltinCustomDataLayerProvider shade_smooth(
@@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
make_derived_write_attribute<MPoly,
bool,
get_shade_smooth,
set_shade_smooth,
ATTR_DOMAIN_FACE>,
make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
nullptr);
static BuiltinCustomDataLayerProvider crease(
@@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
edge_access,
make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
make_derived_read_attribute<MEdge, float, get_crease>,
make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
nullptr);
static NamedLegacyCustomDataProvider uvs(
@@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT2,
CD_MLOOPUV,
corner_access,
make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
static NamedLegacyCustomDataProvider vertex_colors(
ATTR_DOMAIN_CORNER,
CD_PROP_COLOR,
CD_MLOOPCOL,
corner_access,
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
make_derived_write_attribute<MLoopCol,
Color4f,
get_loop_color,
set_loop_color,
ATTR_DOMAIN_CORNER>);
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);

View File

@@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
template<typename T, AttributeDomain Domain>
static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
template<typename T>
static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
}
template<typename T, AttributeDomain Domain>
static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
template<typename T>
static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
{
return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
MutableSpan<T>((T *)data, domain_size));
}
/**
@@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
},
update_custom_data_pointers};
static BuiltinCustomDataLayerProvider position(
"position",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
nullptr);
static BuiltinCustomDataLayerProvider radius(
"radius",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
nullptr);
static BuiltinCustomDataLayerProvider position("position",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
make_array_write_attribute<float3>,
nullptr);
static BuiltinCustomDataLayerProvider radius("radius",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
make_array_write_attribute<float>,
nullptr);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
}

View File

@@ -24,6 +24,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
@@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new InstancesComponent();
case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
case GEO_COMPONENT_TYPE_CURVE:
return new CurveComponent();
}
BLI_assert_unreachable();
return nullptr;
@@ -182,6 +185,13 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (volume != nullptr) {
BKE_volume_min_max(volume, *r_min, *r_max);
}
const SplineGroup *curve = this->get_curve_for_read();
if (curve != nullptr) {
/* Note the the choice of using the evaluated positions is somewhat arbitrary, and may counter
* the idea that the curve is the reduced set of control point information, but it may also be
* the expected result. */
curve->bounds_min_max(*r_min, *r_max, true);
}
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@@ -252,6 +262,13 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns a read-only curve or null. */
const SplineGroup *GeometrySet::get_curve_for_read() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -273,6 +290,13 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
/* Returns true when the geometry set has a curve component that has a curve. */
bool GeometrySet::has_curve() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return component != nullptr && component->has_curve();
}
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -292,6 +316,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
/* Create a new geometry set that only contains the given curve. */
GeometrySet GeometrySet::create_with_curve(SplineGroup *curve, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
component.replace(curve, ownership);
return geometry_set;
}
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -299,6 +332,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
/* Clear the existing curve and replace it with the given one. */
void GeometrySet::replace_curve(SplineGroup *curve, GeometryOwnershipType ownership)
{
CurveComponent &component = this->get_component_for_write<CurveComponent>();
component.replace(curve, ownership);
}
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
@@ -306,6 +346,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy
pointcloud_component.replace(pointcloud, ownership);
}
/* Clear the existing volume and replace with the given one. */
void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership)
{
VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>();
volume_component.replace(volume, ownership);
}
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
@@ -327,6 +374,13 @@ Volume *GeometrySet::get_volume_for_write()
return component.get_for_write();
}
/* Returns a mutable curve or null. No ownership is transferred. */
SplineGroup *GeometrySet::get_curve_for_write()
{
CurveComponent &component = this->get_component_for_write<CurveComponent>();
return component.get_for_write();
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -449,13 +449,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
result.attribute_try_create(entry.key, domain_output, data_type_output);
WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
write_attribute->domain() != domain_output) {
result.attribute_try_create(
entry.key, domain_output, data_type_output, AttributeInitDefault());
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
write_attribute.domain != domain_output) {
continue;
}
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -467,11 +469,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
fn::GSpan src_span = source_attribute->get_span();
fn::GVArray_GSpan src_span{*source_attribute};
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
@@ -486,7 +488,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
}
}
write_attribute->apply_span();
dst_span.save();
}
}

View File

@@ -2433,7 +2433,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
mat_idx = gpencil_material_find_index_by_name(ob_gp, element_name);
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
if (ma != NULL) {

View File

@@ -303,6 +303,7 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain,
/* Can be called with both old_collection and new_collection being NULL,
* this means we have to check whole Main database then. */
static void libblock_remap_data_postprocess_collection_update(Main *bmain,
Collection *owner_collection,
Collection *UNUSED(old_collection),
Collection *new_collection)
{
@@ -311,7 +312,7 @@ static void libblock_remap_data_postprocess_collection_update(Main *bmain,
* and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check
* whole existing collections for NULL pointers.
* I'd consider optimizing that whole collection remapping process a TODO for later. */
BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/);
BKE_collections_child_remove_nulls(bmain, owner_collection, NULL /*old_collection*/);
}
else {
/* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from
@@ -523,7 +524,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
bmain, (Collection *)old_id, (Collection *)new_id);
bmain, NULL, (Collection *)old_id, (Collection *)new_id);
break;
case ID_ME:
case ID_CU:
@@ -628,6 +629,12 @@ void BKE_libblock_relink_ex(
switch (GS(id->name)) {
case ID_SCE:
case ID_GR: {
/* Note: here we know which collection we have affected, so at lest for NULL children
* detection we can only process that one.
* This is also a required fix in case `id` would not be in Main anymore, which can happen
* e.g. when called from `id_delete`. */
Collection *owner_collection = (GS(id->name) == ID_GR) ? (Collection *)id :
((Scene *)id)->master_collection;
if (old_id) {
switch (GS(old_id->name)) {
case ID_OB:
@@ -636,7 +643,7 @@ void BKE_libblock_relink_ex(
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
bmain, (Collection *)old_id, (Collection *)new_id);
bmain, owner_collection, (Collection *)old_id, (Collection *)new_id);
break;
default:
break;
@@ -644,7 +651,7 @@ void BKE_libblock_relink_ex(
}
else {
/* No choice but to check whole objects/collections. */
libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL);
libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL);
libblock_remap_data_postprocess_object_update(bmain, NULL, NULL);
}
break;

View File

@@ -1065,6 +1065,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
Curve *curve = (Curve *)object->data;
Curve remapped_curve = *curve;
Object remapped_object = *object;
remapped_object.runtime.bb = NULL;
remapped_object.data = &remapped_curve;
/* Clear all modifiers for the bevel object.
@@ -1077,6 +1078,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object bevel_object = {{NULL}};
if (remapped_curve.bevobj != NULL) {
bevel_object = *remapped_curve.bevobj;
bevel_object.runtime.bb = NULL;
BLI_listbase_clear(&bevel_object.modifiers);
remapped_curve.bevobj = &bevel_object;
}
@@ -1085,6 +1087,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object taper_object = {{NULL}};
if (remapped_curve.taperobj != NULL) {
taper_object = *remapped_curve.taperobj;
taper_object.runtime.bb = NULL;
BLI_listbase_clear(&taper_object.modifiers);
remapped_curve.taperobj = &taper_object;
}
@@ -1107,6 +1110,10 @@ static void curve_to_mesh_eval_ensure(Object *object)
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
MEM_SAFE_FREE(remapped_object.runtime.bb);
MEM_SAFE_FREE(taper_object.runtime.bb);
MEM_SAFE_FREE(bevel_object.runtime.bb);
BKE_object_free_curve_cache(&bevel_object);
BKE_object_free_curve_cache(&taper_object);
}
@@ -1535,7 +1542,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
int did_shapekeys = 0;
bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
@@ -1590,7 +1597,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
}
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
did_shapekeys = 1;
did_shapekeys = true;
}
/* copy texture space */
@@ -1619,13 +1626,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
totedge);
}
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
/* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
* BKE_mesh_update_customdata_pointers() call below. */
tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
CustomData_add_layer(&tmp.ldata,
CD_MLOOP,
CD_ASSIGN,
(alloctype == CD_ASSIGN) ? mesh_src->mloop :
MEM_dupallocN(mesh_src->mloop),
tmp.totloop);
CustomData_add_layer(&tmp.pdata,
CD_MPOLY,
CD_ASSIGN,
(alloctype == CD_ASSIGN) ? mesh_src->mpoly :
MEM_dupallocN(mesh_src->mpoly),
tmp.totpoly);
}
/* object had got displacement layer, should copy this layer to save sculpted data */
@@ -1634,15 +1646,16 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
if (totloop == mesh_dst->totloop) {
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
if (alloctype == CD_ASSIGN) {
/* Assign NULL to prevent double-free. */
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
}
}
}
/* yes, must be before _and_ after tessellate */
BKE_mesh_update_customdata_pointers(&tmp, false);
/* since 2.65 caller must do! */
// BKE_mesh_tessface_calc(&tmp);
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);

View File

@@ -51,7 +51,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm
(axis == 1 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Y) ||
(axis == 2 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Z));
const float bisect_distance = 0.001f;
const float bisect_distance = mmd->bisect_threshold;
Mesh *result;
BMesh *bm;

View File

@@ -0,0 +1,158 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_attribute_math.hh"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
namespace blender::bke::mesh_surface_sample {
static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
return {looptris, looptris_len};
}
template<typename T>
BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
const int v0_index = mesh.mloop[looptri.tri[0]].v;
const int v1_index = mesh.mloop[looptri.tri[1]].v;
const int v2_index = mesh.mloop[looptri.tri[2]].v;
const T v0 = data_in[v0_index];
const T v1 = data_in[v1_index];
const T v2 = data_in[v2_index];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
data_out[i] = interpolated_value;
}
}
void sample_point_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const GVArray &data_in,
const GMutableSpan data_out)
{
BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_out.size() == bary_coords.size());
BLI_assert(data_in.size() == mesh.totvert);
BLI_assert(data_in.type() == data_out.type());
const CPPType &type = data_in.type();
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
sample_point_attribute<T>(
mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
});
}
template<typename T>
BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : bary_coords.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
const int loop_index_0 = looptri.tri[0];
const int loop_index_1 = looptri.tri[1];
const int loop_index_2 = looptri.tri[2];
const T v0 = data_in[loop_index_0];
const T v1 = data_in[loop_index_1];
const T v2 = data_in[loop_index_2];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
data_out[i] = interpolated_value;
}
}
void sample_corner_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const Span<float3> bary_coords,
const GVArray &data_in,
const GMutableSpan data_out)
{
BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_out.size() == bary_coords.size());
BLI_assert(data_in.size() == mesh.totloop);
BLI_assert(data_in.type() == data_out.type());
const CPPType &type = data_in.type();
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
sample_corner_attribute<T>(
mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
});
}
template<typename T>
void sample_face_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const VArray<T> &data_in,
const MutableSpan<T> data_out)
{
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
for (const int i : data_out.index_range()) {
const int looptri_index = looptri_indices[i];
const MLoopTri &looptri = looptris[looptri_index];
const int poly_index = looptri.poly;
data_out[i] = data_in[poly_index];
}
}
void sample_face_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
const GVArray &data_in,
const GMutableSpan data_out)
{
BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_in.size() == mesh.totpoly);
BLI_assert(data_in.type() == data_out.type());
const CPPType &type = data_in.type();
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
});
}
} // namespace blender::bke::mesh_surface_sample

View File

@@ -4946,6 +4946,10 @@ static void registerGeometryNodes()
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
register_node_type_geo_curve_sample_points();
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_transform_test();
register_node_type_geo_curve_trim();
register_node_type_geo_edge_split();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
@@ -4968,6 +4972,7 @@ static void registerGeometryNodes()
register_node_type_geo_sample_texture();
register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();

View File

@@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
if (ob->type == OB_HAIR) {
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
if (ob->type == OB_POINTCLOUD) {
if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
return (mti->modifyGeometrySet != NULL);
}
if (ob->type == OB_VOLUME) {
return (mti->modifyVolume != NULL);
}
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
return false;

View File

@@ -2636,6 +2636,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
bool used_multiple_passes = false;
bool run_callbacks = DEG_id_type_any_updated(depsgraph);
if (run_callbacks) {
@@ -2672,8 +2673,6 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
* If there are no relations changed by the callback this call will do nothing. */
DEG_graph_relations_update(depsgraph);
}
/* Inform editors about possible changes. */
DEG_editors_update(bmain, depsgraph, scene, view_layer, false);
/* If user callback did not tag anything for update we can skip second iteration.
* Otherwise we update scene once again, but without running callbacks to bring
@@ -2682,8 +2681,22 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
break;
}
/* Clear recalc flags for second pass, but back them up for editors update. */
const bool backup = true;
DEG_ids_clear_recalc(depsgraph, backup);
used_multiple_passes = true;
run_callbacks = false;
}
/* Inform editors about changes, using recalc flags from both passes. */
if (used_multiple_passes) {
DEG_ids_restore_recalc(depsgraph);
}
const bool is_time_update = false;
DEG_editors_update(depsgraph, is_time_update);
const bool backup = false;
DEG_ids_clear_recalc(depsgraph, backup);
}
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
@@ -2697,11 +2710,11 @@ void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
}
/* applies changes right away, does all sets too */
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool clear_recalc)
{
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
Main *bmain = DEG_get_bmain(depsgraph);
bool used_multiple_passes = false;
/* Keep this first. */
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE);
@@ -2738,16 +2751,38 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
DEG_graph_relations_update(depsgraph);
}
/* Inform editors about possible changes. */
DEG_editors_update(bmain, depsgraph, scene, view_layer, true);
/* If user callback did not tag anything for update we can skip second iteration.
* Otherwise we update scene once again, but without running callbacks to bring
* scene to a fully evaluated state with user modifications taken into account. */
if (DEG_is_fully_evaluated(depsgraph)) {
break;
}
/* Clear recalc flags for second pass, but back them up for editors update. */
const bool backup = true;
DEG_ids_clear_recalc(depsgraph, backup);
used_multiple_passes = true;
}
/* Inform editors about changes, using recalc flags from both passes. */
if (used_multiple_passes) {
DEG_ids_restore_recalc(depsgraph);
}
const bool is_time_update = true;
DEG_editors_update(depsgraph, is_time_update);
/* Clear recalc flags, can be skipped for e.g. renderers that will read these
* and clear the flags later. */
if (clear_recalc) {
const bool backup = false;
DEG_ids_clear_recalc(depsgraph, backup);
}
}
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
{
BKE_scene_graph_update_for_newframe_ex(depsgraph, true);
}
/**
@@ -3507,8 +3542,8 @@ GHash *BKE_scene_undo_depsgraphs_extract(Main *bmain)
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
if (scene->depsgraph_hash == NULL) {
/* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will be
* built so this pointer may be NULL. */
/* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will
* be built so this pointer may be NULL. */
continue;
}
for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;

View File

@@ -0,0 +1,339 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_array.hh"
#include "BLI_span.hh"
#include "BKE_spline.hh"
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
Spline::Type Spline::type() const
{
return this->type_;
}
int Spline::evaluated_edges_size() const
{
const int points_len = this->evaluated_points_size();
return this->is_cyclic ? points_len : points_len - 1;
}
float Spline::length() const
{
return this->evaluated_lengths().last();
}
int Spline::segments_size() const
{
const int points_len = this->size();
return this->is_cyclic ? points_len : points_len - 1;
}
static void accumulate_lengths(Span<float3> positions,
const bool is_cyclic,
MutableSpan<float> lengths)
{
float length = 0.0f;
for (const int i : IndexRange(positions.size() - 1)) {
length += float3::distance(positions[i], positions[i + 1]);
lengths[i] = length;
}
if (is_cyclic) {
lengths.last() = length + float3::distance(positions.last(), positions.first());
}
}
/**
* Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
* length of the subsequent segment, i.e. the first value is the length of the first segment rather
* than 0. This calculation is rather trivial, and only depends on the evaluated positions.
* However, the results are used often, so it makes sense to cache it.
*/
Span<float> Spline::evaluated_lengths() const
{
if (!this->length_cache_dirty_) {
return evaluated_lengths_cache_;
}
std::lock_guard lock{this->length_cache_mutex_};
if (!this->length_cache_dirty_) {
return evaluated_lengths_cache_;
}
const int total = this->evaluated_edges_size();
this->evaluated_lengths_cache_.resize(total);
Span<float3> positions = this->evaluated_positions();
accumulate_lengths(positions, this->is_cyclic, this->evaluated_lengths_cache_);
this->length_cache_dirty_ = false;
return evaluated_lengths_cache_;
}
static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
{
const float3 dir_prev = (middle - prev).normalized();
const float3 dir_next = (next - middle).normalized();
return (dir_prev + dir_next).normalized();
}
static void calculate_tangents(Span<float3> positions,
const bool is_cyclic,
MutableSpan<float3> tangents)
{
if (positions.size() == 1) {
return;
}
for (const int i : IndexRange(1, positions.size() - 2)) {
tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
}
if (is_cyclic) {
const float3 &second_to_last = positions[positions.size() - 2];
const float3 &last = positions.last();
const float3 &first = positions.first();
const float3 &second = positions[1];
tangents.first() = direction_bisect(last, first, second);
tangents.last() = direction_bisect(second_to_last, last, first);
}
else {
tangents.first() = (positions[1] - positions[0]).normalized();
tangents.last() = (positions.last() - positions[positions.size() - 1]).normalized();
}
}
/**
* Return non-owning access to the direction of the curve at each evaluated point.
*/
Span<float3> Spline::evaluated_tangents() const
{
if (!this->tangent_cache_dirty_) {
return evaluated_tangents_cache_;
}
std::lock_guard lock{this->tangent_cache_mutex_};
if (!this->tangent_cache_dirty_) {
return evaluated_tangents_cache_;
}
const int total = this->evaluated_points_size();
this->evaluated_tangents_cache_.resize(total);
Span<float3> positions = this->evaluated_positions();
calculate_tangents(positions, this->is_cyclic, this->evaluated_tangents_cache_);
this->correct_end_tangents();
this->tangent_cache_dirty_ = false;
return evaluated_tangents_cache_;
}
#if 0 /* Not supported yet, has errors. */
static float3 initial_normal(const float3 first_tangent)
{
/* TODO: Should be is "almost" zero. */
if (first_tangent.is_zero()) {
return float3(0.0f, 0.0f, 1.0f);
}
const float3 normal = float3::cross(first_tangent, float3(0.0f, 0.0f, 1.0f));
if (!normal.is_zero()) {
return normal.normalized();
}
return float3::cross(first_tangent, float3(0.0f, 1.0f, 0.0f)).normalized();
}
static float3 rotate_around_axis(const float3 dir, const float3 axis, const float angle)
{
BLI_ASSERT_UNIT_V3(axis);
const float3 scaled_axis = axis * float3::dot(dir, axis);
const float3 sub = dir - scaled_axis;
const float3 cross = float3::cross(sub, sub);
const float sin = std::sin(angle);
const float cos = std::cos(angle);
return (scaled_axis + sub * cos + cross * sin).normalized();
}
static float3 project_on_center_plane(const float3 vector, const float3 plane_normal)
{
BLI_ASSERT_UNIT_V3(plane_normal);
const float distance = float3::dot(vector, plane_normal);
const float3 projection_vector = plane_normal * -distance;
return vector + projection_vector;
}
static float3 propagate_normal(const float3 last_normal,
const float3 last_tangent,
const float3 current_tangent)
{
const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
if (angle == 0.0f) {
return last_normal;
}
const float3 axis = float3::cross(last_tangent, current_tangent).normalized();
const float3 new_normal = rotate_around_axis(last_normal, axis, angle);
return project_on_center_plane(new_normal, current_tangent).normalized();
}
static void apply_rotation_gradient(Span<float3> tangents,
MutableSpan<float3> normals,
const float full_angle)
{
float remaining_rotation = full_angle;
float done_rotation = 0.0f;
for (const int i : IndexRange(1, normals.size() - 1)) {
if (angle_v3v3(tangents[i], tangents[i - 1]) < 0.001f) {
normals[i] = rotate_around_axis(normals[i], tangents[i], done_rotation);
}
else {
const float angle = remaining_rotation / (normals.size() - i);
normals[i] = rotate_around_axis(normals[i], tangents[i], angle + done_rotation);
remaining_rotation -= angle;
done_rotation += angle;
}
}
}
static void make_normals_cyclic(Span<float3> tangents, MutableSpan<float3> normals)
{
const float3 last_normal = propagate_normal(normals.last(), tangents.last(), tangents.first());
float angle = angle_normalized_v3v3(normals.first(), last_normal);
const float3 cross = float3::cross(normals.first(), last_normal);
if (float3::dot(cross, tangents.first()) <= 0.0f) {
angle = -angle;
}
apply_rotation_gradient(tangents, normals, -angle);
}
/* This algorithm is a copy from animation nodes bezier normal calculation.
* TODO: Explore different methods, this also doesn't work right now. */
static void calculate_normals_minimum_twist(Span<float3> tangents,
const bool is_cyclic,
MutableSpan<float3> normals)
{
if (normals.size() == 1) {
normals.first() = float3(1.0f, 0.0f, 0.0f);
return;
}
/* Start by calculating a simple normal for the first point. */
normals[0] = initial_normal(tangents[0]);
/* Then propogate that normal along the spline. */
for (const int i : IndexRange(1, normals.size() - 1)) {
normals[i] = propagate_normal(normals[i - 1], tangents[i - 1], tangents[i]);
}
if (is_cyclic) {
make_normals_cyclic(tangents, normals);
}
}
#endif
static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
{
for (const int i : normals.index_range()) {
normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
}
}
/**
* Return non-owning access to the direction vectors perpendicular to the tangents at every
* evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
*/
Span<float3> Spline::evaluated_normals() const
{
if (!this->normal_cache_dirty_) {
return evaluated_normals_cache_;
}
std::lock_guard lock{this->normal_cache_mutex_};
if (!this->normal_cache_dirty_) {
return evaluated_normals_cache_;
}
const int total = this->evaluated_points_size();
this->evaluated_normals_cache_.resize(total);
Span<float3> tangents = this->evaluated_tangents();
#if 0 /* Not supported yet, has errors. */
switch (this->normal_mode) {
case NormalCalculationMode::Minimum:
calculate_normals_minimum_twist(tangents, is_cyclic, this->evaluated_normals_cache_);
break;
case NormalCalculationMode::ZUp:
break;
case NormalCalculationMode::Tangent:
calculate_normals_tangent(tangents, this->evaluated_normals_cache_);
break;
}
#endif
calculate_normals_z_up(tangents, this->evaluated_normals_cache_);
this->normal_cache_dirty_ = false;
return evaluated_normals_cache_;
}
Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
{
return this->lookup_evaluated_length(this->length() * factor);
}
/**
* \note This does not support extrapolation currently.
*/
Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
{
BLI_assert(length >= 0.0f && length <= this->length());
Span<float> lengths = this->evaluated_lengths();
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
const int index = offset - lengths.begin();
const int next_index = (index == this->size() - 1) ? 0 : index + 1;
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
const float factor = (length - previous_length) / (lengths[index] - previous_length);
return LookupResult{index, next_index, factor};
}
void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
{
Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
for (const float3 &position : positions) {
minmax_v3v3_v3(min, max, position);
}
}

View File

@@ -0,0 +1,483 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_array.hh"
#include "BLI_span.hh"
#include "BLI_task.hh"
#include "BKE_spline.hh"
using blender::Array;
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
SplinePtr BezierSpline::copy() const
{
SplinePtr new_spline = std::make_unique<BezierSpline>(*this);
return new_spline;
}
int BezierSpline::size() const
{
const int size = this->positions_.size();
BLI_assert(this->handle_types_start_.size() == size);
BLI_assert(this->handle_positions_start_.size() == size);
BLI_assert(this->handle_types_end_.size() == size);
BLI_assert(this->handle_positions_end_.size() == size);
BLI_assert(this->radii_.size() == size);
BLI_assert(this->tilts_.size() == size);
return size;
}
int BezierSpline::resolution() const
{
return this->resolution_;
}
void BezierSpline::set_resolution(const int value)
{
this->resolution_ = value;
this->mark_cache_invalid();
}
MutableSpan<float3> BezierSpline::positions()
{
return this->positions_;
}
Span<float3> BezierSpline::positions() const
{
return this->positions_;
}
MutableSpan<float> BezierSpline::radii()
{
return this->radii_;
}
Span<float> BezierSpline::radii() const
{
return this->radii_;
}
MutableSpan<float> BezierSpline::tilts()
{
return this->tilts_;
}
Span<float> BezierSpline::tilts() const
{
return this->tilts_;
}
Span<BezierSpline::HandleType> BezierSpline::handle_types_start() const
{
return this->handle_types_start_;
}
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_start()
{
return this->handle_types_start_;
}
Span<float3> BezierSpline::handle_positions_start() const
{
return this->handle_positions_start_;
}
MutableSpan<float3> BezierSpline::handle_positions_start()
{
return this->handle_positions_start_;
}
Span<BezierSpline::HandleType> BezierSpline::handle_types_end() const
{
return this->handle_types_end_;
}
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_end()
{
return this->handle_types_end_;
}
Span<float3> BezierSpline::handle_positions_end() const
{
return this->handle_positions_end_;
}
MutableSpan<float3> BezierSpline::handle_positions_end()
{
return this->handle_positions_end_;
}
void BezierSpline::add_point(const float3 position,
const HandleType handle_type_start,
const float3 handle_position_start,
const HandleType handle_type_end,
const float3 handle_position_end,
const float radius,
const float tilt)
{
handle_types_start_.append(handle_type_start);
handle_positions_start_.append(handle_position_start);
positions_.append(position);
handle_types_end_.append(handle_type_end);
handle_positions_end_.append(handle_position_end);
radii_.append(radius);
tilts_.append(tilt);
}
void BezierSpline::drop_front(const int count)
{
BLI_assert(this->size() - count > 0);
this->handle_types_start_.remove(0, count);
this->handle_positions_start_.remove(0, count);
this->positions_.remove(0, count);
this->handle_types_end_.remove(0, count);
this->handle_positions_end_.remove(0, count);
this->radii_.remove(0, count);
this->tilts_.remove(0, count);
this->mark_cache_invalid();
}
void BezierSpline::drop_back(const int count)
{
const int new_size = this->size() - count;
BLI_assert(new_size > 0);
this->handle_types_start_.resize(new_size);
this->handle_positions_start_.resize(new_size);
this->positions_.resize(new_size);
this->handle_types_end_.resize(new_size);
this->handle_positions_end_.resize(new_size);
this->radii_.resize(new_size);
this->tilts_.resize(new_size);
this->mark_cache_invalid();
}
bool BezierSpline::point_is_sharp(const int index) const
{
return ELEM(handle_types_start_[index], HandleType::Vector, HandleType::Free) ||
ELEM(handle_types_end_[index], HandleType::Vector, HandleType::Free);
}
bool BezierSpline::handle_start_is_automatic(const int index) const
{
return ELEM(handle_types_start_[index], HandleType::Free, HandleType::Align);
}
bool BezierSpline::handle_end_is_automatic(const int index) const
{
return ELEM(handle_types_end_[index], HandleType::Free, HandleType::Align);
}
void BezierSpline::move_control_point(const int index, const float3 new_position)
{
const float3 position_delta = new_position - positions_[index];
if (!this->handle_start_is_automatic(index)) {
handle_positions_start_[index] += position_delta;
}
if (!this->handle_end_is_automatic(index)) {
handle_positions_end_[index] += position_delta;
}
positions_[index] = new_position;
}
bool BezierSpline::segment_is_vector(const int index) const
{
if (index == this->size() - 1) {
BLI_assert(this->is_cyclic);
return this->handle_types_end_.last() == HandleType::Vector &&
this->handle_types_start_.first() == HandleType::Vector;
}
return this->handle_types_end_[index] == HandleType::Vector &&
this->handle_types_start_[index + 1] == HandleType::Vector;
}
void BezierSpline::mark_cache_invalid()
{
this->offset_cache_dirty_ = true;
this->position_cache_dirty_ = true;
this->mapping_cache_dirty_ = true;
this->tangent_cache_dirty_ = true;
this->normal_cache_dirty_ = true;
this->length_cache_dirty_ = true;
}
int BezierSpline::evaluated_points_size() const
{
const int points_len = this->size();
BLI_assert(points_len > 0);
const int last_offset = this->control_point_offsets().last();
if (this->is_cyclic) {
return last_offset + (this->segment_is_vector(points_len - 1) ? 0 : this->resolution_);
}
return last_offset + 1;
}
/**
* If the spline is not cyclic, the direction for the first and last points is just the
* direction formed by the corresponding handles and control points. In the unlikely situation
* that the handles define a zero direction, fallback to using the direction defined by the
* first and last evaluated segments already calculated in #Spline::evaluated_tangents().
*/
void BezierSpline::correct_end_tangents() const
{
MutableSpan<float3> tangents(this->evaluated_tangents_cache_);
if (handle_positions_start_.first() != positions_.first()) {
tangents.first() = (positions_.first() - handle_positions_start_.first()).normalized();
}
if (handle_positions_end_.last() != positions_.last()) {
tangents.last() = (handle_positions_end_.last() - positions_.last()).normalized();
}
}
static void bezier_forward_difference_3d(const float3 &point_0,
const float3 &point_1,
const float3 &point_2,
const float3 &point_3,
MutableSpan<float3> result)
{
const float len = static_cast<float>(result.size());
const float len_squared = len * len;
const float len_cubed = len_squared * len;
BLI_assert(len > 0.0f);
const float3 rt1 = 3.0f * (point_1 - point_0) / len;
const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) / len_squared;
const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) / len_cubed;
float3 q0 = point_0;
float3 q1 = rt1 + rt2 + rt3;
float3 q2 = 2.0f * rt2 + 6.0f * rt3;
float3 q3 = 6.0f * rt3;
for (const int i : result.index_range()) {
result[i] = q0;
q0 += q1;
q1 += q2;
q2 += q3;
}
}
void BezierSpline::evaluate_bezier_segment(const int index,
const int next_index,
MutableSpan<float3> positions) const
{
if (this->segment_is_vector(index)) {
positions.first() = this->positions_[index];
}
else {
bezier_forward_difference_3d(this->positions_[index],
this->handle_positions_end_[index],
this->handle_positions_start_[next_index],
this->positions_[next_index],
positions);
}
}
/**
* Returns access to a cache of offsets into the evaluated point array for each control point.
* This is important because while most control point edges generate the number of edges specified
* by the resolution, vector segments only generate one edge.
*/
Span<int> BezierSpline::control_point_offsets() const
{
if (!this->offset_cache_dirty_) {
return this->offset_cache_;
}
std::lock_guard lock{this->offset_cache_mutex_};
if (!this->offset_cache_dirty_) {
return this->offset_cache_;
}
const int points_len = this->size();
this->offset_cache_.resize(points_len);
MutableSpan<int> offsets = this->offset_cache_;
int offset = 0;
for (const int i : IndexRange(points_len - 1)) {
offsets[i] = offset;
offset += this->segment_is_vector(i) ? 1 : this->resolution_;
}
offsets.last() = offset;
this->offset_cache_dirty_ = false;
return offsets;
}
/**
* Returns non-owning access to an array of values ontains the information necessary to
* interpolate values from the original control points to evaluated points. The control point
* index is the integer part of each value, and the factor used for interpolating to the next
* control point is the remaining factional part.
*/
Span<float> BezierSpline::evaluated_mappings() const
{
if (!this->mapping_cache_dirty_) {
return this->evaluated_mapping_cache_;
}
std::lock_guard lock{this->mapping_cache_mutex_};
if (!this->mapping_cache_dirty_) {
return this->evaluated_mapping_cache_;
}
const int size = this->size();
const int eval_size = this->evaluated_points_size();
this->evaluated_mapping_cache_.resize(eval_size);
MutableSpan<float> mappings = this->evaluated_mapping_cache_;
Span<int> offsets = this->control_point_offsets();
Span<float> lengths = this->evaluated_lengths();
/* Subtract one from the index into the lengths array to get the length
* at the start point rather than the length at the end of the edge. */
const float first_segment_len = lengths[offsets[1] - 1];
for (const int eval_index : IndexRange(0, offsets[1])) {
const float point_len = eval_index == 0 ? 0.0f : lengths[eval_index - 1];
const float length_factor = (first_segment_len == 0.0f) ? 0.0f : 1.0f / first_segment_len;
mappings[eval_index] = point_len * length_factor;
}
const int grain_size = std::max(512 / this->resolution_, 1);
blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
for (const int i : range) {
const float segment_start_len = lengths[offsets[i] - 1];
const float segment_end_len = lengths[offsets[i + 1] - 1];
const float segment_len = segment_end_len - segment_start_len;
const float length_factor = (segment_len == 0.0f) ? 0.0f : 1.0f / segment_len;
for (const int eval_index : IndexRange(offsets[i], offsets[i + 1] - offsets[i])) {
const float factor = (lengths[eval_index - 1] - segment_start_len) * length_factor;
mappings[eval_index] = i + factor;
}
}
});
if (this->is_cyclic) {
const float segment_start_len = lengths[offsets.last() - 1];
const float segment_end_len = this->length();
const float segment_len = segment_end_len - segment_start_len;
const float length_factor = (segment_len == 0.0f) ? 0.0f : 1.0f / segment_len;
for (const int eval_index : IndexRange(offsets.last(), eval_size - offsets.last())) {
const float factor = (lengths[eval_index - 1] - segment_start_len) * length_factor;
mappings[eval_index] = size - 1 + factor;
}
mappings.last() = 0.0f;
}
else {
mappings.last() = size - 1;
}
this->mapping_cache_dirty_ = false;
return this->evaluated_mapping_cache_;
}
Span<float3> BezierSpline::evaluated_positions() const
{
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
std::lock_guard lock{this->position_cache_mutex_};
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
const int eval_total = this->evaluated_points_size();
this->evaluated_position_cache_.resize(eval_total);
MutableSpan<float3> positions = this->evaluated_position_cache_;
Span<int> offsets = this->control_point_offsets();
BLI_assert(offsets.last() <= eval_total);
const int grain_size = std::max(512 / this->resolution_, 1);
blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) {
for (const int i : range) {
this->evaluate_bezier_segment(
i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
}
});
const int i_last = this->size() - 1;
if (this->is_cyclic) {
this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), this->resolution_));
}
else {
/* Since evaulating the bezier segment doesn't add the final point,
* it must be added manually in the non-cyclic case. */
positions.last() = this->positions_.last();
}
this->position_cache_dirty_ = false;
return this->evaluated_position_cache_;
}
BezierSpline::InterpolationData BezierSpline::interpolation_data_from_map(const float map) const
{
const int points_len = this->size();
const int index = std::floor(map);
if (index == points_len) {
BLI_assert(this->is_cyclic);
return InterpolationData{points_len - 1, 0, 1.0f};
}
if (index == points_len - 1) {
return InterpolationData{points_len - 2, points_len - 1, 1.0f};
}
return InterpolationData{index, index + 1, map - index};
}
template<typename T>
static void interpolate_to_evaluated_points_impl(Span<float> mappings,
const blender::VArray<T> &source_data,
MutableSpan<T> result_data)
{
const int points_len = source_data.size();
/* TODO: Use a set of functions mix2 in attribute_math instead of DefaultMixer. */
blender::attribute_math::DefaultMixer<T> mixer(result_data);
for (const int i : result_data.index_range()) {
const int index = std::floor(mappings[i]);
const int next_index = (index == points_len - 1) ? 0 : index + 1;
const float factor = mappings[i] - index;
const T &value = source_data[index];
const T &next_value = source_data[next_index];
mixer.mix_in(i, value, 1.0f - factor);
mixer.mix_in(i, next_value, factor);
}
mixer.finalize();
}
blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const
{
BLI_assert(source_data.size() == this->size());
Span<float> mappings = this->evaluated_mappings();
blender::fn::GVArrayPtr new_varray;
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
Array<T> values(this->evaluated_points_size());
interpolate_to_evaluated_points_impl<T>(mappings, source_data.typed<T>(), values);
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
std::move(values));
}
});
return new_varray;
}

View File

@@ -0,0 +1,205 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_array.hh"
#include "BLI_listbase.h"
#include "BLI_span.hh"
#include "DNA_curve_types.h"
#include "BKE_curve.h"
#include "BKE_spline.hh"
using blender::float3;
using blender::float4x4;
using blender::Span;
SplineGroup *SplineGroup::copy()
{
SplineGroup *new_curve = new SplineGroup();
for (SplinePtr &spline : this->splines) {
new_curve->splines.append(spline->copy());
}
return new_curve;
}
void SplineGroup::translate(const float3 translation)
{
for (SplinePtr &spline : this->splines) {
for (float3 &position : spline->positions()) {
position += translation;
}
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
for (float3 &handle_position : bezier_spline->handle_positions_start()) {
handle_position += translation;
}
for (float3 &handle_position : bezier_spline->handle_positions_end()) {
handle_position += translation;
}
}
spline->mark_cache_invalid();
}
}
void SplineGroup::transform(const float4x4 &matrix)
{
for (SplinePtr &spline : this->splines) {
for (float3 &position : spline->positions()) {
position = matrix * position;
}
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
for (float3 &handle_position : bezier_spline->handle_positions_start()) {
handle_position = matrix * handle_position;
}
for (float3 &handle_position : bezier_spline->handle_positions_end()) {
handle_position = matrix * handle_position;
}
}
spline->mark_cache_invalid();
}
}
void SplineGroup::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
{
for (const SplinePtr &spline : this->splines) {
spline->bounds_min_max(min, max, use_evaluated);
}
}
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
{
switch (dna_handle_type) {
case HD_FREE:
return BezierSpline::Free;
case HD_AUTO:
return BezierSpline::Auto;
case HD_VECT:
return BezierSpline::Vector;
case HD_ALIGN:
return BezierSpline::Align;
case HD_AUTO_ANIM:
return BezierSpline::Auto;
case HD_ALIGN_DOUBLESIDE:
return BezierSpline::Align;
}
BLI_assert_unreachable();
return BezierSpline::Auto;
}
static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
{
switch (twist_mode) {
case CU_TWIST_Z_UP:
return Spline::NormalCalculationMode::ZUp;
case CU_TWIST_MINIMUM:
return Spline::NormalCalculationMode::Minimum;
case CU_TWIST_TANGENT:
return Spline::NormalCalculationMode::Tangent;
}
BLI_assert_unreachable();
return Spline::NormalCalculationMode::Minimum;
}
static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
{
switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
case CU_NURB_ENDPOINT:
return NURBSpline::KnotsMode::EndPoint;
case CU_NURB_BEZIER:
return NURBSpline::KnotsMode::Bezier;
default:
return NURBSpline::KnotsMode::Normal;
}
BLI_assert_unreachable();
return NURBSpline::KnotsMode::Normal;
}
SplineGroup *dcurve_from_dna_curve(const Curve &dna_curve)
{
SplineGroup *curve = new SplineGroup();
const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
curve->splines.reserve(BLI_listbase_count(nurbs));
LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
switch (nurb->type) {
case CU_BEZIER: {
std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
spline->set_resolution(nurb->resolu);
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
/* TODO: Optimize by reserving the correct size. */
for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
spline->add_point(bezt.vec[1],
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
bezt.vec[0],
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
bezt.vec[2],
bezt.radius,
bezt.tilt);
}
curve->splines.append(std::move(spline));
break;
}
case CU_NURBS: {
std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
spline->set_resolution(nurb->resolu);
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
spline->set_order(nurb->orderu);
spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
}
curve->splines.append(std::move(spline));
break;
}
case CU_POLY: {
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt);
}
curve->splines.append(std::move(spline));
break;
}
default: {
BLI_assert_unreachable();
break;
}
}
}
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
* from multiple curve objects, where the value may be different. */
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
dna_curve.twist_mode);
for (SplinePtr &spline : curve->splines) {
spline->normal_mode = normal_mode;
}
return curve;
}
/** \} */

View File

@@ -0,0 +1,444 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_array.hh"
#include "BLI_span.hh"
#include "BLI_virtual_array.hh"
#include "BKE_attribute_math.hh"
#include "BKE_spline.hh"
using blender::Array;
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
SplinePtr NURBSpline::copy() const
{
SplinePtr new_spline = std::make_unique<NURBSpline>(*this);
return new_spline;
}
int NURBSpline::size() const
{
const int size = this->positions_.size();
BLI_assert(this->radii_.size() == size);
BLI_assert(this->tilts_.size() == size);
BLI_assert(this->weights_.size() == size);
return size;
}
int NURBSpline::resolution() const
{
return this->resolution_;
}
void NURBSpline::set_resolution(const int value)
{
this->resolution_ = value;
this->mark_cache_invalid();
}
uint8_t NURBSpline::order() const
{
return this->order_;
}
void NURBSpline::set_order(const uint8_t value)
{
BLI_assert(value >= 2 && value <= 6);
this->order_ = value;
this->mark_cache_invalid();
}
void NURBSpline::add_point(const float3 position,
const float radius,
const float tilt,
const float weight)
{
this->positions_.append(position);
this->radii_.append(radius);
this->tilts_.append(tilt);
this->weights_.append(weight);
this->knots_dirty_ = true;
}
void NURBSpline::drop_front(const int count)
{
BLI_assert(this->size() - count > 0);
this->positions_.remove(0, count);
this->radii_.remove(0, count);
this->tilts_.remove(0, count);
this->weights_.remove(0, count);
this->mark_cache_invalid();
}
void NURBSpline::drop_back(const int count)
{
const int new_size = this->size() - count;
BLI_assert(new_size > 0);
this->positions_.resize(new_size);
this->radii_.resize(new_size);
this->tilts_.resize(new_size);
this->weights_.resize(new_size);
this->mark_cache_invalid();
}
MutableSpan<float3> NURBSpline::positions()
{
return this->positions_;
}
Span<float3> NURBSpline::positions() const
{
return this->positions_;
}
MutableSpan<float> NURBSpline::radii()
{
return this->radii_;
}
Span<float> NURBSpline::radii() const
{
return this->radii_;
}
MutableSpan<float> NURBSpline::tilts()
{
return this->tilts_;
}
Span<float> NURBSpline::tilts() const
{
return this->tilts_;
}
MutableSpan<float> NURBSpline::weights()
{
return this->weights_;
}
Span<float> NURBSpline::weights() const
{
return this->weights_;
}
void NURBSpline::mark_cache_invalid()
{
this->basis_cache_dirty_ = true;
this->position_cache_dirty_ = true;
this->tangent_cache_dirty_ = true;
this->normal_cache_dirty_ = true;
this->length_cache_dirty_ = true;
}
int NURBSpline::evaluated_points_size() const
{
return this->resolution_ * this->segments_size();
}
void NURBSpline::correct_end_tangents() const
{
}
bool NURBSpline::check_valid_size_and_order() const
{
if (this->size() < this->order_) {
return false;
}
if (!this->is_cyclic && this->knots_mode == KnotsMode::Bezier) {
if (this->order_ == 4) {
if (this->size() < 5) {
return false;
}
}
else if (this->order_ != 3) {
return false;
}
}
return true;
}
int NURBSpline::knots_size() const
{
const int size = this->size() + this->order_;
return this->is_cyclic ? size + this->order_ - 1 : size;
}
void NURBSpline::calculate_knots() const
{
const KnotsMode mode = this->knots_mode;
const int length = this->size();
const int order = this->order_;
this->knots_.resize(this->knots_size());
MutableSpan<float> knots = this->knots_;
if (mode == NURBSpline::KnotsMode::Normal || this->is_cyclic) {
for (const int i : knots.index_range()) {
knots[i] = static_cast<float>(i);
}
}
else if (mode == NURBSpline::KnotsMode::EndPoint) {
float k = 0.0f;
for (const int i : IndexRange(1, knots.size())) {
knots[i - 1] = k;
if (i >= order && i <= length) {
k += 1.0f;
}
}
}
else if (mode == NURBSpline::KnotsMode::Bezier) {
BLI_assert(ELEM(order, 3, 4));
if (order == 3) {
float k = 0.6f;
for (const int i : knots.index_range()) {
if (i >= order && i <= length) {
k += 0.5f;
}
knots[i] = std::floor(k);
}
}
else {
float k = 0.34f;
for (const int i : knots.index_range()) {
knots[i] = std::floor(k);
k += 1.0f / 3.0f;
}
}
}
if (this->is_cyclic) {
const int b = length + order - 1;
if (order > 2) {
for (const int i : IndexRange(1, order - 2)) {
if (knots[b] != knots[b - i]) {
if (i == order - 1) {
knots[length + order - 2] += 1.0f;
break;
}
}
}
}
int c = order;
for (int i = b; i < this->knots_size(); i++) {
knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
c--;
}
}
}
Span<float> NURBSpline::knots() const
{
if (!this->knots_dirty_) {
BLI_assert(this->knots_.size() == this->size() + this->order_);
return this->knots_;
}
std::lock_guard lock{this->knots_mutex_};
if (!this->knots_dirty_) {
BLI_assert(this->knots_.size() == this->size() + this->order_);
return this->knots_;
}
this->calculate_knots();
this->knots_dirty_ = false;
return this->knots_;
}
static void calculate_basis_for_point(const float parameter,
const int points_len,
const int order,
Span<float> knots,
MutableSpan<float> basis_buffer,
NURBSpline::BasisCache &basis_cache)
{
/* Clamp parameter due to floating point inaccuracy. TODO: Look into using doubles. */
const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
int start = 0;
int end = 0;
for (const int i : IndexRange(points_len + order - 1)) {
const bool knots_equal = knots[i] == knots[i + 1];
if (knots_equal || t < knots[i] || t > knots[i + 1]) {
basis_buffer[i] = 0.0f;
continue;
}
basis_buffer[i] = 1.0f;
start = std::max(i - order - 1, 0);
end = i;
basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
break;
}
basis_buffer[points_len + order - 1] = 0.0f;
for (const int i_order : IndexRange(2, order - 1)) {
if (end + i_order >= points_len + order) {
end = points_len + order - 1 - i_order;
}
for (const int i : IndexRange(start, end - start + 1)) {
float new_basis = 0.0f;
if (basis_buffer[i] != 0.0f) {
new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
}
if (basis_buffer[i + 1] != 0.0f) {
new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
(knots[i + i_order] - knots[i + 1]);
}
basis_buffer[i] = new_basis;
}
}
/* Shrink the range of calculated values to avoid storing unecessary zeros. */
while (basis_buffer[start] == 0.0f && start < end) {
start++;
}
while (basis_buffer[end] == 0.0f && end > start) {
end--;
}
basis_cache.weights.clear();
basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
basis_cache.start_index = start;
}
void NURBSpline::calculate_basis_cache() const
{
if (!this->basis_cache_dirty_) {
return;
}
std::lock_guard lock{this->basis_cache_mutex_};
if (!this->basis_cache_dirty_) {
return;
}
const int points_len = this->size();
const int evaluated_len = this->evaluated_points_size();
this->basis_cache_.resize(evaluated_len);
const int order = this->order();
Span<float> control_weights = this->weights();
Span<float> knots = this->knots();
MutableSpan<BasisCache> basis_cache(this->basis_cache_);
/* This buffer is reused by each basis calculation to store temporary values.
* Theoretically it could be optimized away in the future. */
Array<float> basis_buffer(this->knots_size());
const float start = knots[order - 1];
const float end = this->is_cyclic ? knots[points_len + order - 1] : knots[points_len];
const float step = (end - start) / (evaluated_len - (this->is_cyclic ? 0 : 1));
float parameter = start;
for (const int i : IndexRange(evaluated_len)) {
BasisCache &basis = basis_cache[i];
calculate_basis_for_point(parameter,
points_len + (this->is_cyclic ? order - 1 : 0),
order,
knots,
basis_buffer,
basis);
BLI_assert(basis.weights.size() <= order);
for (const int j : basis.weights.index_range()) {
const int point_index = (basis.start_index + j) % points_len;
basis.weights[j] *= control_weights[point_index];
}
parameter += step;
}
this->basis_cache_dirty_ = false;
}
template<typename T>
void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
const blender::VArray<T> &source_data,
MutableSpan<T> result_data)
{
const int points_len = source_data.size();
BLI_assert(result_data.size() == weights.size());
blender::attribute_math::DefaultMixer<T> mixer(result_data);
for (const int i : result_data.index_range()) {
Span<float> point_weights = weights[i].weights;
const int start_index = weights[i].start_index;
for (const int j : point_weights.index_range()) {
const int point_index = (start_index + j) % points_len;
mixer.mix_in(i, source_data[point_index], point_weights[j]);
}
}
mixer.finalize();
}
blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const
{
BLI_assert(source_data.size() == this->size());
this->calculate_basis_cache();
Span<BasisCache> weights(this->basis_cache_);
blender::fn::GVArrayPtr new_varray;
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
Array<T> values(this->evaluated_points_size());
interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
std::move(values));
}
});
return new_varray;
}
Span<float3> NURBSpline::evaluated_positions() const
{
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
std::lock_guard lock{this->position_cache_mutex_};
if (!this->position_cache_dirty_) {
return this->evaluated_position_cache_;
}
const int total = this->evaluated_points_size();
this->evaluated_position_cache_.resize(total);
blender::fn::GVArray_For_Span<float3> positions_varray(this->positions_.as_span());
blender::fn::GVArrayPtr evaluated_positions_varray = this->interpolate_to_evaluated_points(
positions_varray);
/* TODO: Avoid copying. */
Span<float3> evaluated_positions =
evaluated_positions_varray->typed<float3>()->get_internal_span();
for (const int i : IndexRange(total)) {
this->evaluated_position_cache_[i] = evaluated_positions[i];
}
this->position_cache_dirty_ = false;
return this->evaluated_position_cache_;
}

View File

@@ -0,0 +1,144 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_span.hh"
#include "BLI_virtual_array.hh"
#include "BKE_spline.hh"
using blender::float3;
using blender::MutableSpan;
using blender::Span;
SplinePtr PolySpline::copy() const
{
SplinePtr new_spline = std::make_unique<PolySpline>(*this);
return new_spline;
}
int PolySpline::size() const
{
const int size = this->positions_.size();
BLI_assert(this->radii_.size() == size);
BLI_assert(this->tilts_.size() == size);
return size;
}
int PolySpline::resolution() const
{
return 1;
}
void PolySpline::set_resolution(const int UNUSED(value))
{
/* Poly curve has no resolution, there is just one evaluated point per control point. */
}
void PolySpline::add_point(const float3 position, const float radius, const float tilt)
{
this->positions_.append(position);
this->radii_.append(radius);
this->tilts_.append(tilt);
}
void PolySpline::drop_front(const int count)
{
BLI_assert(this->size() - count > 0);
this->positions_.remove(0, count);
this->radii_.remove(0, count);
this->tilts_.remove(0, count);
this->mark_cache_invalid();
}
void PolySpline::drop_back(const int count)
{
const int new_size = this->size() - count;
BLI_assert(new_size > 0);
this->positions_.resize(new_size);
this->radii_.resize(new_size);
this->tilts_.resize(new_size);
this->mark_cache_invalid();
}
MutableSpan<float3> PolySpline::positions()
{
return this->positions_;
}
Span<float3> PolySpline::positions() const
{
return this->positions_;
}
MutableSpan<float> PolySpline::radii()
{
return this->radii_;
}
Span<float> PolySpline::radii() const
{
return this->radii_;
}
MutableSpan<float> PolySpline::tilts()
{
return this->tilts_;
}
Span<float> PolySpline::tilts() const
{
return this->tilts_;
}
void PolySpline::mark_cache_invalid()
{
this->tangent_cache_dirty_ = true;
this->normal_cache_dirty_ = true;
this->length_cache_dirty_ = true;
}
int PolySpline::evaluated_points_size() const
{
return this->size();
}
void PolySpline::correct_end_tangents() const
{
}
Span<float3> PolySpline::evaluated_positions() const
{
return this->positions();
}
/**
* Poly spline interpolation from control points to evaluated points is a special case, since the
* result data is the same as the input data.
*/
blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
const blender::fn::GVArray &source_data) const
{
BLI_assert(source_data.size() == this->size());
/* TODO: Must make sure this ownership idea works. */
if (source_data.is_span()) {
return std::make_unique<blender::fn::GVArray_For_GSpan>(source_data.get_internal_span());
}
// if (source_data.is_single()) {
// BUFFER_FOR_CPP_TYPE_VALUE(source_data.type(), value);
// source_data.get_internal_single(value);
// return std::make_unique<blender::fn::GVArray_For_SingleValue>(
// source_data.type(), source_data.size(), value);
// }
return {};
}

View File

@@ -39,6 +39,7 @@
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -1003,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
#endif
}
static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
Volume *volume_input)
static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
GeometrySet &geometry_set)
{
Volume *volume = volume_input;
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -1030,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
if (mti->modifyVolume) {
/* Ensure we are not modifying the input. */
if (volume == volume_input) {
volume = BKE_volume_copy_for_eval(volume, true);
}
Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
if (volume_next && volume_next != volume) {
/* If the modifier returned a new volume, release the old one. */
if (volume != volume_input) {
BKE_id_free(nullptr, volume);
}
volume = volume_next;
}
if (mti->modifyGeometrySet) {
mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
@@ -1072,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
}
}
static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
{
if (!geometry_set.has<VolumeComponent>()) {
return nullptr;
}
VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
Volume *volume = volume_component.release();
if (volume != nullptr) {
/* Add back, but only as read-only non-owning component. */
volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
}
else {
/* The component was empty, we can remove it. */
geometry_set.remove<VolumeComponent>();
}
return volume;
}
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@@ -1079,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Volume *volume = (Volume *)object->data;
Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
GeometrySet geometry_set;
geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
/* If the geometry set did not contain a volume, we still create an empty one. */
if (volume_eval == nullptr) {
volume_eval = BKE_volume_new_for_eval(volume);
}
/* Assign evaluated object. */
const bool is_owned = (volume != volume_eval);
BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
const bool eval_is_owned = (volume != volume_eval);
BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)

View File

@@ -98,3 +98,10 @@
#else
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
#endif
/* Alignment directive */
#ifdef _WIN64
# define ALIGN_STRUCT __declspec(align(64))
#else
# define ALIGN_STRUCT
#endif

View File

@@ -245,6 +245,13 @@ struct float3 {
return result;
}
static float3 cross(const float3 &a, const float3 &b)
{
float3 result;
cross_v3_v3v3(result, a, b);
return result;
}
static float3 project(const float3 &a, const float3 &b)
{
float3 result;

View File

@@ -45,6 +45,37 @@ struct float4x4 {
return mat;
}
static float4x4 from_normalized_axis_data(const float3 location,
const float3 forward,
const float3 up)
{
BLI_ASSERT_UNIT_V3(forward);
BLI_ASSERT_UNIT_V3(up);
float4x4 matrix;
const float3 cross = float3::cross(forward, up);
matrix.values[0][0] = forward.x;
matrix.values[1][0] = cross.x;
matrix.values[2][0] = up.x;
matrix.values[3][0] = location.x;
matrix.values[0][1] = forward.y;
matrix.values[1][1] = cross.y;
matrix.values[2][1] = up.y;
matrix.values[3][1] = location.y;
matrix.values[0][2] = forward.z;
matrix.values[1][2] = cross.z;
matrix.values[2][2] = up.z;
matrix.values[3][2] = location.z;
matrix.values[0][3] = 0.0f;
matrix.values[1][3] = 0.0f;
matrix.values[2][3] = 0.0f;
matrix.values[3][3] = 1.0f;
return matrix;
}
static float4x4 identity()
{
float4x4 mat;
@@ -116,6 +147,19 @@ struct float4x4 {
return scale;
}
void apply_scale(const float scale)
{
values[0][0] *= scale;
values[0][1] *= scale;
values[0][2] *= scale;
values[1][0] *= scale;
values[1][1] *= scale;
values[1][2] *= scale;
values[2][0] *= scale;
values[2][1] *= scale;
values[2][2] *= scale;
}
float4x4 inverted() const
{
float4x4 result;

View File

@@ -247,11 +247,11 @@ class Map {
{
this->add_new_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename ForwardValue>
void add_new_as(ForwardKey &&key, ForwardValue &&value)
template<typename ForwardKey, typename... ForwardValue>
void add_new_as(ForwardKey &&key, ForwardValue &&... value)
{
this->add_new__impl(
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -277,11 +277,11 @@ class Map {
{
return this->add_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename ForwardValue>
bool add_as(ForwardKey &&key, ForwardValue &&value)
template<typename ForwardKey, typename... ForwardValue>
bool add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add__impl(
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -307,11 +307,11 @@ class Map {
{
return this->add_overwrite_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename ForwardValue>
bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value)
template<typename ForwardKey, typename... ForwardValue>
bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add_overwrite__impl(
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -413,12 +413,12 @@ class Map {
{
return this->pop_default_as(key, std::move(default_value));
}
template<typename ForwardKey, typename ForwardValue>
Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
template<typename ForwardKey, typename... ForwardValue>
Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value)
{
Slot *slot = this->lookup_slot_ptr(key, hash_(key));
if (slot == nullptr) {
return std::forward<ForwardValue>(default_value);
return Value(std::forward<ForwardValue>(default_value)...);
}
Value value = std::move(*slot->value());
slot->remove();
@@ -525,15 +525,15 @@ class Map {
{
return this->lookup_default_as(key, default_value);
}
template<typename ForwardKey, typename ForwardValue>
Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
template<typename ForwardKey, typename... ForwardValue>
Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const
{
const Value *ptr = this->lookup_ptr_as(key);
if (ptr != nullptr) {
return *ptr;
}
else {
return std::forward<ForwardValue>(default_value);
return Value(std::forward<ForwardValue>(default_value)...);
}
}
@@ -557,11 +557,11 @@ class Map {
{
return this->lookup_or_add_as(std::move(key), std::move(value));
}
template<typename ForwardKey, typename ForwardValue>
Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value)
template<typename ForwardKey, typename... ForwardValue>
Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->lookup_or_add__impl(
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -982,7 +982,7 @@ class Map {
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash);
slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value()));
return;
}
}
@@ -996,8 +996,8 @@ class Map {
new (this) Map(NoExceptConstructor(), allocator);
}
template<typename ForwardKey, typename ForwardValue>
void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
template<typename ForwardKey, typename... ForwardValue>
void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->contains_as(key));
@@ -1005,7 +1005,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return;
}
@@ -1013,14 +1013,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
template<typename ForwardKey, typename ForwardValue>
bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
template<typename ForwardKey, typename... ForwardValue>
bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return true;
}
@@ -1075,7 +1075,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), create_value(), hash);
slot.occupy(std::forward<ForwardKey>(key), hash, create_value());
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1086,14 +1086,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
template<typename ForwardKey, typename ForwardValue>
Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
template<typename ForwardKey, typename... ForwardValue>
Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1104,15 +1104,15 @@ class Map {
MAP_SLOT_PROBING_END();
}
template<typename ForwardKey, typename ForwardValue>
bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
template<typename ForwardKey, typename... ForwardValue>
bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
auto create_func = [&](Value *ptr) {
new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value));
new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...);
return true;
};
auto modify_func = [&](Value *ptr) {
*ptr = std::forward<ForwardValue>(value);
*ptr = Value(std::forward<ForwardValue>(value)...);
return false;
};
return this->add_or_modify__impl(
@@ -1221,16 +1221,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper {
map_.reserve(n);
}
template<typename ForwardKey, typename ForwardValue>
void add_new(ForwardKey &&key, ForwardValue &&value)
template<typename ForwardKey, typename... ForwardValue>
void add_new(ForwardKey &&key, ForwardValue &&... value)
{
map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)});
}
template<typename ForwardKey, typename ForwardValue>
bool add(ForwardKey &&key, ForwardValue &&value)
template<typename ForwardKey, typename... ForwardValue>
bool add(ForwardKey &&key, ForwardValue &&... value)
{
return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
return map_
.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)})
.second;
}
bool contains(const Key &key) const

View File

@@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot {
* Change the state of this slot from empty/removed to occupied. The key/value has to be
* constructed by calling the constructor with the given key/value as parameter.
*/
template<typename ForwardKey, typename ForwardValue>
void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
template<typename ForwardKey, typename... ForwardValue>
void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
new (&value_buffer_) Value(std::forward<ForwardValue>(value));
new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
state_ = Occupied;
}
@@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
return is_equal(key, key_);
}
template<typename ForwardKey, typename ForwardValue>
void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
template<typename ForwardKey, typename... ForwardValue>
void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
new (&value_buffer_) Value(std::forward<ForwardValue>(value));
new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
}

View File

@@ -94,7 +94,7 @@ template<typename T> class Span {
using iterator = const T *;
using size_type = int64_t;
private:
protected:
const T *data_ = nullptr;
int64_t size_ = 0;
@@ -477,7 +477,7 @@ template<typename T> class MutableSpan {
using iterator = T *;
using size_type = int64_t;
private:
protected:
T *data_;
int64_t size_;
@@ -661,6 +661,16 @@ template<typename T> class MutableSpan {
return IndexRange(size_);
}
/**
* Return a reference to the first element in the array. This invokes undefined behavior when the
* array is empty.
*/
constexpr T &first() const
{
BLI_assert(size_ > 0);
return data_[0];
}
/**
* Returns a reference to the last element. This invokes undefined behavior when the array is
* empty.

View File

@@ -232,13 +232,14 @@ class Stack {
{
this->push_as(std::move(value));
}
template<typename ForwardT> void push_as(ForwardT &&value)
/* This is similar to `std::stack::emplace`. */
template<typename... ForwardT> void push_as(ForwardT &&... value)
{
if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
try {
new (top_) T(std::forward<ForwardT>(value));
new (top_) T(std::forward<ForwardT>(value)...);
top_++;
size_++;
}

View File

@@ -68,6 +68,11 @@ char *BLI_str_replaceN(const char *__restrict str,
void BLI_str_replace_char(char *string, char src, char dst) ATTR_NONNULL();
bool BLI_str_replace_table_exact(char *string,
const size_t string_len,
const char *replace_table[][2],
int replace_table_len);
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
ATTR_NONNULL(1, 3) ATTR_PRINTF_FORMAT(3, 4);
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)

View File

@@ -437,13 +437,17 @@ class Vector {
*/
void append(const T &value)
{
this->ensure_space_for_one();
this->append_unchecked(value);
this->append_as(value);
}
void append(T &&value)
{
this->append_as(std::move(value));
}
/* This is similar to `std::vector::emplace_back`. */
template<typename... ForwardValue> void append_as(ForwardValue &&...value)
{
this->ensure_space_for_one();
this->append_unchecked(std::move(value));
this->append_unchecked_as(std::forward<ForwardValue>(value)...);
}
/**
@@ -474,10 +478,18 @@ class Vector {
* behavior when not enough capacity has been reserved beforehand. Only use this in performance
* critical code.
*/
template<typename ForwardT> void append_unchecked(ForwardT &&value)
void append_unchecked(const T &value)
{
this->append_unchecked_as(value);
}
void append_unchecked(T &&value)
{
this->append_unchecked_as(std::move(value));
}
template<typename... ForwardT> void append_unchecked_as(ForwardT &&...value)
{
BLI_assert(end_ < capacity_end_);
new (end_) T(std::forward<ForwardT>(value));
new (end_) T(std::forward<ForwardT>(value)...);
end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -656,6 +668,21 @@ class Vector {
return *(end_ - 1);
}
/**
* Return a reference to the first element in the vector.
* This invokes undefined behavior when the vector is empty.
*/
const T &first() const
{
BLI_assert(this->size() > 0);
return *begin_;
}
T &first()
{
BLI_assert(this->size() > 0);
return *begin_;
}
/**
* Return how many values are currently stored in the vector.
*/

View File

@@ -37,6 +37,7 @@
* see of the increased compile time and binary size is worth it.
*/
#include "BLI_array.hh"
#include "BLI_span.hh"
namespace blender {
@@ -71,6 +72,11 @@ template<typename T> class VArray {
return size_ == 0;
}
IndexRange index_range() const
{
return IndexRange(size_);
}
/* Returns true when the virtual array is stored as a span internally. */
bool is_span() const
{
@@ -82,13 +88,13 @@ template<typename T> class VArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
Span<T> get_span() const
Span<T> get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return {};
}
return this->get_span_impl();
return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -102,20 +108,35 @@ template<typename T> class VArray {
/* Returns the value that is returned for every index. This invokes undefined behavior if the
* virtual array would not return the same value for every index. */
T get_single() const
T get_internal_single() const
{
BLI_assert(this->is_single());
if (size_ == 1) {
return this->get(0);
}
return this->get_single_impl();
return this->get_internal_single_impl();
}
/* Get the element at a specific index. Note that this operator cannot be used to assign values
* to an index, because the return value is not a reference. */
T operator[](const int64_t index) const
{
return this->get(index);
}
/* Copy the entire virtual array into a span. */
void materialize(MutableSpan<T> r_span) const
{
BLI_assert(size_ == r_span.size());
this->materialize_impl(r_span);
}
void materialize_to_uninitialized(MutableSpan<T> r_span) const
{
BLI_assert(size_ == r_span.size());
this->materialize_to_uninitialized_impl(r_span);
}
protected:
virtual T get_impl(const int64_t index) const = 0;
@@ -124,7 +145,7 @@ template<typename T> class VArray {
return false;
}
virtual Span<T> get_span_impl() const
virtual Span<T> get_internal_span_impl() const
{
BLI_assert_unreachable();
return {};
@@ -135,56 +156,201 @@ template<typename T> class VArray {
return false;
}
virtual T get_single_impl() const
virtual T get_internal_single_impl() const
{
/* Provide a default implementation, so that subclasses don't have to provide it. This method
* should never be called because `is_single_impl` returns false by default. */
BLI_assert_unreachable();
return T();
}
virtual void materialize_impl(MutableSpan<T> r_span) const
{
if (this->is_span()) {
const Span<T> span = this->get_internal_span();
initialized_copy_n(span.data(), size_, r_span.data());
}
else if (this->is_single()) {
const T single = this->get_internal_single();
initialized_fill_n(r_span.data(), size_, single);
}
else {
const int64_t size = size_;
for (int64_t i = 0; i < size; i++) {
r_span[i] = this->get(i);
}
}
}
virtual void materialize_to_uninitialized_impl(MutableSpan<T> r_span) const
{
if (this->is_span()) {
const Span<T> span = this->get_internal_span();
uninitialized_copy_n(span.data(), size_, r_span.data());
}
else if (this->is_single()) {
const T single = this->get_internal_single();
uninitialized_fill_n(r_span.data(), size_, single);
}
else {
const int64_t size = size_;
T *dst = r_span.data();
for (int64_t i = 0; i < size; i++) {
new (dst + i) T(this->get(i));
}
}
}
};
/* Similar to VArray, but the elements are mutable. */
template<typename T> class VMutableArray : public VArray<T> {
public:
VMutableArray(const int64_t size) : VArray<T>(size)
{
}
void set(const int64_t index, T value)
{
BLI_assert(index >= 0);
BLI_assert(index < this->size_);
this->set_impl(index, std::move(value));
}
/* Copy the values from the source span to all elements in the virtual array. */
void set_all(Span<T> src)
{
BLI_assert(src.size() == this->size_);
this->set_all_impl(src);
}
MutableSpan<T> get_internal_span()
{
BLI_assert(this->is_span());
Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span();
return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
}
protected:
virtual void set_impl(const int64_t index, T value) = 0;
virtual void set_all_impl(Span<T> src)
{
if (this->is_span()) {
const MutableSpan<T> span = this->get_internal_span();
initialized_copy_n(src.data(), this->size_, span.data());
}
else {
const int64_t size = this->size_;
for (int64_t i = 0; i < size; i++) {
this->set(i, src[i]);
}
}
}
};
template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
/**
* A virtual array implementation for a span. This class is final so that it can be devirtualized
* by the compiler in some cases (e.g. when #devirtualize_varray is used).
* A virtual array implementation for a span. Methods in this class are final so that it can be
* devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used).
*/
template<typename T> class VArrayForSpan final : public VArray<T> {
private:
const T *data_;
template<typename T> class VArray_For_Span : public VArray<T> {
protected:
const T *data_ = nullptr;
public:
VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
{
}
protected:
T get_impl(const int64_t index) const override
VArray_For_Span(const int64_t size) : VArray<T>(size)
{
}
T get_impl(const int64_t index) const final
{
return data_[index];
}
bool is_span_impl() const final
{
return true;
}
Span<T> get_internal_span_impl() const final
{
return Span<T>(data_, this->size_);
}
};
template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> {
protected:
T *data_ = nullptr;
public:
VMutableArray_For_MutableSpan(const MutableSpan<T> data)
: VMutableArray<T>(data.size()), data_(data.data())
{
}
protected:
VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size)
{
}
T get_impl(const int64_t index) const final
{
return data_[index];
}
void set_impl(const int64_t index, T value) final
{
data_[index] = value;
}
bool is_span_impl() const override
{
return true;
}
Span<T> get_span_impl() const override
Span<T> get_internal_span_impl() const override
{
return Span<T>(data_, this->size_);
}
};
/**
* A variant of `VArray_For_Span` that owns the underlying data.
* The `Container` type has to implement a `size()` and `data()` method.
* The `data()` method has to return a pointer to the first element in the continuous array of
* elements.
*/
template<typename Container, typename T = typename Container::value_type>
class VArray_For_ArrayContainer : public VArray_For_Span<T> {
private:
Container container_;
public:
VArray_For_ArrayContainer(Container container)
: VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container))
{
this->data_ = container_.data();
}
};
/**
* A virtual array implementation that returns the same value for every index. This class is final
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
* used).
*/
template<typename T> class VArrayForSingle final : public VArray<T> {
template<typename T> class VArray_For_Single final : public VArray<T> {
private:
T value_;
public:
VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
{
}
@@ -199,7 +365,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return this->size_ == 1;
}
Span<T> get_span_impl() const override
Span<T> get_internal_span_impl() const override
{
return Span<T>(&value_, 1);
}
@@ -209,12 +375,170 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return true;
}
T get_single_impl() const override
T get_internal_single_impl() const override
{
return value_;
}
};
/**
* In many cases a virtual array is a span internally. In those cases, access to individual could
* be much more efficient than calling a virtual method. When the underlying virtual array is not a
* span, this class allocates a new array and copies the values over.
*
* This should be used in those cases:
* - All elements in the virtual array are accessed multiple times.
* - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
* from faster access.
* - An API is called, that does not accept virtual arrays, but only spans.
*/
template<typename T> class VArray_Span final : public Span<T> {
private:
const VArray<T> &varray_;
Array<T> owned_data_;
public:
VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray)
{
this->size_ = varray_.size();
if (varray_.is_span()) {
this->data_ = varray_.get_internal_span().data();
}
else {
owned_data_.~Array();
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
varray_.materialize_to_uninitialized(owned_data_);
this->data_ = owned_data_.data();
}
}
};
/**
* Same as VArray_Span, but for a mutable span.
* The important thing to note is that when changing this span, the results might not be
* immediately reflected in the underlying virtual array (only when the virtual array is a span
* internally). The #save method can be used to write all changes to the underlying virtual array,
* if necessary.
*/
template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
private:
VMutableArray<T> &varray_;
Array<T> owned_data_;
bool save_has_been_called_ = false;
bool show_not_saved_warning_ = true;
public:
/* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
* not, a new array has to be allocated as a wrapper for the underlying virtual array. */
VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true)
: MutableSpan<T>(), varray_(varray)
{
this->size_ = varray_.size();
if (varray_.is_span()) {
this->data_ = varray_.get_internal_span().data();
}
else {
if (copy_values_to_span) {
owned_data_.~Array();
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
varray_.materialize_to_uninitialized(owned_data_);
}
else {
owned_data_.reinitialize(varray_.size());
}
this->data_ = owned_data_.data();
}
}
~VMutableArray_Span()
{
if (show_not_saved_warning_) {
if (!save_has_been_called_) {
std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
}
}
}
/* Write back all values from a temporary allocated array to the underlying virtual array. */
void save()
{
save_has_been_called_ = true;
if (this->data_ != owned_data_.data()) {
return;
}
varray_.set_all(owned_data_);
}
void disable_not_applied_warning()
{
show_not_saved_warning_ = false;
}
};
/**
* This class makes it easy to create a virtual array for an existing function or lambda. The
* `GetFunc` should take a single `index` argument and return the value at that index.
*/
template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> {
private:
GetFunc get_func_;
public:
VArray_For_Func(const int64_t size, GetFunc get_func)
: VArray<T>(size), get_func_(std::move(get_func))
{
}
private:
T get_impl(const int64_t index) const override
{
return get_func_(index);
}
};
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
class VArray_For_DerivedSpan : public VArray<ElemT> {
private:
const StructT *data_;
public:
VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data())
{
}
private:
ElemT get_impl(const int64_t index) const override
{
return GetFunc(data_[index]);
}
};
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
void (*SetFunc)(StructT &, ElemT)>
class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> {
private:
StructT *data_;
public:
VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
: VMutableArray<ElemT>(data.size()), data_(data.data())
{
}
private:
ElemT get_impl(const int64_t index) const override
{
return GetFunc(data_[index]);
}
void set_impl(const int64_t index, ElemT value) override
{
SetFunc(data_[index], std::move(value));
}
};
/**
* Generate multiple versions of the given function optimized for different virtual arrays.
* One has to be careful with nesting multiple devirtualizations, because that results in an
@@ -229,14 +553,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool
/* Support disabling the devirtualization to simplify benchmarking. */
if (enable) {
if (varray.is_single()) {
/* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
/* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */
const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()};
func(varray_single);
return;
}
if (varray.is_span()) {
/* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
const VArrayForSpan<T> varray_span{varray.get_span()};
/* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */
const VArray_For_Span<T> varray_span{varray.get_internal_span()};
func(varray_span);
return;
}
@@ -262,26 +586,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
const bool is_single1 = varray1.is_single();
const bool is_single2 = varray2.is_single();
if (is_span1 && is_span2) {
const VArrayForSpan<T1> varray1_span{varray1.get_span()};
const VArrayForSpan<T2> varray2_span{varray2.get_span()};
const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_span, varray2_span);
return;
}
if (is_span1 && is_single2) {
const VArrayForSpan<T1> varray1_span{varray1.get_span()};
const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_span, varray2_single);
return;
}
if (is_single1 && is_span2) {
const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
const VArrayForSpan<T2> varray2_span{varray2.get_span()};
const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_single, varray2_span);
return;
}
if (is_single1 && is_single2) {
const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_single, varray2_single);
return;
}

View File

@@ -2691,8 +2691,12 @@ static IMesh raycast_patches_boolean(const IMesh &tm,
* each edge in the output triangles either matches the original edge
* for the (identical) edge of f, or else is -1. So diagonals added
* for triangulation can later be identified by having #NO_INDEX for original.
*
* This code uses Blenlib's BLI_polyfill_calc to do triangulation, and is therefore quite fast.
* Unfortunately, it can product degenerate triangles that mesh_intersect will remove, leaving
* the mesh non-PWN.
*/
static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
{
/* Similar to loop body in BM_mesh_calc_tesselation. */
int flen = f->size();
@@ -2746,6 +2750,113 @@ static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
return ans;
}
/**
* Which CDT output edge index is for an edge between output verts
* v1 and v2 (in either order)?
* \return -1 if none.
*/
static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2)
{
for (int e : cdt_out.edge.index_range()) {
const std::pair<int, int> &edge = cdt_out.edge[e];
if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) {
return e;
}
}
return -1;
}
/**
* Tessellate face f into triangles and return an array of `const Face *`
* giving that triangulation.
* Care is taken so that the original edge index associated with
* each edge in the output triangles either matches the original edge
* for the (identical) edge of f, or else is -1. So diagonals added
* for triangulation can later be identified by having #NO_INDEX for original.
*
* The method used is to use the CDT triangulation. Usually that triangulation
* will only use the existing vertices. However, if the face self-intersects
* then the CDT triangulation will include the intersection points.
* If this happens, we use the polyfill triangulator instead. We don't
* use the polyfill triangulator by default because it can create degenerate
* triangles (which we can handle but they'll create non-manifold meshes).
*/
static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
{
int flen = f->size();
CDT_input<mpq_class> cdt_in;
cdt_in.vert = Array<mpq2>(flen);
cdt_in.face = Array<Vector<int>>(1);
cdt_in.face[0].reserve(flen);
for (int i : f->index_range()) {
cdt_in.face[0].append(i);
}
/* Project poly along dominant axis of normal to get 2d coords. */
if (!f->plane_populated()) {
f->populate_plane(false);
}
const double3 &poly_normal = f->plane->norm;
int axis = double3::dominant_axis(poly_normal);
/* If project down y axis as opposed to x or z, the orientation
* of the polygon will be reversed.
* Yet another reversal happens if the poly normal in the dominant
* direction is opposite that of the positive dominant axis. */
bool rev1 = (axis == 1);
bool rev2 = poly_normal[axis] < 0;
bool rev = rev1 ^ rev2;
for (int i = 0; i < flen; ++i) {
int ii = rev ? flen - i - 1 : i;
mpq2 &p2d = cdt_in.vert[ii];
int k = 0;
for (int j = 0; j < 3; ++j) {
if (j != axis) {
p2d[k++] = (*f)[ii]->co_exact[j];
}
}
}
CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE);
int n_tris = cdt_out.face.size();
Array<Face *> ans(n_tris);
for (int t = 0; t < n_tris; ++t) {
int i_v_out[3];
const Vert *v[3];
int eo[3];
bool needs_steiner = false;
for (int i = 0; i < 3; ++i) {
i_v_out[i] = cdt_out.face[t][i];
if (cdt_out.vert_orig[i_v_out[i]].size() == 0) {
needs_steiner = true;
break;
}
v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]];
}
if (needs_steiner) {
/* Fall back on the polyfill triangulator. */
return polyfill_triangulate_poly(f, arena);
}
for (int i = 0; i < 3; ++i) {
int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]);
BLI_assert(e_out != -1);
eo[i] = NO_INDEX;
for (int orig : cdt_out.edge_orig[e_out]) {
if (orig != NO_INDEX) {
eo[i] = orig;
break;
}
}
}
if (rev) {
ans[t] = arena->add_face(
{v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false});
}
else {
ans[t] = arena->add_face(
{v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
}
}
return ans;
}
/**
* Return an #IMesh that is a triangulation of a mesh with general
* polygonal faces, #IMesh.

View File

@@ -539,6 +539,29 @@ void BLI_str_replace_char(char *str, char src, char dst)
}
}
/**
* Simple exact-match string replacement.
*
* \param replace_table: Array of source, destination pairs.
*
* \note Larger tables should use a hash table.
*/
bool BLI_str_replace_table_exact(char *string,
const size_t string_len,
const char *replace_table[][2],
int replace_table_len)
{
for (int i = 0; i < replace_table_len; i++) {
if (STREQ(string, replace_table[i][0])) {
BLI_strncpy(string, replace_table[i][1], string_len);
return true;
}
}
return false;
}
/** \} */
/**
* Compare two strings without regard to case.
*

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