1
1

Compare commits

...

268 Commits

Author SHA1 Message Date
f0731bd5ac Merge branch 'master' into temp-ui-tweaks 2021-10-04 16:21:26 +02:00
4a3464050c Assets: Support dragging assets on "Unassigned" catalog
Dragging assets onto the "Unassigned" catalog tree item will effectively
move the assets out of any catalog. Technically this means unsetting the
Catalog-ID stored in the asset metadata, or more precisely setting the
UUID to be all zeros.
2021-10-04 16:17:09 +02:00
b536605e78 Cleanup: Use static function for asset catalog tree-view helper 2021-10-04 15:29:39 +02:00
4882208633 Cleanup: Separate interface & implementation for asset catalog tree-view
Should make the code a bit more organized and help getting an overview
of the interfaces more quickly.
2021-10-04 15:29:39 +02:00
cc636db8f2 Fix T91823: Regression not showing idblocks when recursion is set to Blend file
Introduced by fc7beac8d6. During code review it wasn't clear why this
branch was needed, so we removed it. Now it is clear why it is needed
so we added it back and added a comment why the branch is needed.

Patch provided by @Severin.
2021-10-04 15:25:40 +02:00
f806bd8261 Fix T91861: Black environment behind shadow catcher
Always sample background pass behind shadow catcher (if the pass
exists, of course), regardless of whether shadow catcher will be
used as approximate or accurate.

Allows to combine accurate shadows into an environment map.

Differential Revision: https://developer.blender.org/D12747
2021-10-04 15:07:32 +02:00
fc4886a314 Fix T91894: Cycles baking normal maps of transformed objects not working 2021-10-04 13:58:37 +02:00
326bd76d3b Fix T89759: baking normals does not take into account mirror modifier 2021-10-04 13:58:37 +02:00
a80a2f07b7 Fix T90815: wrong Cycles OSL normal map render after recent optimization 2021-10-04 13:58:37 +02:00
76238af213 Fix Cycles render time pass being available in UI, but it was removed
This previously only work for CPU rendering, and isn't that practical to get
working in the new architecture.
2021-10-04 13:58:37 +02:00
8ca7250982 Fix T91911: error in image dithering code after recent changes
Thanks to Patrik Olsson for spotting this.
2021-10-04 13:58:37 +02:00
2c5661682b Merge branch 'master' into temp-ui-tweaks
# Conflicts:
#	source/blender/editors/animation/time_scrub_ui.c
2021-10-04 12:59:53 +02:00
4e50c5f4fb Merge branch 'master' into temp-ui-tweaks
# Conflicts:
#	source/blender/editors/animation/time_scrub_ui.c
2021-10-04 12:58:42 +02:00
dc4c2815a6 Cleanup: Reverting submodules hash
This partially reverts commit e62ce9e08e.
2021-10-04 12:42:07 +02:00
Simon Lenz
e62ce9e08e Fix camera border bug in passepartout render view
{F10761402}

With active viewport render from camera view, the camera border shows up, even when passepartout and overlays are disabled.

By moving the line-drawing code to the passepartout section, it is effectively disabled when passepartout is off.

Reviewed By: sebastian_k

Differential Revision: https://developer.blender.org/D12745
2021-10-04 12:21:40 +02:00
23d9953c80 I18n tools: Fix issue when extracting messages on release builds.
This was also affecting prototype of buildbot-driven UI messages
extraction...
2021-10-04 12:17:50 +02:00
ce6a24976a Cleanup: Redundant space at the end of comment 2021-10-04 12:02:14 +02:00
f2b86471ea Fix session uuid ghash comparison return value
Because of legacy reasons (C string compare function returning 0 when
strings are equal), the ghash compare function is expected to return
false when hashes are equal.
2021-10-04 11:55:09 +02:00
37003cbbc1 Fix T91867: Error reading tiles with Persistent Data ON 2021-10-04 11:54:11 +02:00
87a3cb3bff Cleanup: Neighbour -> Neighbor. and other minor UI messages fixes.
Blender English should use 'American' variants, not 'British' variants.
2021-10-04 11:14:57 +02:00
8c55333a8e Cleanup: tag unused parameters as such. 2021-10-04 09:43:40 +02:00
fc6228bd85 Fix T91873: Crash when opening properties panel
This patch fixes a crash that was recently introduced by rB5cebcb415e76.
The reason were missing poll functions in the UI and operator.

Reviewed By: ISS

Maniphest Tasks: T91873

Differential Revision: https://developer.blender.org/D12736
2021-10-04 08:15:03 +02:00
93e92ac126 Cleanup: remove unused assignments 2021-10-04 13:15:15 +11:00
357acd1d50 Cleanup: pass arguments as const 2021-10-04 13:15:15 +11:00
e43fcc014a Cleanup: remove redundant assignment 2021-10-04 13:15:15 +11:00
3c3669894f Cleanup: use system includes 2021-10-04 13:14:58 +11:00
6e48a51af7 check_cppcheck: use quiet output
Without this, each cppcheck invocation included all defines/includes
flooding the console with unhelpful information.

Also remove nonexistent directory to exclude.
2021-10-04 13:12:37 +11:00
606271966e check_cppcheck: use '--cppcheck-build-dir'
Use a temporary directory for faster performance.
2021-10-04 13:12:36 +11:00
e0e7a5522f project_source_info: queue_processes() now waits for jobs to finish
queue_processes() - used for some of the "make check_*" utilities,
wasn't waiting for all jobs to finish before returning.

This conflicted with running cleanup operations.
2021-10-04 13:12:35 +11:00
e7274dedc4 Fix possible NULL pointer deference
The pointer was referenced before being checked.
2021-10-04 13:12:34 +11:00
cc8fa3ee90 Fix T91904: Assert when loading empty CurveProfile
Somehow, the file from T71329 has an empty curve profile. While that may
be a problem in itself, it's reasonable to avoid asserts or crashes when
loading or drawing such a CurveProfile. This commit just makes sure the
table always has a single vertex, and adds some checks in drawing code.
2021-10-03 20:28:31 -05:00
dfdc9c6219 Cleanup: Make more functions static
This simplifies the surface of the API for a CurveProfile.
2021-10-03 19:52:59 -05:00
b6195f6664 Cleanup: Replace macro with function 2021-10-03 18:54:52 -05:00
272a38e0c2 Cleanup: Make function static 2021-10-03 18:31:56 -05:00
c9af025936 Cleanup: Add doxygen sections, rearrange functions 2021-10-03 18:23:58 -05:00
1e5cfebf66 Fix key-map with fall-back tool on RMB select
Regression in bffda4185d.
2021-10-04 09:28:33 +11:00
57272d598d Cleanup: rename eRegionType -> eRegion_Type
Match eSpace_Type.
2021-10-04 09:28:33 +11:00
ee79bde54d Keymap: print more verbose output for --debug-handlers
Include the short-cut text and the operator properties to make it easier
to track down the key-map item source that matched the event.
2021-10-04 09:28:33 +11:00
c4dca65228 Asset Browser: Support dragging assets into catalogs
With this it is possible to select any number of assets in the Asset
Browser and drag them into catalogs. The assets will be moved to that
catalog then. However, this will only work in the "Current File" asset
library, since that is the only library that allows changing assets,
which is what's done here.

While dragging assets over the tree row, a tooltip is shown explaining
what's going to happen.

In preparation to this, the new UI tree-view API was already extended
with custom drop support, see 4ee2d9df42.

----

Changes here to the `wmDrag` code were needed to support dragging multiple
assets. Some of it is considered temporary because a) a proper #AssetHandle
design should replace some ugly parts of this patch and b) the multi-item
support in `wmDrag` isn't that great yet. The entire API will have to be
written anyway (see D4071).

Maniphest Tasks: T91573

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

Reviewed by: Sybren Stüvel
2021-10-03 23:58:20 +02:00
3b1a243039 Cleanup: Rename file-list function to better match what it's doing 2021-10-03 23:18:04 +02:00
2305f270c5 Geometry Nodes: Handle Type Selection Node Update
This node creates a boolean field selection of bezier spline points
that have a handle of the given type on the selected 'side' of the
contol point. This is evaluated on the point domain.

Differential Revision: https://developer.blender.org/D12559
2021-10-03 15:01:30 -05:00
adc084a3e9 Cleanup: Move curveprofile.c to C++ 2021-10-03 14:40:08 -05:00
ae86584404 Geometry Nodes: Points to Volume Fields fixes
A few items when OpenVDB is not enabled.
  - Cleanup a compiler warning
  - Add a node warning message
  - Return an empty geometry set
2021-10-03 13:43:51 -05:00
98fe05fb5b Fix T91810 Bevel not working well with certain unbeveled edge positions.
A previous commit, c56526d8b6, which sometimes didn't drop offsets
into 'in plane' faces, as a fix to T71329, was overly aggressive.
If all the intermediate edges are in the same plane then it is fine
to just put the meeting point on the plane of the start and end edges.
2021-10-03 11:30:40 -04:00
1833ebea31 Cleanup: move as_span method out of header
This method does not have to be in a header and results in a relatively
large number of symbols to be generated (42).
2021-10-03 17:07:26 +02:00
bf354cde96 Cleanup: move special methods of geometry set out of header
This reduces the compile time, because fewer symbols have to be generated
in translation units using geometry sets.
2021-10-03 16:58:33 +02:00
64d07ffcc3 Cleanup: move methods out of field classes
This makes it easier to scan through the classes and simplifies
testing the compile time impact of having these methods in the header.
2021-10-03 16:47:54 +02:00
17e2d54a3d Cleanup: use extern templates for typed output attribute
This is very similar to rBa812fe8ceb75fd2b.
This time the number of symbols decreases further from 1335 to 928.
Compile time of the distribute node decreases from ~2.4s to ~2.3s.
2021-10-03 16:30:14 +02:00
a812fe8ceb Nodes: use extern templates for socket declarations
The new socket declaration api generates a surprising amount
of symbols in each translation unit where it is used. This resulted
in a measurable compile time increase.

This commit reduces the number of symbols that are generated in
each translation unit significantly. For example, in
`node_geo_distribute_points_on_faces.cc` the number of symbols
decreased from 1930 to 1335. In my tests, this results in a 5-20%
compile time speedup when this and similar files are compiled
in isolation (measured by executing the command in `compile_commands.json`).

Compiling the distribute points on faces node sped up from ~2.65s to ~2.4s.
2021-10-03 16:05:19 +02:00
2f52f5683c Cleanup: move more method definitions out of their class
This makes it easier to test the impact of moving them out
of the header later.
2021-10-03 15:24:21 +02:00
06c3bac23b Cleanup: move more methods out of classes 2021-10-03 15:14:03 +02:00
fb34cdc7da Geometry Nodes: Points to Volume Fields Update
This update of the Points to Volume node allows a field to populate the
radius input of the node and removes the implicit realization of
instances.

Differential Revision: https://developer.blender.org/D12531
2021-10-03 08:03:46 -05:00
0998856c92 Cleanup: use movable output attribute instead of optional
This simplifies the code a bit and improves compile times a bit.
2021-10-03 15:01:02 +02:00
8fc97a871f Cleanup: make typed output attribute movable
This comes at a small performance cost due to an additional
memory allocation, but that is not significant currently.
2021-10-03 14:49:15 +02:00
5d5a753d96 Cleanup: move AttributeIDRef and OutputAttribute methods out of class
This makes the classes easier to read and simplifies testing
the compile time impact of defining these methods in the header.
2021-10-03 14:39:17 +02:00
e1e75bd62c Cleanup: move resource scope method definitions out of class 2021-10-03 14:23:26 +02:00
a8d6a86981 Cleanup: move StringRef method definitions out of class
This makes the classes more appealing to look at and makes
it easier to see what different methods are available.
2021-10-03 14:10:26 +02:00
c206fa9627 Cleanup: add nolint comment to move semantic test 2021-10-03 14:03:53 +02:00
f2da98d816 Cleanup: remove unused functions 2021-10-03 13:44:44 +02:00
d3afe0c126 Geometry Nodes: Resample Curve Fields Update
This update of the Resample Curve node allows a field to populate the
count or length input of the node depending on the current mode. The
field is evaluated on the spline domain.

Differential Revision: https://developer.blender.org/D12735
2021-10-02 21:45:51 -05:00
e863e05697 Data Transfer: Remove unnecessary noisy error message
I've seen requests to remove this or complaints about this error message
quite frequently. In lots of production files it's just always going off.
It's not an actionable warning, and since "slow" is relative, it isn't
always even correct.

Differential Revision: https://developer.blender.org/D12694
2021-10-02 20:33:15 -05:00
b57b4dfab1 Cleanup: clang-format 2021-10-03 12:13:29 +11:00
f49dff97d4 Cleanup: spelling in strings 2021-10-03 12:13:29 +11:00
74f45ed9c5 Cleanup: spelling in comments 2021-10-03 12:13:29 +11:00
Jarrett Johnson
c5c94e3eae Geometry Nodes: Add Rotate Euler Node
This commit introduces the Rotate Euler function node which modifies
an input euler rotation. The node replaces the "Point Rotate" node.

Addresses T91375.

Differential Revision: https://developer.blender.org/D12531
2021-10-02 20:04:45 -05:00
34cf33eb12 Geometry Nodes: Switch Node Fields Update
This update of the Switch node allows for field compatible types
to be switched through the node. This includes the following:

Float, Int, Bool, String, Vector, and Color

The remaining types are processed with the orginal code:

Geometry, Object, Collection, Texture, and Material

Because the old types require a diffent "switch" socket than the
field types, versioning for old files is included to move links
of those types to a new switch socket. Once fields of other types
are supported, this node can be updated to support them as well.

Differential Revision: https://developer.blender.org/D12642
2021-10-02 17:34:47 -05:00
Leon Leno
54927caf4f Geometry Nodes: Add side and fill segments to Cone/Cylinder nodes
This commit extends the 'Cone' and 'Cylinder' mesh primitive nodes,
with two inputs to control the segments along the side and in the fill.
This makes the nodes more flexible and brings them more in line with
the improved cube node.

Differential Revision: https://developer.blender.org/D12463
2021-10-02 17:29:25 -05:00
12e8c78353 Fix T91888: Pivot point settings shown in timeline
Added to timeline by accident in f9e0981976.
2021-10-01 23:26:59 +02:00
1476d35870 Geometry Nodes: Set Handle Type Node Field Update
This update of the Set Handle Type node allows for a bool field to be
used as the selection of the affected control point handles for
bezier splines. If no bezier splines are provided a info message is
shown.

Differential Revision: https://developer.blender.org/D12526
2021-10-01 14:22:24 -05:00
1fb364491b Geometry Nodes: Set Spline Type Node Field Update
This update of the Set Spline Type node allows for a bool field to be
used as the selection of the affected splines.

Differential Revision: https://developer.blender.org/D12522
2021-10-01 11:59:29 -05:00
eacdc0ab4a VSE: Draw active strips with a different color in the preview window 2021-10-01 18:03:18 +02:00
dc30a9087c Geometry Nodes: Spline Length Input Node
The Spline Length Input node provides a field containing the
length of the current evaluated spline to the Point and Spline
domains.

Differential Revision: https://developer.blender.org/D12706
2021-10-01 09:58:49 -05:00
9e456ca695 Asset Browser: expose current catalog ID in RNA
Add read-only access to the active catalog ID via
`context.space_data.params.catalog_id` in the Asset Browser context.

The UUID is exposed as string to Python.
2021-10-01 16:47:16 +02:00
aae96176e8 Geometry Nodes: Curve Subdivide Node with Fields
The curve subdivide node can now take an int field to specify the
number of subdivisions to make at each curve segment.

Reviewed by: Hans Goudey
Differential Revision: https://developer.blender.org/D12534
2021-10-01 09:43:04 -05:00
e1952c541a Cleanup: unused function declaration
This should have been removed during the recent velocity attribute
refactor.
2021-10-01 16:41:50 +02:00
Johan Walles
fb820496f5 Tracking: Sort motion tracking tracks by start and end frames
Enable sorting motion tracking tracks by start / end times.

Help identifying what cases reconstructed camera jumps, based
on information about whether any track starts/ends at the frame.

Based on revision eb0eb54d96.

{F10563305}

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D12621
2021-10-01 16:29:38 +02:00
f497e471f8 Asset Catalogs: always have an Asset Catalog Tree available
Always create an `AssetCatalogTree` in the `AssetCatalogService`. This
ensures that newly-created catalogs are immediately visible in the UI
(because they insert themselves into an already-existing tree).
2021-10-01 16:15:50 +02:00
3be4cb5b27 Cleanup: Asset Catalog Test, fix clang-tidy warnings
No functional changes.
2021-10-01 15:24:04 +02:00
56ce51d1f7 Asset Catalogs: add catalog filter for the asset browser
Given an "active catalog" (i.e. the one selected in the UI), construct
an `AssetCatalogFilter` instance. This filter can determine whether an
asset should be shown or not. It returns `true` when The asset's catalog
ID is:

- the active catalog,
- an alias of the active catalog (so different UUID that maps to the
  same path),
- a sub-catalog of the active catalog.

Not yet hooked up to the UI.
2021-10-01 15:22:05 +02:00
1c7ce7e0b4 Cleanup: asset catalogs, make function const
Declare `AssetCatalogService::find_catalog()` as `const`, as it's not
requiring modification off the service object.

No functional changes.
2021-10-01 15:22:05 +02:00
271210126e Fix T91834: Appending objects with shape keys into new file is broken.
Recent append refactor 'broke' this, we need special recursive care and
handling of those nasty shpae keys... again.
2021-10-01 14:49:04 +02:00
f9acf21063 Python API Docs: add an example of Bone.convert_local_to_pose usage.
The use case for this method is quite obscure and difficult to
understand without an example. Despite how big looks, this is
actually the simplest example that makes sense.
2021-10-01 15:47:30 +03:00
bdc66c9569 GPU: set 'GL_PACK_ALIGNMENT' 1 as default
This fixes T91828.

The current value of `GL_PACK_ALIGNMENT` may result in crash in the `gpu` module if the buffer is not aligned.

Differential Revision: https://developer.blender.org/D12720
2021-10-01 09:11:37 -03:00
eb3a8fb4e8 Fix T91872: incorrect socket inspection on group nodes
This bug was introduced in rBef45399f3be0955ba8.
2021-10-01 13:21:03 +02:00
798e593002 Fix T87189: Copy/pasting IDs does not handle properly instantiation.
Copy/Paste uses its own code path for ID linking, which was not setting
`LIB_TAG_DOIT` for proper instantiation later on.

Would be nice the make this logic closer to the rest of the link/append
code at some point, but for now this fix will do.
2021-10-01 12:21:08 +02:00
21c29480c3 Fix paste code linking 'direct' IDs with 'INDIRECT' flag.
No idea why this was done that way (it originate from initial paste
commit, rB12b642062c6f).

But the IDs 'selected' as direct paste in `BLO_library_link_copypaste`
should be 'directly' linked, it's similar case to actual append of
selected IDs by the user.

Related to T87189.
2021-10-01 12:21:08 +02:00
928d644895 Append: Fix appended objects potentially auto-instantiated in more than one collection.
Related to T87189.
2021-10-01 12:21:08 +02:00
af0b7925db Fix T87536: incorrect socket types in reroute nodes
This refactors and fixes the code that propagates socket types
through reroute nodes. In my tests it is faster than the previous
code. The difference becomes larger the more reroute nodes
there are, because the old code had O(n^2) runtime, while the
new code runs in linear time.

Differential Revision: https://developer.blender.org/D12716
2021-10-01 11:42:00 +02:00
7843cd63d8 Fix T91839: incorrect active vertex group index
Differential Revision: https://developer.blender.org/D12712
2021-10-01 11:36:10 +02:00
ae4b45145c Cleanup: Asset Catalog Paths, move default constructor to header file
No functional changes.
2021-10-01 10:58:33 +02:00
2e6c6426d3 Asset Catalogs: test that missing catalogs are created once
Add asset catalogs test, to ensure missing catalogs are only created once,
and not for every originally defined catalog.

No functional changes to Blender (the code was already doing the right
thing).
2021-10-01 10:58:16 +02:00
bdb7d262aa Cleanup: clang-tidy warnings 2021-10-01 16:20:31 +10:00
b559fb178e Gizmo: hide 2D gizmos while transforming
Hide gizmos in the sequencer & UV editor while transforming.
2021-10-01 16:19:12 +10:00
4485dc483c Cleanup: use C-style comments, nullptr for C++
Minor changes extracted from D6408
2021-10-01 10:45:09 +10:00
Vitor Boschi
3a59ddb292 Fix: Incorrect warning in curve to mesh node with instances
The node was setting a warning when used with instances on input,
even though it worked fine.

Differential Revision: https://developer.blender.org/D12718
2021-09-30 18:27:58 -05:00
66fe1c79f3 Compositor: Fix Composite node using alpha when "Use Alpha" is off
Alpha input was not receiving the final composite canvas 
as preferred causing a Translate operation being inserted 
for centering. This resulted in a transparent background.
The issue only affects Full Frame mode.
2021-09-30 23:56:53 +02:00
e2df5c8a56 Compositor: Fix Flip node not flipping translation on Full Frame
To match tiled implementation, flip center should not be translated
when canvas has offset. Instead the canvas offset needs to be flipped.
2021-09-30 23:56:53 +02:00
f3274bfa70 Compositor: Fix Dilate/Erode node crash with Step option
It was writing the buffer out of bounds.
Only "Full Frame" mode was affected.
2021-09-30 23:56:53 +02:00
4569d9c0c3 Compositor: Fix Movie Distortion node rendering an empty image
Input area of interest calculation was incorrect because `m_margin`
was uninitialized.
Only "Full Frame" mode was affected.
2021-09-30 23:10:27 +02:00
Pratik Borhade
33dc584b37 Fix T91285: Bad tooltip for VSE Slip operator
This patch is created to change the tooltip for Slip Strip Contents
As per the present info, only active strip will be affected.
But in reality selected strips can be trimmed with this operator.

Word Trim changed to Slip in tooltip

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D12450
2021-09-30 21:11:09 +02:00
Josef Raschen
213554f24a VSE: Add ASC CDL color correction method
Add Offset/Slope/Power controls to the color balance modifier. This is
already available in compositor.

Reviewed By: sergey, ISS

Differential Revision: https://developer.blender.org/D12575
2021-09-30 21:09:47 +02:00
8d60ac2bb0 Cleanup: Fix unused variable warning 2021-09-30 14:01:56 -05:00
Brecht Van Lommel
1a134c4c30 Cycles: refactor API for render output
* Add OutputDriver, replacing function callbacks in Session.
* Add PathTraceTile, replacing tile access methods in Session.
* Add more detailed comments about how this driver should be implemented.
* Add OIIOOutputDriver for Cycles standalone to output an image.

Differential Revision: https://developer.blender.org/D12627
2021-09-30 20:53:27 +02:00
a754e35198 Cycles: refactor API for GPU display
* Split GPUDisplay into two classes. PathTraceDisplay to implement the Cycles side,
  and DisplayDriver to implement the host application side. The DisplayDriver is now
  a fully abstract base class, embedded in the PathTraceDisplay.
* Move copy_pixels_to_texture implementation out of the host side into the Cycles side,
  since it can be implemented in terms of the texture buffer mapping.
* Move definition of DeviceGraphicsInteropDestination into display driver header, so
  that we do not need to expose private device headers in the public API.
* Add more detailed comments about how the DisplayDriver should be implemented.

The "driver" terminology might not be obvious, but is also used in other renderers.

Differential Revision: https://developer.blender.org/D12626
2021-09-30 20:48:08 +02:00
ac582056e2 Geometry Nodes: Swap order of geometry proximity inputs
"Target" is the most important, so it goes at the top.
2021-09-30 13:41:55 -05:00
Charlie Jolly
be70827e6f Nodes: Add Float Curve for GN and Shader nodes.
Replacement for float curve in legacy Attribute Curve Map node.

Float Curve defaults to [0.0-1.0] range.

Reviewed By: JacquesLucke, brecht

Differential Revision: https://developer.blender.org/D12683
2021-09-30 19:24:40 +01:00
827e30bd15 Geometry Nodes: Change default for mesh to points node
While "Vertices" may be less useful since mesh vertices are already
points, the output is more easily understandable, so it's a better
default.
2021-09-30 11:53:48 -05:00
6cff1d6480 Fix T91734: Crash snapping mesh if a beveled curve is present
`BKE_mesh_boundbox_get` cannot be called for objects of type Curve.

The BoundBox however does not match the object seen in the scene.
This will be dealt with in another commit.
2021-09-30 02:48:02 -03:00
dd3391dd99 Asset Catalogs: create missing parent catalogs
For every known catalog, ensure its parent catalog also exists. This
ensures that assets can be assigned to parent catalogs, even when they
didn't exist in the Catalog Definition File yet.
2021-09-30 17:34:58 +02:00
4389067929 Fix possible use-after-free in drag-drop handling logic
Would happen when there were multiple drag items in parallel. There was
a listbase constructed with twice the same item, even though that item
would be deleted after it was handled the first time.
2021-09-30 16:39:09 +02:00
4ee2d9df42 UI: Support easy dropping into/onto rows in new tree-view API
Adds an easy way to add drop support for tree-view rows.

Most of the work is handled by the tree-view UI code. The tree items can
simply override a few functions (`can_drop()`, `on_drop()`,
`drop_tooltip()`) to implement their custom drop behavior.

While dragging over a tree-view item that can be dropped into/onto, the
item can show a custom and dynamic tooltip explaining what's gonna
happen on drop.

This isn't used yet, but will soon be for asset catalogs.

See documentation here:
https://wiki.blender.org/wiki/Source/Interface/Views#Further_Customizations
2021-09-30 16:39:09 +02:00
42ce88f15c Cleanup: remove CatalogPath alias
The `CatalogPath` name was an alias for `std::string`, so that it could
be easily switched over to something else. This happened in the previous
commit (switched to `AssetCatalogPath`), so the alias is no longer
necessary.

This commit removes the `CatalogPath` alias.

No functional changes.
2021-09-30 16:34:30 +02:00
628fab696c Asset Catalog: introduce AssetCatalogPath class
So far we have used `std::string` for asset catalog paths. Some
operations are better described on a dedicated class for this, though.
This commits switches catalog paths from using `std::string` to a
dedicated `blender::bke::AssetCatalogPath` class.

The `using CatalogPath = AssetCatalogPath` alias is still there, and
will be removed in a following cleanup commit.

New `AssetCatalogPath` code reviewed by @severin in D12710.
2021-09-30 16:29:14 +02:00
5d42ea0369 GPencil: Change default template for better contrast in header
Patch created by Pablo Vazquez

This change darkens the header area a bit to create more contrast with the texts.

Differential Revision: https://developer.blender.org/D12711
2021-09-30 16:14:44 +02:00
d754d85845 Fix RigidBodyWorld copy using NO_MAIN instead of COW flag for cache handling.
We only want to share caches in case of CoW copying for the depsgraph,
not for regular `NO_MAIN` data.
2021-09-30 16:00:29 +02:00
1a72744ddc Fix T90246: Full Copy'ing a scene confuses physics in the original scene.
Handling of RigidBody data in duplicate of scenes/collections was very
wrong. This commit:
 - Add handling of duplication of RB collections when fully duplicating
   a scene.
 - Fix Object duplication trying to add duplicated RB objects to
   matching RBW collections.

While the later behavior is desired when only duplicated objects, when
duplicating their collections and/or scenes it is actually very bad, as
it would add back new object duplicates to old (RBW) collections.
2021-09-30 16:00:29 +02:00
779ea49af7 Cleanup: move node_common.c to c++
Buildbot compiled without problems.
2021-09-30 15:44:08 +02:00
07c5d02a11 Asset Browser: Support activating catalogs in the "Current File" library
If the "Current File" asset library is selected in the Asset Browser,
now asssets are filtered based on the active asset catalog. Previously
it would just show all assets. This was marked as a TODO in the code
already.

Maniphest Task: https://developer.blender.org/T91820
2021-09-30 15:23:13 +02:00
20c27a5151 Merge branch 'master' into temp-ui-tweaks 2021-09-27 16:11:27 +02:00
150b879969 Merge branch 'master' into temp-ui-tweaks 2021-09-22 16:40:14 +02:00
76c49d5884 GPencil: Fix error in previous commit
By error I commited the previous version.
2021-09-22 15:54:11 +02:00
71072a904f GPencil: Change Rotation tooltip
The tooltip was not clear about in what shading modes  works.

Related to T91467
2021-09-22 15:54:11 +02:00
cb0ab7bccc Cleanup: spelling (correct c5c8c68eec)
"iff" was intended as "if and only if". while exact use of abbreviations
isn't clear cut, I assumed this was a typo & it's not used anywhere
else in source/, expand to "only if" (suggested by Sybren).
2021-09-22 15:54:11 +02:00
7ae6f9dede Build: change make update to not print errors regarding submodule branches
Instead of trying to checkout non-existent branches and getting confusing fatal
error prints, check if the branch exists first.

Ref D12560
2021-09-22 15:54:11 +02:00
52fb64024b Geometry Nodes: support Noise Texture node
This makes the Noise Texture node available in geometry nodes.
It should behave the same as in shader node, with the exception
that it does not have an implicit position input yet. That will
be added separately.

Differential Revision: https://developer.blender.org/D12467
2021-09-22 15:54:11 +02:00
c2ab184162 Geometry Nodes: support Set Position node on instances
Previously, the node would always realize instances implicitly.
Now it can change the position of entire instances.
The Realize Instances node can be used before if the old
behavior is required.

Differential Revision: https://developer.blender.org/D12555
2021-09-22 15:54:11 +02:00
3107db97e2 make_update: Fix case where a sub-module would not have required branch.
Issue revealed by rB546314fc9669 change, also error itself exited before
that commit.

Now we do accept git command to fail when trying to checkout the
specified branch from sub-modules, and only actually error in case the
fall-back branch (aka master) cannot be properly checked out.

Thanks fot Ray molenkamp (@LazyDodo) for report and initial patch
(D12560).
2021-09-22 15:54:11 +02:00
3ea201bfa6 Geometry Nodes: new Realize Instances node
This node has a simple geometry input and output.
If the input geometry contains instances, they will be realized
into actual geometry. When there are many instances, this can
be very slow and memory intensive. Generally, instances should
only be made real when necessary, e.g. when every instance
should be deformed independently.

Differential Revision: https://developer.blender.org/D12556
2021-09-22 15:54:11 +02:00
f1f3552b9b UUID: add nil value for UUIDs
Add `BLI_uuid_nil()` that returns the nil UUID (used to indicate "not
set") and `BLI_uuid_is_nil(uuid)` to do an equality test with the nil
value.
2021-09-22 15:54:11 +02:00
03a10c31d6 UUID: fix seeding the RNG clock on macOS
On Apple machines, call `clock_gettime()` instead of `timespec_get()`.

macOS only introduced `timespec_get()` in version 10.15 (introduced
approx two years ago, so in 2019), even though the function is from C11.
2021-09-22 15:54:11 +02:00
3486a23b1e Cleanup: UUID, prevent "missing braces" warning on macOS
Add braces around initialization of sub-objects, as per the warning
suggestion on macOS.

No functional changes.
2021-09-22 15:54:11 +02:00
03fa0ef033 UUID: include 'seconds' field of current time in RNG seed
XOR the 'seconds' and 'nanoseconds' fields of the current time to seed the
RNG used for generating random UUIDs. This ensures a better seed just in
case the clock as no sub-second resolution.
2021-09-22 15:54:11 +02:00
9f576b7db8 Fix T91511: GPencil weight_get and Vertex Groups not working at expected
The API was checking the number of total weights with the first point of the stroke and this was not valid because each point can have different number of weight elemnts,
2021-09-22 15:54:11 +02:00
451d734f80 UI: rename "Save Screenshot (Area => Editor)"
The term "area" isn't normally exposed in the UI.
2021-09-22 15:54:11 +02:00
3dc53b1a29 Cleanup: spelling 2021-09-22 15:54:11 +02:00
db70f583a2 Fix: Incorrect default values for the curve trim node
The default end factor should be 1. The proper value for the default end
length is somewhat arbitrary, but it shouldn't be zero.
2021-09-22 15:54:10 +02:00
5d4ca28f8c Fix: Spline length calculation fails with no evaluated points
The case that checked whether there were evaluated edges was incorrect,
since two points are needed for an edge. Then also avoid running the
accumulation for an empty span.
2021-09-22 15:54:10 +02:00
de5b91205a Fix build error after previous commit
Incorrect renaming and use of enum after search and replace.
2021-09-22 15:54:10 +02:00
bd67caa872 Cleanup: Fix/improve variable names and comments 2021-09-22 15:54:10 +02:00
a7e75b855e Compositor: Add OIDN prefiltering option to Denoise node
It's equivalent to the OpenImageDenoise prefiltering option in Cycles.
See D12043.

Prefilter modes:
- None: No prefiltering, use when guiding passes are noise-free.
- Fast: Denoise image and guiding passes together. Improves quality when
guiding passes are noisy using least amount of extra processing time.
- Accurate: Prefilter noisy guiding passes before denoising image.
Improves quality when guiding passes are noisy using extra
processing time.

Reviewed By: #compositing, jbakker, sergey

Differential Revision: https://developer.blender.org/D12342
2021-09-22 15:54:10 +02:00
f60420116c Compositor: Fix crash exporting buffers on debug
ImBuf allocates 4 channels, use copying to support buffers
with 1 and 3 channels.
2021-09-22 15:54:10 +02:00
0d206598f7 Compositor: Fix Alpha Over node ignoring emissive colors
It was an issue on Full Frame mode only.
2021-09-22 15:54:10 +02:00
b44b39d581 Cleanup: Rename curve node enums
The enum called "interpolate" was really a choice of methods for mapping
inputs to positions on the curve, whereas the "sample" enum was used to
define a way to create a whole set of new points from the curve, without
any input parameters. The "re-sample" vs. "sample" naming makes that
distinction better.
2021-09-22 15:54:10 +02:00
638f972453 Revert "GPencil: Curvature support for length modifier."
Reason for revert: accidental push of a intermediate change locally.

This reverts commit 25e548c96b.
2021-09-22 15:54:10 +02:00
24902401ba GPencil: Curvature support for length modifier. 2021-09-22 15:54:10 +02:00
fc406830f7 Audaspace: added audio file streams functionality.
On the blender side this commit fixes importing video files with audio
and video streams that do not share the same start time and duration.

Differential Revision: https://developer.blender.org/D12353
2021-09-22 15:54:10 +02:00
c9259808a1 Py API Docs: Fix audio docs example
After new AUD API changes from 2.8x what "buffer" function used to do
has now become "cache" function (it caches a sound into RAM). Therefore,
the basic aud example should call this new "cache" function instead of
"buffer" function.

Thanks to Michael-Z-Freeman for pointing out.
2021-09-22 15:54:10 +02:00
c3045118b3 Cleanup: add missing includes 2021-09-22 15:54:10 +02:00
68e229feea Cleanup: typo 2021-09-22 15:54:10 +02:00
ea6838ec6d Fix T91461: Pose Library name filter not working
since `AssetHandle` does not have a `name_property`
(`RNA_def_struct_name_property`), and the UIList is just using the
default `uilist_filter_items_default` it simply cannot filter on names
(`RNA_struct_name_get_alloc` wont succeed).

Adding a name_property also wont work since `AssetHandle` inherits
`PropertyGroup` (which already sets name_property).

So this adds a (temporary) hack exception for RNA_AssetHandle in
uilist_filter_items_default until the design of `AssetHandle` progresses
further.

thx @Severin for additional feedback

Maniphest Tasks: T91461

Differential Revision: https://developer.blender.org/D12541
2021-09-22 15:54:10 +02:00
d612181827 UI: Always Create Asset Previews
This patch allows Asset Browser previews to be made regardless
of the setting of the (unrelated) "File Preview Type" Preference.

See D12484 for more details.

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

Reviewed by Julian Eisel
2021-09-22 15:54:10 +02:00
7b1a1bc8eb Geometry Nodes: hide Attribute Remove node when fields are enabled 2021-09-22 15:54:10 +02:00
6092fa3702 Fix (harmless) mistake in recent new Append code.
This code path is not yet used so no harm, but that was a fairly nasty
potential crash-generator.
2021-09-22 15:54:10 +02:00
a17e895d7d Cleanup: clang-tidy warnings in UUID code
Use C++ version of C headers, and avoid static function call on instance.

No functional changes.
2021-09-22 15:54:10 +02:00
5e5a98e51a Cleanup: fix memory leak 2021-09-22 15:54:10 +02:00
Henrik Dick
8129dd3189 Fix T91481: Grease Pencil Layer Double Transformations
Use the inverse of the grease pencil object. This patch fixes the issue for bones and objects.

Maniphest Tasks: T91481

Differential Revision: https://developer.blender.org/D12539
2021-09-22 15:54:10 +02:00
e831f41d0b Blenlib: introduce a UUID type
Add `BLI_uuid` and `DNA_uuid_types.h` with a UUID implementation
following RFC4122 (https://datatracker.ietf.org/doc/html/rfc4122.html).

The following features are implemented:
- A struct of 128 bits that can be used in DNA definitions.
- Generation of version 4 UUIDs, that is, purely random ones.
- UUID equality function.
- String to UUID and UUID to string conversion functions that are
  compatible with RFC4122.
- C++ stream operator that outputs the UUID as string.

This UUID will be used by the asset system, to uniquely identify asset
catalogs.

Reviewed By: Severin, jacqueslucke

Differential Revision: https://developer.blender.org/D12475
2021-09-22 15:54:10 +02:00
fe2efb6866 Fix T91448: GPencil Fill simplify not working in render
The simplify was hardcode to be disabled in render.
2021-09-22 15:54:10 +02:00
2f06f35513 Fix T85564: FCurve modifier zero influence on restrict range borders
When using FModifier `Restrict Frame Range`, the resulting influence was
zero being exactly on `Start` / `End` range borders (so borders were
**exclusive**).
This made it impossible to chain FModifers together (forcing the user to
specify values slightly below the desired border in following
FModifiers).
This is now corrected to be **inclusive** on Start / End range borders.

Before
{F10234864}
After
{F10234865}
Testfile
{F10234866}

In the case of touching open borders (so [frame A frame B] followed by
[frame B frame C]) both modifiers are evaluated (in stack order).
If the later modifier has full influence (and is not additive) this simply
means the result is the same as the later modifier's value.
If influences below 1 are used (or modifiers are additive) both modifier's
values are interpolated/added accordingly.

technical notes:
- this was caused by the introduction of FModifier Influence/BlendIn-Out
in rB185663b52b61.
- for comparison, see other occurrences of
`FMODIFIER_FLAG_RANGERESTRICT`.
- the following conditions in `eval_fmodifier_influence` for blend in/
out have been changed accordingly.

Maniphest Tasks: T85564

Differential Revision: https://developer.blender.org/D10401
2021-09-22 15:54:10 +02:00
f91718bdd9 WM: expose the "any" state of KeyMapItem modifiers
Change KeyMapItem.alt/ctrl/shift/oskey to integer types,
where -1 is used to ignore the modifier when matching key-map items.

It was only possible to set all modifiers to -1 at once from RNA
using the 'any' property.
Afterwards individual modifiers could be set back to true/false.
Although these key-map items could not be exported/imported.

Exposing the values directly avoids the need for cumbersome workarounds.
2021-09-22 15:54:10 +02:00
c0fb68a93d Cleanup: remove KM_MOD_SECOND & KM_SHIFT2, KM_CTRL2.. etc
These were added in a1c8543f2a (2007)
but never used.
Nor did they have any meaning in practice.

Note that versioning keymap items isn't needed as these values were
never set. The code-paths that set these values also set KM_MOD_FIRST
causing `keymap_event_set` to only ever assign values of 0 or 1.

These flags complicate further exposing KM_ANY (-1)
which is also a valid value for modifiers.
2021-09-22 15:54:10 +02:00
13a0a0d592 UI: expose "Lasso Select" & "Extrude to Cursor" in menus
- Show "Lasso Select" in menus (along with Box & Circle select)
- Show "Extrude to Cursor" (along with other extrude actions).
- Rename operators that add/extrude on Ctrl-Click
  since their names were inconsistent.

This is mainly for discoverability.
2021-09-22 15:54:10 +02:00
ff3345a554 UI: enable the depend-on-cursor flag for some operators
- Bend (Transform).
- Extrude to Cursor.
- Lasso Select (related operators such as node-cut links, mask.. etc).
- Rip Mesh / UV's.
- Vertex/Edge Slide.
2021-09-22 15:54:10 +02:00
9ec46174a0 UI: split screenshot area into a separate operator
While the screenshot operator showed an "Area" option,
it wasn't usable from the main menu (unless accessed via menu search).

Split screenshot area into an operator that depends on cursor.
2021-09-22 15:54:10 +02:00
500d0e1fda UI: wait for input for operators that depend on cursor location
Support waiting for input so operators that depend on the
cursor location are usable from menus / buttons.

Use an operator type flag which the user interface code checks for,
waiting for input when run from a menu item.

This patch only supports this feature, there are no functional changes.

The motivation for this change is discoverability since some actions
were either hidden or broken when accessed from menus
(where the behavior of the operator depended on the menu location).

In general, waiting for input is *not* an efficient way to access tools,
however there are over 50 operators with a "wait_for_input" property
so this isn't introducing a new kind of interaction,
rather exposing this in a way that does not need to be hard-coded into
each operator, or having modal callbacks added for the sole purpose
of waiting for input.

Besides requiring boiler plate code using a "wait_for_input" property
has the added down-side of preventing key shortcuts from showing.
Only the menu items will enable the property,
causing them not to match key-map items.

Reviewed By: Severin

Ref D12255
2021-09-22 15:54:10 +02:00
711673caff Fix: Nodes modifier ignores input value with new property missing
An issue with the previous commit-- the default value of the type was
used instead of the property value when the "use_attribute" property
was missing.
2021-09-22 15:54:10 +02:00
2e309eb86a Geometry Nodes: Add a toggle to use attributes as input values
This adds a toggle to node group inputs exposed in the modifier to use
an attribute instead of a single value. When the toggle is pressed, the
button switches to a text button to choose an attribute name. Attribute
search isn't implemented here yet.

One confusing thing is that some values can't be driven by attributes
at all, like the size of a primitive node. In that case, we should have
a node warning, but that will be separate since it's more general.
We can also have an option to turn off this toggle in node group
input settings.

The two new properties for each input are stored with the same name
as the value, but with `"_use_attribute"` and `"_attribute_name"``
suffixes. The properties are not added for socket types that don't
support attribute input, like object sockets.

Differential Revision: https://developer.blender.org/D12504
2021-09-22 15:54:10 +02:00
f4ee2de310 UI: Automatic Blend Thumbnail Selection
Adds an "Auto" option to blend thumbnail types that will automatically
use Screenshot if there is no camera and 3dview, or workbench render
with shading settings from the largest 3dview.

See D12407 for more details.

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

Reviewed by Campbell Barton
2021-09-22 15:54:10 +02:00
a241b69982 Cleanup: Pass const mesh argument
Also remove unnecessary parantheses.
2021-09-22 15:54:10 +02:00
b4a6d70789 Cleanup: Move mesh_convert.c to C++
This should allow easier changes when it's helpful to use C++ types.
The diff is for a test on the buildbot.

Differential Revision: https://developer.blender.org/D12528
2021-09-22 15:54:10 +02:00
89fe4220bc UI: Remove Menus & Show Wait Cursor When Saving
When saving blend files close any menus that might be open, show
"waiting" mouse cursor right away, before creating preview.

See D12507 for more details.

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

Reviewed by Campbell Barton
2021-09-22 15:54:10 +02:00
2ea4b17c95 Cleanup: Remove unnecessary manual of move constructor
Turns out this isn't actually necessary.
2021-09-22 15:54:10 +02:00
f25c1f29b0 Geometry Nodes: Transfer attributes in the curve to mesh node
This patch allows point and spline attributes to be transferred to the
mesh generated by the curve to mesh node. All dynamic named and
anonymous attributes are transferred. So a user-created attribute will
be transferred, but "radius", "tilt" or the handle position attributes
won't be transferred by default and will need to be copied first.
This trade-off is made for performance, since most of the time, users
won't need these attributes copied.

Generally, attributes are transferred to the point/vertex domain.
However, if they have the same name as a built-in mesh attribute that
only exists on a different domain, like "shade_smooth", then they can
be transferred directly to that domain as well. Conversion directly to
the face corner domain is not necessary because there are no builtin
face corner attributes. I see this conversion directly to other domains
as an optimization we could use behind the scenes in the future as well,
when named attributes are less common.

For performance, I haven't tested which of the following is better:
```
for each spline combination:
    for each attribute:

for each attribute:
    for each spline combination:
```
For now I used the existing loop to avoid more threading overhead.

Differential Revision: https://developer.blender.org/D12363
2021-09-22 15:54:10 +02:00
084719ff1b Geometry Nodes: Simplify using OutputAttribute in a vector
Store the optional temporary span storage as a unique_ptr and move
it in the move constructor, to avoid the need to add a special move
constructor that clears the "show_warning" fields from it. Maybe this
is very slightly slower, but we'll need this class less often in the future
anyway.
2021-09-22 15:54:10 +02:00
23feb6770a Cleanup: Add built-in check for an attribute ID 2021-09-22 15:54:10 +02:00
7f8ba9dbb4 Rename App Menu to Blender Menu
"App" is a name reserved for the application templates at the moment.
It may become its own term in the near future if Templates are separated
from "Apps".

So since this is a name not exposed to the users we should renamed it
sooner than later.

Note that this will require scripts to update since the name of the
class is renamed here.

This also requires an update in the User Manual.

Differential Revision: https://developer.blender.org/D12523
2021-09-22 15:54:10 +02:00
fa165fc19e Assets: Recursive reading of asset libraries
With this, asset libraries can be directory structures and all assets in
sub-directories will show up in an Asset Browser.

With complex directory structures and many .blend files inside, asset
library reading will be quite slow for now. There's initial work being
done to introduce indexing for this (T82979), other optimizations are
being discussed as well.

Addresses T91406.

Differential Revision: https://developer.blender.org/D12139
2021-09-22 15:54:10 +02:00
1cf11da339 Hide empty keymap warnings when running a template 2021-09-22 15:54:10 +02:00
301c0c247c App Settings: Edge Resize
This prevents both editor resize as well as regions (e.g., the toolbar).

Note: This option is not visible in the UI.

Differential Revision: D12516
2021-09-22 15:54:10 +02:00
66e0182b80 App Settings: Regions Visbility Toggle
The toggle that allow users to "show" the region (header, toolbar, ...)
when it is collapsed can now be configured for the apps.

Note: This option is not visibile in the UI.

Differential Revision: D12516
2021-09-22 15:54:10 +02:00
4a538f0add Rename show_layout_ui > show_corner_split and remove from UI
This breaks API compatibility. However we are now grouping this setting
in the proper section (preferences.apps), so scripts had to update anyways.

So they may as well do it for the final name.

The reason to remove from the UI is that this is intended for app setup,
and as such it should not be exposed to final users until we have apps
better presented (for 3.1 hopefully).

Differential Revision: D12516
2021-09-22 15:54:10 +02:00
15acdf04d9 Cleanup: Rename USER_APP_LOCK_UI_LAYOUT
There will be other settings that lock other aspects of the
UI layout (e.g., resizing of editors). So better to name
this setting what it actually handles (the corners).

New name: USER_APP_LOCK_CORNER_SPLIT

Differential Revision: D12516
2021-09-22 15:54:10 +02:00
b4100bfa59 Cleanup: WM append code: de-duplicate post-make-local process a bit. 2021-09-22 15:54:10 +02:00
d82a73c67a Fix: properly implement the 'only append' execption case for WorkSpaces.
Add needed extra flag and utils to IDType to mark and check an ID type
as only appendable.

Note that this is only a loose user-level enforcement currently, in any
case you need to be able to link an ID to append it currently, so for
low-level code this does not really matter.

Currently only WorkSpace and Screen ID types are marked with the new
`IDTYPE_FLAGS_ONLY_APPEND` flag.
2021-09-22 15:54:09 +02:00
05b2ed822d Cleanup/Fix outliner 'make local' code.
While likely harmless, this code was doing extremely bad thing,
by-passing the whole lower-level `BKE_lib_id_make_local` call in case it
would fail and deciding by itself to forcefully make the given ID local.
Bad. Very, very, very bad.
2021-09-22 15:54:09 +02:00
71e29bf0b0 Cleanup: no need to clear new flags and pointers from whole Main when making a single ID local. 2021-09-22 15:54:09 +02:00
83449ab7d0 Cleanup: Comment about shpaekey being treated as embedded IDs in BKE_id_newptr_and_tag_clear. 2021-09-22 15:54:09 +02:00
9cee00621e Cleanup: Add comment about 'make_local' callback of Brush doing bad thing.
Callbacks in IDTypeInfo should never affect other IDs if they are not
embedded.

We break this rule in some cases, at least each of those should be
clearly commented about and get some security checks to try to avoid
unexpected issues as much as possible.
2021-09-22 15:54:09 +02:00
7df30bb5f6 Cleanup: Rename BKE_id_clear_newpoin to BKE_id_newptr_and_tag_clear. 2021-09-22 15:54:09 +02:00
abd31665dd Fix/refactor BKE_id_clear_newpoin and BKE_main_id_newptr_and_tag_clear.
Those were not clearing embdeed IDs flags and `newid` pointers at all...
2021-09-22 15:54:09 +02:00
68b306156c IDManagement: refactor: Remove 'test' part from BKE_lib_id_make_local.
Mixing testing and actual action in a single function is just not a good
way to do things, and the 'testing' feature is not used anywhere
anymore, time to get rid of it.
2021-09-22 15:54:09 +02:00
05cdd5f177 IDType: Add BKE_idtype_idcode_is_localizable.
This is the same as `BKE_idtype_idcode_is_linkable` currently, used only
in one place in UI code of IDtemplate.
2021-09-22 15:54:09 +02:00
a0949f0c30 IDType: Cleanup: Remove useless IDTYPE_FLAGS_NO_MAKELOCAL.
This flag became a full duplicate of `IDTYPE_FLAGS_NO_LIBLINKING`, which
is a good thing (don't think we ever want to be able to link some data,
without being able to make it local...).

So we can now remove it and use `IDTYPE_FLAGS_NO_LIBLINKING` instead.
2021-09-22 15:54:09 +02:00
a81e121d8d Fix ID-property UI versioning skipping nested meta-strips 2021-09-22 15:54:09 +02:00
5d3d2d839d Fix security popup re-displaying after undo
Don't reset these flags when loading a file (or undoing)
rely on BPY_python_reset to reset the flags.
2021-09-22 15:54:09 +02:00
5be80aea08 Fix memory leak if an error occurred assigning id-property sequence 2021-09-22 15:54:09 +02:00
081dc2cc92 Fix bisect gizmo offset while dragging 2021-09-22 15:54:09 +02:00
2923409795 GPencil: Fix dash modifier reading error.
The reference for parent modifier in segment data is not assigned. Now fixed.
2021-09-22 15:54:09 +02:00
14a9a3687b Cleanup: Use function to mark mesh normals dirty 2021-09-22 15:54:09 +02:00
0e1270ea9f Cleanup: Remove duplicate warning from subdivision surface node 2021-09-22 15:54:09 +02:00
5839e55295 Geometry Nodes: Hide values for selection inputs
Toggling the selection off in the node is the same as muting it,
so exposing it there doesn't help, and makes it less clear that it's
meant to be used as a field.
2021-09-22 15:54:09 +02:00
3f0d9a89d3 Fix: Division by zero in curve spiral primitive node 2021-09-22 15:54:09 +02:00
Johnny Matthews
0706f7a08a Splines: Add a method for reversing a Spline
This moved the spline reversing logic out of the Curve Reverse geometry
node and into the spline class. This allows a spline to reverse itself
with a call to `my_spline.reverse()`

The base class will reverse position, radii & tilt, while specialized
versions are created for Bezier and Nurbs splines to reverse the
additional data that these classes encapsulate.

Differential Revision: https://developer.blender.org/D12501
2021-09-22 15:54:09 +02:00
a969417f37 Tests: Updated test message for Directory Not Found 2021-09-22 15:54:09 +02:00
52bbd01895 Fix broken WorkSpace use after recent refactor of append code.
Essentially, Workspace (and Screen) types were defined as not
localizable.

In previous code it happended to work by mere chance (code path taken in
`BKE_library_make_local` was conviniently skipping the call to
`BKE_lib_id_make_local` in that case, hence not checking for
`IDTYPE_FLAGS_NO_MAKELOCAL` flag of the localized ID type).

This is a total abuse of this IDType flag, for now removing it.

That specific case (IDtype appendable but nor linkable) requires its own
proper flag, this will be tackled in a later commit.

Issue introduced in rB3be5ce4aad5e.
2021-09-22 15:54:09 +02:00
7e87a6963c Fix missing passes result when rendering multiple views
Caused by the lazily pass pixels allocation which didn't reset
allocation state of the render result.

Demo file: XXX
2021-09-22 15:54:09 +02:00
ba5859a5d7 Fix crash in 'drag asset' case in new append code from yesterday.
Scene and related pointer parameter can be NULL in link/append code, in
which case there is no instantiation of new objects/collections/obdata.

Link code in blendloader was already checking that, new instantiation
code in WM area from yesterday did not.

Issue introduced by rB3be5ce4aad5e.
2021-09-22 15:54:09 +02:00
Johnny Matthews
149501c774 BLI: Add a reverse method to MutableSpan
Add a method that allows a MutableSpan to reverse itself. This reverses
the data in the original span object. This is a first step in extracting
some functionality from nodes and making it more general.

Differential Revision: https://developer.blender.org/D12485
2021-09-22 15:54:09 +02:00
8ec6b1fc4c Geometry Nodes: Add fields version of material nodes
This commit moves the old material nodes to a "legacy" folder and adds
versions of the nodes that work with fields.

The "Select by Material" node is a field node now, so it doesn't have
a geometry output. This is an improvement because there are fewer links
to connect, and it's more easily usable in different situations.
It's also called "Material Selection", since it's more of an input
than an action now.

It's sometimes necessary to use the attribute capture node to get a
more predictable interpolation to mesh faces. This is because the
selection field input is always evaluated on the face domain, so
attribute inputs are interpolated before they are booleans, so they
cannot use the new interpolations from rB5841f8656d9580d7b9.

Differential Revision: https://developer.blender.org/D12456
2021-09-22 15:54:09 +02:00
f3241b8e34 Geometry Nodes: Add special domain interpolation for selections
The generic domain interpolation algorithms didn't quite work for
selections. The interpolation would do unexpected things that
were different than the results in edit mode. The new behavior
is supposed to be the same as edit mode, although we also have
to handle face corner selections here.

Currently the code assumes that all boolean attributes should be
handled that way. I'm not sure of why that wouldn't be the case,
but if we ever need non-selection boolean attributes, that could
be supported too.

Differential Revision: https://developer.blender.org/D12488
2021-09-22 15:54:09 +02:00
83bdfba499 Cleanup: avoid passing redundant parameter 2021-09-22 15:54:09 +02:00
e31d6560a2 Nodes: refactor socket declarations
This commits adds a few common flags to `SocketDeclaration`
so that they are available for all socket types (hide label, hide
value, is multi input). This allows porting over the remaining
geometry nodes to the new declaration system.

Furthermore, this commit separates the concepts of the socket
declaration and corresponding builders. The builders are used
by nodes to declare which sockets they have (e.g. `FloatBuilder`).
The ready build socket declarations can then be consumed by
other systems such as the versioning code. Both use cases
need different APIs and those will change for independent reasons,
so it makes sense to separate the classes.
2021-09-22 15:54:09 +02:00
aeeb7c82c6 Gizmo: add flag to hide the gizmo group during interaction
This allows a hack to be removed that temporarily overwrote
the 3D views gizmo display flag.

Also reverse change from fb27a9bb98
that runs poll on modal gizmo groups as there is some risk
that the poll function unlinks the gizmo.
2021-09-22 15:54:09 +02:00
bb310ff14e Cleanup: unused variable 2021-09-22 15:54:09 +02:00
9d63273bf8 Fix T91421: Length modifier bake influence check.
Reviewed By: Antonio Vazquez (antoniov)

Differential Revision: https://developer.blender.org/D12496
2021-09-22 15:54:09 +02:00
1a677512b7 Fix Asset Browser cannot open containing file anymore
In {rB9cff9f9f5df0} asset_library was renamed → asset_library_ref.

Missed to update this in assets.py.

Differential Revision: https://developer.blender.org/D12497
2021-09-22 15:54:09 +02:00
6312f82324 Geometry Nodes: multi threaded field evaluation
This adds a new `ParallelMultiFunction` which wraps another multi-function
and evaluates it with multiple threads. The speeds up field evaluation
quite a bit (the effect is most noticeable when the number of evaluations
and the field is large).

There are still other single-threaded performance bottlenecks in field
evaluation that will need to be solved separately. Most notably here
is the process of copying the computed data into the position attribute
in the Set Position node.

Differential Revision: https://developer.blender.org/D12457
2021-09-22 15:54:09 +02:00
1f8eab534d Gizmo: show groups flagged with SHOW_MODAL_ALL during interaction
Follow up to fix for T73684,
which allowed some modal gizmos to hide all others.

Also resolve an issue from 917a972b56
where shear the shear gizmo would be visible during interaction.

Internally there are some changes to gizmo behavior

- The gizmo with modal interaction wont draw if it's poll function fails.
- The WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL flag now causes these gizmo
  groups to draw when another group is being interacted with.
2021-09-22 15:54:09 +02:00
dea0dbc5ef Cleanup: replace defines with functions 2021-09-22 15:54:09 +02:00
0b8524c578 Cleanup: correct tracker ID in comment 2021-09-22 15:54:09 +02:00
8c61473869 GPencil: Dot dash modifier.
Create dot-dash effect for grease pencil strokes. User can manually edit the length, gap and styles for each segment of dashed lines.

The values in each segment can all be key-framed to make animations.

Reviewed By: Hans Goudey (HooglyBoogly), Antonio Vazquez (antoniov)

Differential Revision: http://developer.blender.org/D11876
2021-09-22 15:54:09 +02:00
4f65ad890b LineArt: Automatic crease with flat/smooth faces.
This allows crease lines to be automatically hidden on smooth surfaces, also provided options for:

- Showing crease on marked sharp edges.
- Force crease detection on smooth surfaces.

Reviewed By: Antonio Vazquez (antoniov)

Differential Revision: http://developer.blender.org/D12051
2021-09-22 15:54:09 +02:00
584b8a0def PyDocs: Update theme to latest version 2021-09-22 15:54:09 +02:00
47a7f02339 UI: Addition Changes to Freestyle Properties
- Material Properties: Use split column layout
- Remove the redundent term 'Options'
- Remove the redundent term 'Freesttle'
2021-09-22 15:54:09 +02:00
c0aa938931 Fix T89241: Scale to fit overflows into a second line 2021-09-22 15:54:09 +02:00
2aca22d60f Cleanup: spelling 2021-09-22 15:54:09 +02:00
926e839f80 Cleanup: doxy sections, parameter syntax 2021-09-22 15:54:09 +02:00
4ddce5f1c4 Fix: Use after free in spreadsheet attribute column ID
A temporary string was created in the attribute_foreach callback
and used in a map at a higher scope. When the callback finished,
the string went out of scope, was freed, then the elements in the
set pointed to freed memory.
2021-09-22 15:54:09 +02:00
Christoph Lendenfeld
7a373ecbf5 Fix T89027: "factor" field in pose breakdowner not updated
After applying the pose breakdowner,
the "factor" slider in the redo panel wasn't set to the correct value
This would cause the pose to jump around
once you start dragging the slider

Reviewed by: Sybren A. Stüvel
Differential Revision: https://developer.blender.org/D12187
Ref: D12187
2021-09-22 15:54:09 +02:00
5a8766180d LibLink: Enable unittest that was previously failing in append case.
Previous commit fixed it.
2021-09-22 15:54:09 +02:00
cef3d4225c LibLink: New Append code.
This commit fully refactors the way linked IDs are made local when
appended.

Previously, `BKE_library_make_local` was (ab)used for this task, but it
was missing some contextual data and doing complex processing to try to
work around this, with limited sucess. Further more, it was nearly
impossibe to extend (e.g. to get new append behaviors necessary for the
asset project).

The new code is a dedicated append step in WM linking process.

NOTE: BPY API (`libray.load()` context manager) uses its own code here,
which still relies on `BKE_library_make_local` for appending.
Unfortunately, merging those two different code paths is not trivial so
for now this API will remain unchanged.

Fix T55629: Append already linked Data is impossible.
2021-09-22 15:54:09 +02:00
abe1665569 ID management: Add new version of relink_to_newid using proper new remapping code.
Current `BKE_libblock_relink_to_newid` is using its own simplistic,
limited and not really correct version of ID remapping.

While doing a full replacement would have been ideal, this is
risky/time-constrained for Blender 3.0 release, so for now we'll have
both versions co-existing.
2021-09-22 15:54:09 +02:00
86761e4950 Cleanup: fix inconsistent parameter name
Found by clang-tidy.
2021-09-22 15:54:09 +02:00
494469897c Nodes: cache node declaration on node
Previously, it was necessary to rebuild the node declaration
every time it was used. Now it is cached per node for easy
and fast access.

For more details on what this is, look at the comment in
`DNA_node_types.h`.

Differential Revision: https://developer.blender.org/D12471
2021-09-22 15:54:09 +02:00
205ccb8bbe Cleanup: simplify resource scope methods
Previously, a debug name had to be passed to all methods
that added a resource to the `ResourceScope`. The idea was
that this would make it easier to find certain bugs. In reality
I never found this to be useful, and it was mostly annoying.
The thing is, something that is in a resource scope never leaks
(unless the resource scope is not destructed of course).

Removing the name parameter makes the structure easier to use.
2021-09-22 15:54:09 +02:00
cbd43cedc3 Fix: use type name instead of variable name
That was a typo in rBfd60f6713a9d9e6f7d706b53bf1311f2f1cd9031.
2021-09-22 15:54:09 +02:00
975d21a1a2 Functions: support optional outputs in multi-function
Sometimes not all outputs of a multi-function are required by the
caller. In those cases it would be a waste of compute resources
to calculate the unused values anyway. Now, the caller of a
multi-function can specify when a specific output is not used.
The called function can check if an output is unused and may
ignore it. Multi-functions can still computed unused outputs as
before if they don't want to check if a specific output is unused.

The multi-function procedure system has been updated to support
ignored outputs in call instructions. An ignored output just has no
variable assigned to it.

The field system has been updated to generate a multi-function
procedure where unused outputs are ignored.
2021-09-22 15:54:08 +02:00
14af051591 Geometry Nodes: fix memory leak for multi input sockets 2021-09-22 15:54:08 +02:00
52ee98859d Fix T90862: Texts in Outliner can have wrong icon
In contrast to the Filebrowser, the Outliner (Blender File view) did not
distinguish icons for text-based formats (if they have a filepath this
can be done though).

Maniphest Tasks: T90862

Differential Revision: https://developer.blender.org/D12347
2021-09-22 15:54:08 +02:00
c1e21847b9 Doc: expand on comment for why bound-box access could cause issues 2021-09-22 15:54:08 +02:00
296814ab92 UI: keep navigation gizmos visible during modal operators
Hiding viewport navigation gizmos caused the UI to "flicker"
unnecessarily, the axis could also be useful as a reference.

Resolves T73684
2021-09-22 15:54:08 +02:00
0da8a60a37 Update RNA to User Manual Mappings 2021-09-22 15:54:08 +02:00
667e24d8a0 Asset Template: Extra UI options
This allow users to show/hide:
* Library name / refresh.
* Assets names.
* Filter.

To set them in Python use:
display_options={'NO_NAMES', 'NO_FILTER', 'NO_LIBRARY'}

With contributions by Julian Eisel.

Differential Revision: https://developer.blender.org/D12476
2021-09-22 15:54:08 +02:00
84d5be6993 Cleanup: Remove duplicate code
Class SEQUENCER_PT_overlay as defined twice.
2021-09-22 15:54:08 +02:00
Germano Cavalcante
42aaff79db Fix T90736: Problem applying scale to curves
Curve Points of handle of type `Auto` on curves not uniformly scaled
cause the shape of the curve to be modified after applying the scale.

So change these handles to `Aligned` in these cases.

Reviewed By: campbellbarton

Maniphest Tasks: T90736

Differential Revision: https://developer.blender.org/D12281
2021-09-22 15:54:08 +02:00
0647f4f511 Cleanup: Remove unused variable 2021-09-15 01:50:42 +02:00
a4b39da6c7 UI: Use right-arrow icon for sub menus 2021-09-15 01:47:47 +02:00
ae42e4be1d UI: Match subpanels look with regular panels
By having coloured headers and offset the background.

TODO: Add margin to the bottom on the last subpanel.
2021-09-15 01:47:35 +02:00
8085f1e320 Merge branch 'master' into temp-ui-tweaks 2021-09-13 16:54:18 +02:00
cb23adf94b Merge branch 'master' into temp-ui-tweaks 2021-09-06 14:06:25 +02:00
ba7f03c1bb UI: Align panel titles
WIP: Experiment with shifting the panel titles slightly to the right
matching panels with checkboxes/icons in the header.
2021-09-03 20:06:43 +02:00
e262b00918 UI: Make panel titles text size same as labels
With panels now being more defined boxes, having a different size for the
panel title adds considerable noise.
2021-09-03 20:04:57 +02:00
48c2c7b1b4 UI: Dim collapse/expand icon on panels 2021-09-03 20:03:29 +02:00
83b342fd91 UI: Less prominent drag widget on panels
* Use 2 rows of 3 boxes per row, instead of 4.
* Slightly smaller
* Dimmer
2021-09-03 20:02:50 +02:00
18a3b9b238 Merge branch 'master' into temp-ui-tweaks 2021-09-03 16:39:01 +02:00
870404455b UI: Add margin to panels
Add some breathing space between the panel boundaries and properties/regions.

Make use of the style->panelouter property that hasn't been used in a while.
Also slight tweaks to boxspace and templatespace style properties so they
are multiples of 2 and operations on them round better.
2021-08-28 02:21:07 +02:00
323997ace3 UI: Disable emboss on box widgets
These are meant to be contour for an area flush with the background,
having emboss hardcoded on the widget prevents this.
2021-08-28 01:48:55 +02:00
72003be8fb Fix stack buffer overflow asan warning 2021-08-27 14:59:55 -05:00
82323cafb9 UI: Fix dark and light themes
Mainly so it doesn't look broken to people testing out the branch, namely
the background of radio buttons was pure white, and separator in menus pure black.

Also minor tweaks to the default theme.
2021-08-27 20:57:26 +02:00
7e4af18960 UI: Use rounded corners for panels and subpanels 2021-08-24 19:27:25 +02:00
f3b2cbcb81 UI: Separate each choice inside radio buttons
Add some room between the choices. This way we can give the background and
non-active options the same color and make it look more like only one choice
can be active at a time.

This is in an effort to make the radio buttons and toggle buttons look different.
Currently in Blender they looked the same even though radio buttons only allow
one active option at a time, while toggles can be have multiple.
2021-08-24 18:54:20 +02:00
811dc12e34 UI: Make use of menu item theme roundness
Use theme preference instead of hardcoded square corners.
2021-08-24 02:48:43 +02:00
1cba89572f UI: Use Outline color of menu item for separator
Instead of forcing a transparent shadeof the text color.
2021-08-24 02:46:43 +02:00
ad12ff5376 UI: Adjust outline color of active default buttons
Simply darken slightly the already used outline color. On light themes it prevents too bright outlines on hover.
2021-08-24 02:31:27 +02:00
49777cb729 UI: Support alpha in Value Slider widget
The alpha value was being ignored even though the item property could be set.
2021-08-24 02:13:32 +02:00
a64d281528 UI: Do not rotate vertical scale indicators
In animation editors with vertical scale indicators, such as Graph Editor
or Drivers, display the values aligned to the view.
2021-08-24 02:03:07 +02:00
b9fa39c8e9 UI: Improve contrast on playhead
Add a dark (background coloured) outline around it.
2021-08-20 03:13:24 +02:00
a62ca11d7f UI: Use arrow icon to collapse/expand panels 2021-08-20 00:48:26 +02:00
296 changed files with 8553 additions and 3387 deletions

View File

@@ -24,6 +24,7 @@ import project_source_info
import subprocess
import sys
import os
import tempfile
from typing import (
Any,
@@ -35,7 +36,6 @@ USE_QUIET = (os.environ.get("QUIET", None) is not None)
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
]
CHECKER_BIN = "cppcheck"
@@ -47,13 +47,19 @@ CHECKER_ARGS = [
"--max-configs=1", # speeds up execution
# "--check-config", # when includes are missing
"--enable=all", # if you want sixty hundred pedantic suggestions
# Quiet output, otherwise all defines/includes are printed (overly verbose).
# Only enable this for troubleshooting (if defines are not set as expected for example).
"--quiet",
# NOTE: `--cppcheck-build-dir=<dir>` is added later as a temporary directory.
]
if USE_QUIET:
CHECKER_ARGS.append("--quiet")
def main() -> None:
def cppcheck() -> None:
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
source_defines = project_source_info.build_defines_as_args()
@@ -78,7 +84,10 @@ def main() -> None:
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
sys.stdout.flush()
sys.stdout.write("%s " % percent_str)
sys.stdout.write("%s %s\n" % (
percent_str,
os.path.relpath(c, project_source_info.SOURCE_DIR)
))
return subprocess.Popen(cmd)
@@ -90,5 +99,11 @@ def main() -> None:
print("Finished!")
def main() -> None:
with tempfile.TemporaryDirectory() as temp_dir:
CHECKER_ARGS.append("--cppcheck-build-dir=" + temp_dir)
cppcheck()
if __name__ == "__main__":
main()

View File

@@ -243,7 +243,9 @@ def build_defines_as_args() -> List[str]:
# use this module.
def queue_processes(
process_funcs: Sequence[Tuple[Callable[..., subprocess.Popen[Any]], Tuple[Any, ...]]],
*,
job_total: int =-1,
sleep: float = 0.1,
) -> None:
""" Takes a list of function arg pairs, each function must return a process
"""
@@ -271,14 +273,20 @@ def queue_processes(
if len(processes) <= job_total:
break
else:
time.sleep(0.1)
time.sleep(sleep)
sys.stdout.flush()
sys.stderr.flush()
processes.append(func(*args))
# Don't return until all jobs have finished.
while 1:
processes[:] = [p for p in processes if p.poll() is None]
if not processes:
break
time.sleep(sleep)
def main() -> None:
if not os.path.exists(join(CMAKE_DIR, "CMakeCache.txt")):

View File

@@ -0,0 +1,40 @@
"""
This method enables conversions between Local and Pose space for bones in
the middle of updating the armature without having to update dependencies
after each change, by manually carrying updated matrices in a recursive walk.
"""
def set_pose_matrices(obj, matrix_map):
"Assign pose space matrices of all bones at once, ignoring constraints."
def rec(pbone, parent_matrix):
matrix = matrix_map[pbone.name]
## Instead of:
# pbone.matrix = matrix
# bpy.context.view_layer.update()
# Compute and assign local matrix, using the new parent matrix
if pbone.parent:
pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix,
pbone.bone.matrix_local,
parent_matrix=parent_matrix,
parent_matrix_local=pbone.parent.bone.matrix_local,
invert=True
)
else:
pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix,
pbone.bone.matrix_local,
invert=True
)
# Recursively process children, passing the new matrix through
for child in pbone.children:
rec(child, matrix)
# Scan all bone trees from their roots
for pbone in obj.pose.bones:
if not pbone.parent:
rec(pbone, None)

View File

@@ -1101,6 +1101,7 @@ context_type_map = {
"scene": ("Scene", False),
"sculpt_object": ("Object", False),
"selectable_objects": ("Object", True),
"selected_asset_files": ("FileSelectEntry", True),
"selected_bones": ("EditBone", True),
"selected_editable_bones": ("EditBone", True),
"selected_editable_fcurves": ("FCurve", True),

View File

@@ -64,6 +64,8 @@ if(WITH_CYCLES_STANDALONE)
cycles_standalone.cpp
cycles_xml.cpp
cycles_xml.h
oiio_output_driver.cpp
oiio_output_driver.h
)
add_executable(cycles ${SRC} ${INC} ${INC_SYS})
unset(SRC)
@@ -73,7 +75,7 @@ if(WITH_CYCLES_STANDALONE)
if(APPLE)
if(WITH_OPENCOLORIO)
set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit")
set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit -framework Carbon")
endif()
if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
# OpenImageDenoise uses BNNS from the Accelerate framework.

View File

@@ -36,6 +36,9 @@
#include "util/util_unique_ptr.h"
#include "util/util_version.h"
#include "app/cycles_xml.h"
#include "app/oiio_output_driver.h"
#ifdef WITH_CYCLES_STANDALONE_GUI
# include "util/util_view.h"
#endif
@@ -54,6 +57,7 @@ struct Options {
bool quiet;
bool show_help, interactive, pause;
string output_filepath;
string output_pass;
} options;
static void session_print(const string &str)
@@ -89,30 +93,6 @@ static void session_print_status()
session_print(status);
}
static bool write_render(const uchar *pixels, int w, int h, int channels)
{
string msg = string_printf("Writing image %s", options.output_path.c_str());
session_print(msg);
unique_ptr<ImageOutput> out = unique_ptr<ImageOutput>(ImageOutput::create(options.output_path));
if (!out) {
return false;
}
ImageSpec spec(w, h, channels, TypeDesc::UINT8);
if (!out->open(options.output_path, spec)) {
return false;
}
/* conversion for different top/bottom convention */
out->write_image(
TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride);
out->close();
return true;
}
static BufferParams &session_buffer_params()
{
static BufferParams buffer_params;
@@ -147,9 +127,14 @@ static void scene_init()
static void session_init()
{
options.session_params.write_render_cb = write_render;
options.output_pass = "combined";
options.session = new Session(options.session_params, options.scene_params);
if (!options.output_filepath.empty()) {
options.session->set_output_driver(make_unique<OIIOOutputDriver>(
options.output_filepath, options.output_pass, session_print));
}
if (options.session_params.background && !options.quiet)
options.session->progress.set_update_callback(function_bind(&session_print_status));
#ifdef WITH_CYCLES_STANDALONE_GUI
@@ -160,6 +145,11 @@ static void session_init()
/* load scene */
scene_init();
/* add pass for output. */
Pass *pass = options.scene->create_node<Pass>();
pass->set_name(ustring(options.output_pass.c_str()));
pass->set_type(PASS_COMBINED);
options.session->reset(options.session_params, session_buffer_params());
options.session->start();
}

View File

@@ -333,6 +333,7 @@ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node
}
snode = (ShaderNode *)node_type->create(node_type);
snode->set_owner(graph);
}
xml_read_node(graph_reader, snode, node);

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2021 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.
*/
#include "app/oiio_output_driver.h"
CCL_NAMESPACE_BEGIN
OIIOOutputDriver::OIIOOutputDriver(const string_view filepath,
const string_view pass,
LogFunction log)
: filepath_(filepath), pass_(pass), log_(log)
{
}
OIIOOutputDriver::~OIIOOutputDriver()
{
}
void OIIOOutputDriver::write_render_tile(const Tile &tile)
{
/* Only write the full buffer, no intermediate tiles. */
if (!(tile.size == tile.full_size)) {
return;
}
log_(string_printf("Writing image %s", filepath_.c_str()));
unique_ptr<ImageOutput> image_output(ImageOutput::create(filepath_));
if (image_output == nullptr) {
log_("Failed to create image file");
return;
}
const int width = tile.size.x;
const int height = tile.size.y;
ImageSpec spec(width, height, 4, TypeDesc::FLOAT);
if (!image_output->open(filepath_, spec)) {
log_("Failed to create image file");
return;
}
vector<float> pixels(width * height * 4);
if (!tile.get_pass_pixels(pass_, 4, pixels.data())) {
log_("Failed to read render pass pixels");
return;
}
/* Manipulate offset and stride to convert from bottom-up to top-down convention. */
image_output->write_image(TypeDesc::FLOAT,
pixels.data() + (height - 1) * width * 4,
AutoStride,
-width * 4 * sizeof(float),
AutoStride);
image_output->close();
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2021 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.
*/
#include "render/output_driver.h"
#include "util/util_function.h"
#include "util/util_image.h"
#include "util/util_string.h"
#include "util/util_unique_ptr.h"
#include "util/util_vector.h"
CCL_NAMESPACE_BEGIN
class OIIOOutputDriver : public OutputDriver {
public:
typedef function<void(const string &)> LogFunction;
OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log);
virtual ~OIIOOutputDriver();
void write_render_tile(const Tile &tile) override;
protected:
string filepath_;
string pass_;
LogFunction log_;
};
CCL_NAMESPACE_END

View File

@@ -31,13 +31,14 @@ set(INC_SYS
set(SRC
blender_camera.cpp
blender_device.cpp
blender_display_driver.cpp
blender_image.cpp
blender_geometry.cpp
blender_gpu_display.cpp
blender_light.cpp
blender_mesh.cpp
blender_object.cpp
blender_object_cull.cpp
blender_output_driver.cpp
blender_particles.cpp
blender_curves.cpp
blender_logging.cpp
@@ -51,10 +52,11 @@ set(SRC
CCL_api.h
blender_device.h
blender_gpu_display.h
blender_display_driver.h
blender_id_map.h
blender_image.h
blender_object_cull.h
blender_output_driver.h
blender_sync.h
blender_session.h
blender_texture.h

View File

@@ -211,7 +211,6 @@ def list_render_passes(scene, srl):
if crl.use_pass_shadow_catcher: yield ("Shadow Catcher", "RGB", 'COLOR')
# Debug passes.
if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE')
if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE')
# Cryptomatte passes.

View File

@@ -1197,12 +1197,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
pass_debug_render_time: BoolProperty(
name="Debug Render Time",
description="Render time in milliseconds per sample and pixel",
default=False,
update=update_render_passes,
)
pass_debug_sample_count: BoolProperty(
name="Debug Sample Count",
description="Number of samples/camera rays per pixel",

View File

@@ -792,7 +792,6 @@ class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel):
col.prop(view_layer, "use_pass_material_index")
col = layout.column(heading="Debug", align=True)
col.prop(cycles_view_layer, "pass_debug_render_time", text="Render Time")
col.prop(cycles_view_layer, "pass_debug_sample_count", text="Sample Count")
layout.prop(view_layer, "pass_alpha_threshold")

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "blender/blender_gpu_display.h"
#include "blender/blender_display_driver.h"
#include "device/device.h"
#include "util/util_logging.h"
@@ -273,17 +273,17 @@ uint BlenderDisplaySpaceShader::get_shader_program()
}
/* --------------------------------------------------------------------
* BlenderGPUDisplay.
* BlenderDisplayDriver.
*/
BlenderGPUDisplay::BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene)
BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene)
: b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene))
{
/* Create context while on the main thread. */
gl_context_create();
}
BlenderGPUDisplay::~BlenderGPUDisplay()
BlenderDisplayDriver::~BlenderDisplayDriver()
{
gl_resources_destroy();
}
@@ -292,19 +292,18 @@ BlenderGPUDisplay::~BlenderGPUDisplay()
* Update procedure.
*/
bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams &params,
bool BlenderDisplayDriver::update_begin(const Params &params,
int texture_width,
int texture_height)
{
/* Note that it's the responsibility of BlenderGPUDisplay to ensure updating and drawing
/* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing
* the texture does not happen at the same time. This is achieved indirectly.
*
* When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
* This same lock is also held when do_draw() is called, which together ensure mutual
* exclusion.
*
* This locking is not performed at the GPU display level, because that would cause lock
* inversion. */
* This locking is not performed on the Cycles side, because that would cause lock inversion. */
if (!gl_context_enable()) {
return false;
}
@@ -361,7 +360,7 @@ bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams &params,
return true;
}
void BlenderGPUDisplay::do_update_end()
void BlenderDisplayDriver::update_end()
{
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
@@ -369,54 +368,18 @@ void BlenderGPUDisplay::do_update_end()
gl_context_disable();
}
/* --------------------------------------------------------------------
* Texture update from CPU buffer.
*/
void BlenderGPUDisplay::do_copy_pixels_to_texture(
const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height)
{
/* This call copies pixels to a Pixel Buffer Object (PBO) which is much cheaper from CPU time
* point of view than to copy data directly to the OpenGL texture.
*
* The possible downside of this approach is that it might require a higher peak memory when
* doing partial updates of the texture (although, in practice even partial updates might peak
* with a full-frame buffer stored on the CPU if the GPU is currently occupied). */
half4 *mapped_rgba_pixels = map_texture_buffer();
if (!mapped_rgba_pixels) {
return;
}
if (texture_x == 0 && texture_y == 0 && pixels_width == texture_.width &&
pixels_height == texture_.height) {
const size_t size_in_bytes = sizeof(half4) * texture_.width * texture_.height;
memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes);
}
else {
const half4 *rgba_row = rgba_pixels;
half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_.width + texture_x;
for (int y = 0; y < pixels_height;
++y, rgba_row += pixels_width, mapped_rgba_row += texture_.width) {
memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width);
}
}
unmap_texture_buffer();
}
/* --------------------------------------------------------------------
* Texture buffer mapping.
*/
half4 *BlenderGPUDisplay::do_map_texture_buffer()
half4 *BlenderDisplayDriver::map_texture_buffer()
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
if (!mapped_rgba_pixels) {
LOG(ERROR) << "Error mapping BlenderGPUDisplay pixel buffer object.";
LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object.";
}
if (texture_.need_clear) {
@@ -431,7 +394,7 @@ half4 *BlenderGPUDisplay::do_map_texture_buffer()
return mapped_rgba_pixels;
}
void BlenderGPUDisplay::do_unmap_texture_buffer()
void BlenderDisplayDriver::unmap_texture_buffer()
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
@@ -442,9 +405,9 @@ void BlenderGPUDisplay::do_unmap_texture_buffer()
* Graphics interoperability.
*/
DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get()
BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get()
{
DeviceGraphicsInteropDestination interop_dst;
GraphicsInterop interop_dst;
interop_dst.buffer_width = texture_.buffer_width;
interop_dst.buffer_height = texture_.buffer_height;
@@ -456,12 +419,12 @@ DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get()
return interop_dst;
}
void BlenderGPUDisplay::graphics_interop_activate()
void BlenderDisplayDriver::graphics_interop_activate()
{
gl_context_enable();
}
void BlenderGPUDisplay::graphics_interop_deactivate()
void BlenderDisplayDriver::graphics_interop_deactivate()
{
gl_context_disable();
}
@@ -470,17 +433,17 @@ void BlenderGPUDisplay::graphics_interop_deactivate()
* Drawing.
*/
void BlenderGPUDisplay::clear()
void BlenderDisplayDriver::clear()
{
texture_.need_clear = true;
}
void BlenderGPUDisplay::set_zoom(float zoom_x, float zoom_y)
void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y)
{
zoom_ = make_float2(zoom_x, zoom_y);
}
void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
void BlenderDisplayDriver::draw(const Params &params)
{
/* See do_update_begin() for why no locking is required here. */
const bool transparent = true; // TODO(sergey): Derive this from Film.
@@ -497,7 +460,7 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
/* Texture is requested to be cleared and was not yet cleared.
*
* Do early return which should be equivalent of drawing all-zero texture.
* Watchout for the lock though so that the clear happening during update is properly
* Watch out for the lock though so that the clear happening during update is properly
* synchronized here. */
gl_context_mutex_.unlock();
return;
@@ -584,7 +547,7 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
}
}
void BlenderGPUDisplay::gl_context_create()
void BlenderDisplayDriver::gl_context_create()
{
/* When rendering in viewport there is no render context available via engine.
* Check whether own context is to be created here.
@@ -613,7 +576,7 @@ void BlenderGPUDisplay::gl_context_create()
}
}
bool BlenderGPUDisplay::gl_context_enable()
bool BlenderDisplayDriver::gl_context_enable()
{
if (use_gl_context_) {
if (!gl_context_) {
@@ -628,7 +591,7 @@ bool BlenderGPUDisplay::gl_context_enable()
return true;
}
void BlenderGPUDisplay::gl_context_disable()
void BlenderDisplayDriver::gl_context_disable()
{
if (use_gl_context_) {
if (gl_context_) {
@@ -641,7 +604,7 @@ void BlenderGPUDisplay::gl_context_disable()
RE_engine_render_context_disable(reinterpret_cast<RenderEngine *>(b_engine_.ptr.data));
}
void BlenderGPUDisplay::gl_context_dispose()
void BlenderDisplayDriver::gl_context_dispose()
{
if (gl_context_) {
const bool drw_state = DRW_opengl_context_release();
@@ -653,7 +616,7 @@ void BlenderGPUDisplay::gl_context_dispose()
}
}
bool BlenderGPUDisplay::gl_draw_resources_ensure()
bool BlenderDisplayDriver::gl_draw_resources_ensure()
{
if (!texture_.gl_id) {
/* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
@@ -680,7 +643,7 @@ bool BlenderGPUDisplay::gl_draw_resources_ensure()
return true;
}
void BlenderGPUDisplay::gl_resources_destroy()
void BlenderDisplayDriver::gl_resources_destroy()
{
gl_context_enable();
@@ -703,7 +666,7 @@ void BlenderGPUDisplay::gl_resources_destroy()
gl_context_dispose();
}
bool BlenderGPUDisplay::gl_texture_resources_ensure()
bool BlenderDisplayDriver::gl_texture_resources_ensure()
{
if (texture_.creation_attempted) {
return texture_.is_created;
@@ -740,7 +703,7 @@ bool BlenderGPUDisplay::gl_texture_resources_ensure()
return true;
}
void BlenderGPUDisplay::texture_update_if_needed()
void BlenderDisplayDriver::texture_update_if_needed()
{
if (!texture_.need_update) {
return;
@@ -754,7 +717,7 @@ void BlenderGPUDisplay::texture_update_if_needed()
texture_.need_update = false;
}
void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams &params)
void BlenderDisplayDriver::vertex_buffer_update(const Params &params)
{
/* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
* rendered. */
@@ -767,23 +730,23 @@ void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams &params)
vpointer[0] = 0.0f;
vpointer[1] = 0.0f;
vpointer[2] = params.offset.x;
vpointer[3] = params.offset.y;
vpointer[2] = params.full_offset.x;
vpointer[3] = params.full_offset.y;
vpointer[4] = 1.0f;
vpointer[5] = 0.0f;
vpointer[6] = (float)params.size.x + params.offset.x;
vpointer[7] = params.offset.y;
vpointer[6] = (float)params.size.x + params.full_offset.x;
vpointer[7] = params.full_offset.y;
vpointer[8] = 1.0f;
vpointer[9] = 1.0f;
vpointer[10] = (float)params.size.x + params.offset.x;
vpointer[11] = (float)params.size.y + params.offset.y;
vpointer[10] = (float)params.size.x + params.full_offset.x;
vpointer[11] = (float)params.size.y + params.full_offset.y;
vpointer[12] = 0.0f;
vpointer[13] = 1.0f;
vpointer[14] = params.offset.x;
vpointer[15] = (float)params.size.y + params.offset.y;
vpointer[14] = params.full_offset.x;
vpointer[15] = (float)params.size.y + params.full_offset.y;
glUnmapBuffer(GL_ARRAY_BUFFER);
}

View File

@@ -22,12 +22,14 @@
#include "RNA_blender_cpp.h"
#include "render/gpu_display.h"
#include "render/display_driver.h"
#include "util/util_thread.h"
#include "util/util_unique_ptr.h"
CCL_NAMESPACE_BEGIN
/* Base class of shader used for GPU display rendering. */
/* Base class of shader used for display driver rendering. */
class BlenderDisplayShader {
public:
static constexpr const char *position_attribute_name = "pos";
@@ -96,11 +98,11 @@ class BlenderDisplaySpaceShader : public BlenderDisplayShader {
uint shader_program_ = 0;
};
/* GPU display implementation which is specific for Blender viewport integration. */
class BlenderGPUDisplay : public GPUDisplay {
/* Display driver implementation which is specific for Blender viewport integration. */
class BlenderDisplayDriver : public DisplayDriver {
public:
BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene);
~BlenderGPUDisplay();
BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene);
~BlenderDisplayDriver();
virtual void graphics_interop_activate() override;
virtual void graphics_interop_deactivate() override;
@@ -110,22 +112,15 @@ class BlenderGPUDisplay : public GPUDisplay {
void set_zoom(float zoom_x, float zoom_y);
protected:
virtual bool do_update_begin(const GPUDisplayParams &params,
int texture_width,
int texture_height) override;
virtual void do_update_end() override;
virtual bool update_begin(const Params &params, int texture_width, int texture_height) override;
virtual void update_end() override;
virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels,
int texture_x,
int texture_y,
int pixels_width,
int pixels_height) override;
virtual void do_draw(const GPUDisplayParams &params) override;
virtual half4 *map_texture_buffer() override;
virtual void unmap_texture_buffer() override;
virtual half4 *do_map_texture_buffer() override;
virtual void do_unmap_texture_buffer() override;
virtual GraphicsInterop graphics_interop_get() override;
virtual DeviceGraphicsInteropDestination do_graphics_interop_get() override;
virtual void draw(const Params &params) override;
/* Helper function which allocates new GPU context. */
void gl_context_create();
@@ -152,13 +147,13 @@ class BlenderGPUDisplay : public GPUDisplay {
* This buffer is used to render texture in the viewport.
*
* NOTE: The buffer needs to be bound. */
void vertex_buffer_update(const GPUDisplayParams &params);
void vertex_buffer_update(const Params &params);
BL::RenderEngine b_engine_;
/* OpenGL context which is used the render engine doesn't have its own. */
void *gl_context_ = nullptr;
/* The when Blender RenderEngine side context is not available and the GPUDisplay is to create
/* The when Blender RenderEngine side context is not available and the DisplayDriver is to create
* its own context. */
bool use_gl_context_ = false;
/* Mutex used to guard the `gl_context_`. */

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2021 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.
*/
#include "blender/blender_output_driver.h"
CCL_NAMESPACE_BEGIN
BlenderOutputDriver::BlenderOutputDriver(BL::RenderEngine &b_engine) : b_engine_(b_engine)
{
}
BlenderOutputDriver::~BlenderOutputDriver()
{
}
bool BlenderOutputDriver::read_render_tile(const Tile &tile)
{
/* Get render result. */
BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x,
tile.offset.y,
tile.size.x,
tile.size.y,
tile.layer.c_str(),
tile.view.c_str());
/* Can happen if the intersected rectangle gives 0 width or height. */
if (b_rr.ptr.data == NULL) {
return false;
}
BL::RenderResult::layers_iterator b_single_rlay;
b_rr.layers.begin(b_single_rlay);
/* layer will be missing if it was disabled in the UI */
if (b_single_rlay == b_rr.layers.end()) {
return false;
}
BL::RenderLayer b_rlay = *b_single_rlay;
vector<float> pixels(tile.size.x * tile.size.y * 4);
/* Copy each pass.
* TODO:copy only the required ones for better performance? */
for (BL::RenderPass &b_pass : b_rlay.passes) {
tile.set_pass_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect());
}
b_engine_.end_result(b_rr, false, false, false);
return true;
}
bool BlenderOutputDriver::update_render_tile(const Tile &tile)
{
/* Use final write for preview renders, otherwise render result wouldn't be be updated
* quickly on Blender side. For all other cases we use the display driver. */
if (b_engine_.is_preview()) {
write_render_tile(tile);
return true;
}
else {
/* Don't highlight full-frame tile. */
if (!(tile.size == tile.full_size)) {
b_engine_.tile_highlight_clear_all();
b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true);
}
return false;
}
}
void BlenderOutputDriver::write_render_tile(const Tile &tile)
{
b_engine_.tile_highlight_clear_all();
/* Get render result. */
BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x,
tile.offset.y,
tile.size.x,
tile.size.y,
tile.layer.c_str(),
tile.view.c_str());
/* Can happen if the intersected rectangle gives 0 width or height. */
if (b_rr.ptr.data == NULL) {
return;
}
BL::RenderResult::layers_iterator b_single_rlay;
b_rr.layers.begin(b_single_rlay);
/* Layer will be missing if it was disabled in the UI. */
if (b_single_rlay == b_rr.layers.end()) {
return;
}
BL::RenderLayer b_rlay = *b_single_rlay;
vector<float> pixels(tile.size.x * tile.size.y * 4);
/* Copy each pass. */
for (BL::RenderPass &b_pass : b_rlay.passes) {
if (!tile.get_pass_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) {
memset(&pixels[0], 0, pixels.size() * sizeof(float));
}
b_pass.rect(&pixels[0]);
}
b_engine_.end_result(b_rr, true, false, true);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2021 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
#include "MEM_guardedalloc.h"
#include "RNA_blender_cpp.h"
#include "render/output_driver.h"
CCL_NAMESPACE_BEGIN
class BlenderOutputDriver : public OutputDriver {
public:
BlenderOutputDriver(BL::RenderEngine &b_engine);
~BlenderOutputDriver();
virtual void write_render_tile(const Tile &tile) override;
virtual bool update_render_tile(const Tile &tile) override;
virtual bool read_render_tile(const Tile &tile) override;
protected:
BL::RenderEngine b_engine_;
};
CCL_NAMESPACE_END

View File

@@ -42,7 +42,8 @@
#include "util/util_progress.h"
#include "util/util_time.h"
#include "blender/blender_gpu_display.h"
#include "blender/blender_display_driver.h"
#include "blender/blender_output_driver.h"
#include "blender/blender_session.h"
#include "blender/blender_sync.h"
#include "blender/blender_util.h"
@@ -157,11 +158,13 @@ void BlenderSession::create_session()
b_v3d, b_rv3d, scene->camera, width, height);
session->reset(session_params, buffer_params);
/* Create GPU display. */
/* Create GPU display.
* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */
if (!b_engine.is_preview() && !headless) {
unique_ptr<BlenderGPUDisplay> gpu_display = make_unique<BlenderGPUDisplay>(b_engine, b_scene);
gpu_display_ = gpu_display.get();
session->set_gpu_display(move(gpu_display));
unique_ptr<BlenderDisplayDriver> display_driver = make_unique<BlenderDisplayDriver>(b_engine,
b_scene);
display_driver_ = display_driver.get();
session->set_display_driver(move(display_driver));
}
/* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform
@@ -278,96 +281,6 @@ void BlenderSession::free_session()
session = nullptr;
}
void BlenderSession::read_render_tile()
{
const int2 tile_offset = session->get_render_tile_offset();
const int2 tile_size = session->get_render_tile_size();
/* get render result */
BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x,
tile_offset.y,
tile_size.x,
tile_size.y,
b_rlay_name.c_str(),
b_rview_name.c_str());
/* can happen if the intersected rectangle gives 0 width or height */
if (b_rr.ptr.data == NULL) {
return;
}
BL::RenderResult::layers_iterator b_single_rlay;
b_rr.layers.begin(b_single_rlay);
/* layer will be missing if it was disabled in the UI */
if (b_single_rlay == b_rr.layers.end())
return;
BL::RenderLayer b_rlay = *b_single_rlay;
vector<float> pixels(tile_size.x * tile_size.y * 4);
/* Copy each pass.
* TODO:copy only the required ones for better performance? */
for (BL::RenderPass &b_pass : b_rlay.passes) {
session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect());
}
b_engine.end_result(b_rr, false, false, false);
}
void BlenderSession::write_render_tile()
{
const int2 tile_offset = session->get_render_tile_offset();
const int2 tile_size = session->get_render_tile_size();
const string_view render_layer_name = session->get_render_tile_layer();
const string_view render_view_name = session->get_render_tile_view();
b_engine.tile_highlight_clear_all();
/* get render result */
BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x,
tile_offset.y,
tile_size.x,
tile_size.y,
render_layer_name.c_str(),
render_view_name.c_str());
/* can happen if the intersected rectangle gives 0 width or height */
if (b_rr.ptr.data == NULL) {
return;
}
BL::RenderResult::layers_iterator b_single_rlay;
b_rr.layers.begin(b_single_rlay);
/* layer will be missing if it was disabled in the UI */
if (b_single_rlay == b_rr.layers.end()) {
return;
}
BL::RenderLayer b_rlay = *b_single_rlay;
write_render_result(b_rlay);
b_engine.end_result(b_rr, true, false, true);
}
void BlenderSession::update_render_tile()
{
if (!session->has_multiple_render_tiles()) {
/* Don't highlight full-frame tile. */
return;
}
const int2 tile_offset = session->get_render_tile_offset();
const int2 tile_size = session->get_render_tile_size();
b_engine.tile_highlight_clear_all();
b_engine.tile_highlight_set(tile_offset.x, tile_offset.y, tile_size.x, tile_size.y, true);
}
void BlenderSession::full_buffer_written(string_view filename)
{
full_buffer_files_.emplace_back(filename);
@@ -441,18 +354,8 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
return;
}
/* set callback to write out render results */
session->write_render_tile_cb = [&]() { write_render_tile(); };
/* Use final write for preview renders, otherwise render result wouldn't be be updated on Blender
* side. */
/* TODO(sergey): Investigate whether GPUDisplay can be used for the preview as well. */
if (b_engine.is_preview()) {
session->update_render_tile_cb = [&]() { write_render_tile(); };
}
else {
session->update_render_tile_cb = [&]() { update_render_tile(); };
}
/* Create driver to write out render results. */
session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); };
@@ -598,10 +501,13 @@ void BlenderSession::render_frame_finish()
path_remove(filename);
}
/* clear callback */
session->write_render_tile_cb = function_null;
session->update_render_tile_cb = function_null;
/* Clear driver. */
session->set_output_driver(nullptr);
session->full_buffer_written_cb = function_null;
/* All the files are handled.
* Clear the list so that this session can be re-used by Persistent Data. */
full_buffer_files_.clear();
}
static PassType bake_type_to_pass(const string &bake_type_str, const int bake_filter)
@@ -706,9 +612,8 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
pass->set_type(bake_type_to_pass(bake_type, bake_filter));
pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR));
session->read_render_tile_cb = [&]() { read_render_tile(); };
session->write_render_tile_cb = [&]() { write_render_tile(); };
session->set_gpu_display(nullptr);
session->set_display_driver(nullptr);
session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
if (!session->progress.get_cancel()) {
/* Sync scene. */
@@ -751,43 +656,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
session->wait();
}
session->read_render_tile_cb = function_null;
session->write_render_tile_cb = function_null;
}
void BlenderSession::write_render_result(BL::RenderLayer &b_rlay)
{
if (!session->copy_render_tile_from_device()) {
return;
}
const int2 tile_size = session->get_render_tile_size();
vector<float> pixels(tile_size.x * tile_size.y * 4);
/* Copy each pass. */
for (BL::RenderPass &b_pass : b_rlay.passes) {
if (!session->get_render_tile_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) {
memset(&pixels[0], 0, pixels.size() * sizeof(float));
}
b_pass.rect(&pixels[0]);
}
}
void BlenderSession::update_render_result(BL::RenderLayer &b_rlay)
{
if (!session->copy_render_tile_from_device()) {
return;
}
const int2 tile_size = session->get_render_tile_size();
vector<float> pixels(tile_size.x * tile_size.y * 4);
/* Copy combined pass. */
BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
if (session->get_render_tile_pixels("Combined", b_combined_pass.channels(), &pixels[0])) {
b_combined_pass.rect(&pixels[0]);
}
session->set_output_driver(nullptr);
}
void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
@@ -895,7 +764,7 @@ void BlenderSession::draw(BL::SpaceImageEditor &space_image)
}
BL::Array<float, 2> zoom = space_image.zoom();
gpu_display_->set_zoom(zoom[0], zoom[1]);
display_driver_->set_zoom(zoom[0], zoom[1]);
session->draw();
}

View File

@@ -29,7 +29,7 @@
CCL_NAMESPACE_BEGIN
class BlenderGPUDisplay;
class BlenderDisplayDriver;
class BlenderSync;
class ImageMetaData;
class Scene;
@@ -70,20 +70,7 @@ class BlenderSession {
const int bake_width,
const int bake_height);
void write_render_result(BL::RenderLayer &b_rlay);
void write_render_tile();
void update_render_tile();
void full_buffer_written(string_view filename);
/* update functions are used to update display buffer only after sample was rendered
* only needed for better visual feedback */
void update_render_result(BL::RenderLayer &b_rlay);
/* read functions for baking input */
void read_render_tile();
/* interactive updates */
void synchronize(BL::Depsgraph &b_depsgraph);
@@ -164,8 +151,8 @@ class BlenderSession {
int last_pass_index = -1;
} draw_state_;
/* NOTE: The BlenderSession references the GPU display. */
BlenderGPUDisplay *gpu_display_ = nullptr;
/* NOTE: The BlenderSession references the display driver. */
BlenderDisplayDriver *display_driver_ = nullptr;
vector<string> full_buffer_files_;
};

View File

@@ -279,7 +279,7 @@ static ShaderNode *add_node(Scene *scene,
array<float3> curve_mapping_curves;
float min_x, max_x;
curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, true);
curvemapping_minmax(mapping, true, &min_x, &max_x);
curvemapping_minmax(mapping, 4, &min_x, &max_x);
curves->set_min_x(min_x);
curves->set_max_x(max_x);
curves->set_curves(curve_mapping_curves);
@@ -292,12 +292,25 @@ static ShaderNode *add_node(Scene *scene,
array<float3> curve_mapping_curves;
float min_x, max_x;
curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, false);
curvemapping_minmax(mapping, false, &min_x, &max_x);
curvemapping_minmax(mapping, 3, &min_x, &max_x);
curves->set_min_x(min_x);
curves->set_max_x(max_x);
curves->set_curves(curve_mapping_curves);
node = curves;
}
else if (b_node.is_a(&RNA_ShaderNodeFloatCurve)) {
BL::ShaderNodeFloatCurve b_curve_node(b_node);
BL::CurveMapping mapping(b_curve_node.mapping());
FloatCurveNode *curve = graph->create_node<FloatCurveNode>();
array<float> curve_mapping_curve;
float min_x, max_x;
curvemapping_float_to_array(mapping, curve_mapping_curve, RAMP_TABLE_SIZE);
curvemapping_minmax(mapping, 1, &min_x, &max_x);
curve->set_min_x(min_x);
curve->set_max_x(max_x);
curve->set_curve(curve_mapping_curve);
node = curve;
}
else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) {
RGBRampNode *ramp = graph->create_node<RGBRampNode>();
BL::ShaderNodeValToRGB b_ramp_node(b_node);

View File

@@ -545,8 +545,6 @@ static PassType get_blender_pass_type(BL::RenderPass &b_pass)
MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER);
MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER);
MAP_PASS("Debug Render Time", PASS_RENDER_TIME);
MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER);
MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT);
@@ -604,10 +602,6 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
/* Debug passes. */
if (get_boolean(crl, "pass_debug_render_time")) {
b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
pass_add(scene, PASS_RENDER_TIME, "Debug Render Time");
}
if (get_boolean(crl, "pass_debug_sample_count")) {
b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str());
pass_add(scene, PASS_SAMPLE_COUNT, "Debug Sample Count");

View File

@@ -171,12 +171,11 @@ static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap &curve, float *m
}
static inline void curvemapping_minmax(/*const*/ BL::CurveMapping &cumap,
bool rgb_curve,
int num_curves,
float *min_x,
float *max_x)
{
// const int num_curves = cumap.curves.length(); /* Gives linking error so far. */
const int num_curves = rgb_curve ? 4 : 3;
*min_x = FLT_MAX;
*max_x = -FLT_MAX;
for (int i = 0; i < num_curves; ++i) {
@@ -196,6 +195,28 @@ static inline void curvemapping_to_array(BL::CurveMapping &cumap, array<float> &
}
}
static inline void curvemapping_float_to_array(BL::CurveMapping &cumap,
array<float> &data,
int size)
{
float min = 0.0f, max = 1.0f;
curvemapping_minmax(cumap, 1, &min, &max);
const float range = max - min;
cumap.update();
BL::CurveMap map = cumap.curves[0];
data.resize(size);
for (int i = 0; i < size; i++) {
float t = min + (float)i / (float)(size - 1) * range;
data[i] = cumap.evaluate(map, t);
}
}
static inline void curvemapping_color_to_array(BL::CurveMapping &cumap,
array<float3> &data,
int size,
@@ -214,7 +235,8 @@ static inline void curvemapping_color_to_array(BL::CurveMapping &cumap,
*
* There might be some better estimations here tho.
*/
curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x);
const int num_curves = rgb_curve ? 4 : 3;
curvemapping_minmax(cumap, num_curves, &min_x, &max_x);
const float range_x = max_x - min_x;

View File

@@ -37,14 +37,15 @@ CUDADeviceGraphicsInterop::~CUDADeviceGraphicsInterop()
}
}
void CUDADeviceGraphicsInterop::set_destination(
const DeviceGraphicsInteropDestination &destination)
void CUDADeviceGraphicsInterop::set_display_interop(
const DisplayDriver::GraphicsInterop &display_interop)
{
const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height;
const int64_t new_buffer_area = int64_t(display_interop.buffer_width) *
display_interop.buffer_height;
need_clear_ = destination.need_clear;
need_clear_ = display_interop.need_clear;
if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) {
if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) {
return;
}
@@ -55,12 +56,12 @@ void CUDADeviceGraphicsInterop::set_destination(
}
const CUresult result = cuGraphicsGLRegisterBuffer(
&cu_graphics_resource_, destination.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
&cu_graphics_resource_, display_interop.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
if (result != CUDA_SUCCESS) {
LOG(ERROR) << "Error registering OpenGL buffer: " << cuewErrorString(result);
}
opengl_pbo_id_ = destination.opengl_pbo_id;
opengl_pbo_id_ = display_interop.opengl_pbo_id;
buffer_area_ = new_buffer_area;
}

View File

@@ -41,7 +41,7 @@ class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop {
CUDADeviceGraphicsInterop &operator=(const CUDADeviceGraphicsInterop &other) = delete;
CUDADeviceGraphicsInterop &operator=(CUDADeviceGraphicsInterop &&other) = delete;
virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override;
virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override;
virtual device_ptr map() override;
virtual void unmap() override;

View File

@@ -16,25 +16,12 @@
#pragma once
#include "render/display_driver.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
/* Information about interoperability destination.
* Is provided by the GPUDisplay. */
class DeviceGraphicsInteropDestination {
public:
/* Dimensions of the buffer, in pixels. */
int buffer_width = 0;
int buffer_height = 0;
/* OpenGL pixel buffer object. */
int opengl_pbo_id = 0;
/* Clear the entire destination before doing partial write to it. */
bool need_clear = false;
};
/* Device-side graphics interoperability support.
*
* Takes care of holding all the handlers needed by the device to implement interoperability with
@@ -46,7 +33,7 @@ class DeviceGraphicsInterop {
/* Update this device-side graphics interoperability object with the given destination resource
* information. */
virtual void set_destination(const DeviceGraphicsInteropDestination &destination) = 0;
virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) = 0;
virtual device_ptr map() = 0;
virtual void unmap() = 0;

View File

@@ -37,11 +37,15 @@ HIPDeviceGraphicsInterop::~HIPDeviceGraphicsInterop()
}
}
void HIPDeviceGraphicsInterop::set_destination(const DeviceGraphicsInteropDestination &destination)
void HIPDeviceGraphicsInterop::set_display_interop(
const DisplayDriver::GraphicsInterop &display_interop)
{
const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height;
const int64_t new_buffer_area = int64_t(display_interop.buffer_width) *
display_interop.buffer_height;
if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) {
need_clear_ = display_interop.need_clear;
if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) {
return;
}
@@ -52,12 +56,12 @@ void HIPDeviceGraphicsInterop::set_destination(const DeviceGraphicsInteropDestin
}
const hipError_t result = hipGraphicsGLRegisterBuffer(
&hip_graphics_resource_, destination.opengl_pbo_id, hipGraphicsRegisterFlagsNone);
&hip_graphics_resource_, display_interop.opengl_pbo_id, hipGraphicsRegisterFlagsNone);
if (result != hipSuccess) {
LOG(ERROR) << "Error registering OpenGL buffer: " << hipewErrorString(result);
}
opengl_pbo_id_ = destination.opengl_pbo_id;
opengl_pbo_id_ = display_interop.opengl_pbo_id;
buffer_area_ = new_buffer_area;
}
@@ -77,6 +81,14 @@ device_ptr HIPDeviceGraphicsInterop::map()
hip_device_assert(
device_, hipGraphicsResourceGetMappedPointer(&hip_buffer, &bytes, hip_graphics_resource_));
if (need_clear_) {
hip_device_assert(
device_,
hipMemsetD8Async(static_cast<hipDeviceptr_t>(hip_buffer), 0, bytes, queue_->stream()));
need_clear_ = false;
}
return static_cast<device_ptr>(hip_buffer);
}

View File

@@ -39,7 +39,7 @@ class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop {
HIPDeviceGraphicsInterop &operator=(const HIPDeviceGraphicsInterop &other) = delete;
HIPDeviceGraphicsInterop &operator=(HIPDeviceGraphicsInterop &&other) = delete;
virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override;
virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override;
virtual device_ptr map() override;
virtual void unmap() override;
@@ -53,6 +53,9 @@ class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop {
/* Buffer area in pixels of the corresponding PBO. */
int64_t buffer_area_ = 0;
/* The destination was requested to be cleared. */
bool need_clear_ = false;
hipGraphicsResource hip_graphics_resource_ = nullptr;
};

View File

@@ -28,7 +28,7 @@ void HIPDeviceKernels::load(HIPDevice *device)
for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) {
HIPDeviceKernel &kernel = kernels_[i];
/* No megakernel used for GPU. */
/* No mega-kernel used for GPU. */
if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) {
continue;
}

View File

@@ -1419,7 +1419,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
}
else {
/* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves,
* since it needs to filter out endcaps there).
* since it needs to filter out end-caps there).
* It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit
* programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT.
*/

View File

@@ -27,6 +27,8 @@ set(SRC
pass_accessor.cpp
pass_accessor_cpu.cpp
pass_accessor_gpu.cpp
path_trace_display.cpp
path_trace_tile.cpp
path_trace_work.cpp
path_trace_work_cpu.cpp
path_trace_work_gpu.cpp
@@ -47,6 +49,8 @@ set(SRC_HEADERS
pass_accessor.h
pass_accessor_cpu.h
pass_accessor_gpu.h
path_trace_display.h
path_trace_tile.h
path_trace_work.h
path_trace_work_cpu.h
path_trace_work_gpu.h

View File

@@ -149,9 +149,6 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
/* Denoised passes store their final pixels, no need in special calculation. */
get_pass_float(render_buffers, buffer_params, destination);
}
else if (type == PASS_RENDER_TIME) {
/* TODO(sergey): Needs implementation. */
}
else if (type == PASS_DEPTH) {
get_pass_depth(render_buffers, buffer_params, destination);
}

View File

@@ -19,8 +19,9 @@
#include "device/cpu/device.h"
#include "device/device.h"
#include "integrator/pass_accessor.h"
#include "integrator/path_trace_display.h"
#include "integrator/path_trace_tile.h"
#include "integrator/render_scheduler.h"
#include "render/gpu_display.h"
#include "render/pass.h"
#include "render/scene.h"
#include "render/tile.h"
@@ -67,11 +68,11 @@ PathTrace::PathTrace(Device *device,
PathTrace::~PathTrace()
{
/* Destroy any GPU resource which was used for graphics interop.
* Need to have access to the GPUDisplay as it is the only source of drawing context which is
* used for interop. */
if (gpu_display_) {
* Need to have access to the PathTraceDisplay as it is the only source of drawing context which
* is used for interop. */
if (display_) {
for (auto &&path_trace_work : path_trace_works_) {
path_trace_work->destroy_gpu_resources(gpu_display_.get());
path_trace_work->destroy_gpu_resources(display_.get());
}
}
}
@@ -94,7 +95,7 @@ bool PathTrace::ready_to_reset()
{
/* The logic here is optimized for the best feedback in the viewport, which implies having a GPU
* display. Of there is no such display, the logic here will break. */
DCHECK(gpu_display_);
DCHECK(display_);
/* The logic here tries to provide behavior which feels the most interactive feel to artists.
* General idea is to be able to reset as quickly as possible, while still providing interactive
@@ -126,8 +127,8 @@ void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_t
/* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation.
* It is requires to inform about reset whenever it happens, so that the redraw state tracking is
* properly updated. */
if (gpu_display_) {
gpu_display_->reset(full_params);
if (display_) {
display_->reset(full_params);
}
render_state_.has_denoised_result = false;
@@ -535,25 +536,35 @@ void PathTrace::denoise(const RenderWork &render_work)
render_scheduler_.report_denoise_time(render_work, time_dt() - start_time);
}
void PathTrace::set_gpu_display(unique_ptr<GPUDisplay> gpu_display)
void PathTrace::set_output_driver(unique_ptr<OutputDriver> driver)
{
gpu_display_ = move(gpu_display);
output_driver_ = move(driver);
}
void PathTrace::clear_gpu_display()
void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver)
{
if (gpu_display_) {
gpu_display_->clear();
if (driver) {
display_ = make_unique<PathTraceDisplay>(move(driver));
}
else {
display_ = nullptr;
}
}
void PathTrace::clear_display()
{
if (display_) {
display_->clear();
}
}
void PathTrace::draw()
{
if (!gpu_display_) {
if (!display_) {
return;
}
did_draw_after_reset_ |= gpu_display_->draw();
did_draw_after_reset_ |= display_->draw();
}
void PathTrace::update_display(const RenderWork &render_work)
@@ -562,31 +573,32 @@ void PathTrace::update_display(const RenderWork &render_work)
return;
}
if (!gpu_display_ && !tile_buffer_update_cb) {
if (!display_ && !output_driver_) {
VLOG(3) << "Ignore display update.";
return;
}
if (full_params_.width == 0 || full_params_.height == 0) {
VLOG(3) << "Skipping GPUDisplay update due to 0 size of the render buffer.";
VLOG(3) << "Skipping PathTraceDisplay update due to 0 size of the render buffer.";
return;
}
const double start_time = time_dt();
if (tile_buffer_update_cb) {
if (output_driver_) {
VLOG(3) << "Invoke buffer update callback.";
tile_buffer_update_cb();
PathTraceTile tile(*this);
output_driver_->update_render_tile(tile);
}
if (gpu_display_) {
if (display_) {
VLOG(3) << "Perform copy to GPUDisplay work.";
const int resolution_divider = render_work.resolution_divider;
const int texture_width = max(1, full_params_.width / resolution_divider);
const int texture_height = max(1, full_params_.height / resolution_divider);
if (!gpu_display_->update_begin(texture_width, texture_height)) {
if (!display_->update_begin(texture_width, texture_height)) {
LOG(ERROR) << "Error beginning GPUDisplay update.";
return;
}
@@ -600,10 +612,10 @@ void PathTrace::update_display(const RenderWork &render_work)
* all works in parallel. */
const int num_samples = get_num_samples_in_buffer();
for (auto &&path_trace_work : path_trace_works_) {
path_trace_work->copy_to_gpu_display(gpu_display_.get(), pass_mode, num_samples);
path_trace_work->copy_to_display(display_.get(), pass_mode, num_samples);
}
gpu_display_->update_end();
display_->update_end();
}
render_scheduler_.report_display_update_time(render_work, time_dt() - start_time);
@@ -753,20 +765,26 @@ bool PathTrace::is_cancel_requested()
void PathTrace::tile_buffer_write()
{
if (!tile_buffer_write_cb) {
if (!output_driver_) {
return;
}
tile_buffer_write_cb();
PathTraceTile tile(*this);
output_driver_->write_render_tile(tile);
}
void PathTrace::tile_buffer_read()
{
if (!tile_buffer_read_cb) {
if (!device_scene_->data.bake.use) {
return;
}
if (tile_buffer_read_cb()) {
if (!output_driver_) {
return;
}
PathTraceTile tile(*this);
if (output_driver_->read_render_tile(tile)) {
tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->copy_render_buffers_to_device();
});
@@ -1005,6 +1023,11 @@ int2 PathTrace::get_render_tile_offset() const
return make_int2(tile.x, tile.y);
}
int2 PathTrace::get_render_size() const
{
return tile_manager_.get_size();
}
const BufferParams &PathTrace::get_render_tile_params() const
{
if (full_frame_state_.render_buffers) {

View File

@@ -31,12 +31,14 @@ CCL_NAMESPACE_BEGIN
class AdaptiveSampling;
class Device;
class DeviceScene;
class DisplayDriver;
class Film;
class RenderBuffers;
class RenderScheduler;
class RenderWork;
class PathTraceDisplay;
class OutputDriver;
class Progress;
class GPUDisplay;
class TileManager;
/* PathTrace class takes care of kernel graph and scheduling on a (multi)device. It takes care of
@@ -98,13 +100,16 @@ class PathTrace {
* Use this to configure the adaptive sampler before rendering any samples. */
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling);
/* Set GPU display which takes care of drawing the render result. */
void set_gpu_display(unique_ptr<GPUDisplay> gpu_display);
/* Sets output driver for render buffer output. */
void set_output_driver(unique_ptr<OutputDriver> driver);
/* Clear the GPU display by filling it in with all zeroes. */
void clear_gpu_display();
/* Set display driver for interactive render buffer display. */
void set_display_driver(unique_ptr<DisplayDriver> driver);
/* Perform drawing of the current state of the GPUDisplay. */
/* Clear the display buffer by filling it in with all zeroes. */
void clear_display();
/* Perform drawing of the current state of the DisplayDriver. */
void draw();
/* Cancel rendering process as soon as possible, without waiting for full tile to be sampled.
@@ -157,6 +162,7 @@ class PathTrace {
* instead. */
int2 get_render_tile_size() const;
int2 get_render_tile_offset() const;
int2 get_render_size() const;
/* Get buffer parameters of the current tile.
*
@@ -168,18 +174,6 @@ class PathTrace {
* times, and so on. */
string full_report() const;
/* Callback which communicates an updates state of the render buffer of the current big tile.
* Is called during path tracing to communicate work-in-progress state of the final buffer. */
function<void(void)> tile_buffer_update_cb;
/* Callback which communicates final rendered buffer. Is called after path-tracing is done. */
function<void(void)> tile_buffer_write_cb;
/* Callback which initializes rendered buffer. Is called before path-tracing starts.
*
* This is used for baking. */
function<bool(void)> tile_buffer_read_cb;
/* Callback which is called to report current rendering progress.
*
* It is supposed to be cheaper than buffer update/write, hence can be called more often.
@@ -252,7 +246,11 @@ class PathTrace {
RenderScheduler &render_scheduler_;
TileManager &tile_manager_;
unique_ptr<GPUDisplay> gpu_display_;
/* Display driver for interactive render buffer display. */
unique_ptr<PathTraceDisplay> display_;
/* Output driver to write render buffer to. */
unique_ptr<OutputDriver> output_driver_;
/* Per-compute device descriptors of work which is responsible for path tracing on its configured
* device. */

View File

@@ -14,20 +14,25 @@
* limitations under the License.
*/
#include "render/gpu_display.h"
#include "integrator/path_trace_display.h"
#include "render/buffers.h"
#include "util/util_logging.h"
CCL_NAMESPACE_BEGIN
void GPUDisplay::reset(const BufferParams &buffer_params)
PathTraceDisplay::PathTraceDisplay(unique_ptr<DisplayDriver> driver) : driver_(move(driver))
{
}
void PathTraceDisplay::reset(const BufferParams &buffer_params)
{
thread_scoped_lock lock(mutex_);
const GPUDisplayParams old_params = params_;
const DisplayDriver::Params old_params = params_;
params_.offset = make_int2(buffer_params.full_x, buffer_params.full_y);
params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y);
params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height);
params_.size = make_int2(buffer_params.width, buffer_params.height);
@@ -44,7 +49,7 @@ void GPUDisplay::reset(const BufferParams &buffer_params)
texture_state_.is_outdated = true;
}
void GPUDisplay::mark_texture_updated()
void PathTraceDisplay::mark_texture_updated()
{
texture_state_.is_outdated = false;
texture_state_.is_usable = true;
@@ -54,7 +59,7 @@ void GPUDisplay::mark_texture_updated()
* Update procedure.
*/
bool GPUDisplay::update_begin(int texture_width, int texture_height)
bool PathTraceDisplay::update_begin(int texture_width, int texture_height)
{
DCHECK(!update_state_.is_active);
@@ -66,15 +71,15 @@ bool GPUDisplay::update_begin(int texture_width, int texture_height)
/* Get parameters within a mutex lock, to avoid reset() modifying them at the same time.
* The update itself is non-blocking however, for better performance and to avoid
* potential deadlocks due to locks held by the subclass. */
GPUDisplayParams params;
DisplayDriver::Params params;
{
thread_scoped_lock lock(mutex_);
params = params_;
texture_state_.size = make_int2(texture_width, texture_height);
}
if (!do_update_begin(params, texture_width, texture_height)) {
LOG(ERROR) << "GPUDisplay implementation could not begin update.";
if (!driver_->update_begin(params, texture_width, texture_height)) {
LOG(ERROR) << "PathTraceDisplay implementation could not begin update.";
return false;
}
@@ -83,7 +88,7 @@ bool GPUDisplay::update_begin(int texture_width, int texture_height)
return true;
}
void GPUDisplay::update_end()
void PathTraceDisplay::update_end()
{
DCHECK(update_state_.is_active);
@@ -92,12 +97,12 @@ void GPUDisplay::update_end()
return;
}
do_update_end();
driver_->update_end();
update_state_.is_active = false;
}
int2 GPUDisplay::get_texture_size() const
int2 PathTraceDisplay::get_texture_size() const
{
return texture_state_.size;
}
@@ -106,25 +111,54 @@ int2 GPUDisplay::get_texture_size() const
* Texture update from CPU buffer.
*/
void GPUDisplay::copy_pixels_to_texture(
void PathTraceDisplay::copy_pixels_to_texture(
const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height)
{
DCHECK(update_state_.is_active);
if (!update_state_.is_active) {
LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update.";
LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update.";
return;
}
mark_texture_updated();
do_copy_pixels_to_texture(rgba_pixels, texture_x, texture_y, pixels_width, pixels_height);
/* This call copies pixels to a mapped texture buffer which is typically much cheaper from CPU
* time point of view than to copy data directly to a texture.
*
* The possible downside of this approach is that it might require a higher peak memory when
* doing partial updates of the texture (although, in practice even partial updates might peak
* with a full-frame buffer stored on the CPU if the GPU is currently occupied). */
half4 *mapped_rgba_pixels = map_texture_buffer();
if (!mapped_rgba_pixels) {
return;
}
const int texture_width = texture_state_.size.x;
const int texture_height = texture_state_.size.y;
if (texture_x == 0 && texture_y == 0 && pixels_width == texture_width &&
pixels_height == texture_height) {
const size_t size_in_bytes = sizeof(half4) * texture_width * texture_height;
memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes);
}
else {
const half4 *rgba_row = rgba_pixels;
half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_width + texture_x;
for (int y = 0; y < pixels_height;
++y, rgba_row += pixels_width, mapped_rgba_row += texture_width) {
memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width);
}
}
unmap_texture_buffer();
}
/* --------------------------------------------------------------------
* Texture buffer mapping.
*/
half4 *GPUDisplay::map_texture_buffer()
half4 *PathTraceDisplay::map_texture_buffer()
{
DCHECK(!texture_buffer_state_.is_mapped);
DCHECK(update_state_.is_active);
@@ -135,11 +169,11 @@ half4 *GPUDisplay::map_texture_buffer()
}
if (!update_state_.is_active) {
LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update.";
LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update.";
return nullptr;
}
half4 *mapped_rgba_pixels = do_map_texture_buffer();
half4 *mapped_rgba_pixels = driver_->map_texture_buffer();
if (mapped_rgba_pixels) {
texture_buffer_state_.is_mapped = true;
@@ -148,7 +182,7 @@ half4 *GPUDisplay::map_texture_buffer()
return mapped_rgba_pixels;
}
void GPUDisplay::unmap_texture_buffer()
void PathTraceDisplay::unmap_texture_buffer()
{
DCHECK(texture_buffer_state_.is_mapped);
@@ -160,14 +194,14 @@ void GPUDisplay::unmap_texture_buffer()
texture_buffer_state_.is_mapped = false;
mark_texture_updated();
do_unmap_texture_buffer();
driver_->unmap_texture_buffer();
}
/* --------------------------------------------------------------------
* Graphics interoperability.
*/
DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get()
DisplayDriver::GraphicsInterop PathTraceDisplay::graphics_interop_get()
{
DCHECK(!texture_buffer_state_.is_mapped);
DCHECK(update_state_.is_active);
@@ -175,38 +209,45 @@ DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get()
if (texture_buffer_state_.is_mapped) {
LOG(ERROR)
<< "Attempt to use graphics interoperability mode while the texture buffer is mapped.";
return DeviceGraphicsInteropDestination();
return DisplayDriver::GraphicsInterop();
}
if (!update_state_.is_active) {
LOG(ERROR) << "Attempt to use graphics interoperability outside of GPUDisplay update.";
return DeviceGraphicsInteropDestination();
LOG(ERROR) << "Attempt to use graphics interoperability outside of PathTraceDisplay update.";
return DisplayDriver::GraphicsInterop();
}
/* Assume that interop will write new values to the texture. */
mark_texture_updated();
return do_graphics_interop_get();
return driver_->graphics_interop_get();
}
void GPUDisplay::graphics_interop_activate()
void PathTraceDisplay::graphics_interop_activate()
{
driver_->graphics_interop_activate();
}
void GPUDisplay::graphics_interop_deactivate()
void PathTraceDisplay::graphics_interop_deactivate()
{
driver_->graphics_interop_deactivate();
}
/* --------------------------------------------------------------------
* Drawing.
*/
bool GPUDisplay::draw()
void PathTraceDisplay::clear()
{
driver_->clear();
}
bool PathTraceDisplay::draw()
{
/* Get parameters within a mutex lock, to avoid reset() modifying them at the same time.
* The drawing itself is non-blocking however, for better performance and to avoid
* potential deadlocks due to locks held by the subclass. */
GPUDisplayParams params;
DisplayDriver::Params params;
bool is_usable;
bool is_outdated;
@@ -218,7 +259,7 @@ bool GPUDisplay::draw()
}
if (is_usable) {
do_draw(params);
driver_->draw(params);
}
return !is_outdated;

View File

@@ -16,52 +16,30 @@
#pragma once
#include "device/device_graphics_interop.h"
#include "render/display_driver.h"
#include "util/util_half.h"
#include "util/util_thread.h"
#include "util/util_types.h"
#include "util/util_unique_ptr.h"
CCL_NAMESPACE_BEGIN
class BufferParams;
/* GPUDisplay class takes care of drawing render result in a viewport. The render result is stored
* in a GPU-side texture, which is updated from a path tracer and drawn by an application.
/* PathTraceDisplay is used for efficient render buffer display.
*
* The base GPUDisplay does some special texture state tracking, which allows render Session to
* make decisions on whether reset for an updated state is possible or not. This state should only
* be tracked in a base class and a particular implementation should not worry about it.
* The host applications implements a DisplayDriver, storing a render pass in a GPU-side
* textures. This texture is continuously updated by the path tracer and drawn by the host
* application.
*
* The subclasses should only implement the pure virtual methods, which allows them to not worry
* about parent method calls, which helps them to be as small and reliable as possible. */
* PathTraceDisplay is a wrapper around the DisplayDriver, adding thread safety, state tracking
* and error checking. */
class GPUDisplayParams {
class PathTraceDisplay {
public:
/* Offset of the display within a viewport.
* For example, set to a lower-bottom corner of border render in Blender's viewport. */
int2 offset = make_int2(0, 0);
/* Full viewport size.
*
* NOTE: Is not affected by the resolution divider. */
int2 full_size = make_int2(0, 0);
/* Effective viewport size.
* In the case of border render, size of the border rectangle.
*
* NOTE: Is not affected by the resolution divider. */
int2 size = make_int2(0, 0);
bool modified(const GPUDisplayParams &other) const
{
return !(offset == other.offset && full_size == other.full_size && size == other.size);
}
};
class GPUDisplay {
public:
GPUDisplay() = default;
virtual ~GPUDisplay() = default;
PathTraceDisplay(unique_ptr<DisplayDriver> driver);
virtual ~PathTraceDisplay() = default;
/* Reset the display for the new state of render session. Is called whenever session is reset,
* which happens on changes like viewport navigation or viewport dimension change.
@@ -69,11 +47,6 @@ class GPUDisplay {
* This call will configure parameters for a changed buffer and reset the texture state. */
void reset(const BufferParams &buffer_params);
const GPUDisplayParams &get_params() const
{
return params_;
}
/* --------------------------------------------------------------------
* Update procedure.
*
@@ -94,7 +67,8 @@ class GPUDisplay {
/* --------------------------------------------------------------------
* Texture update from CPU buffer.
*
* NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`.
* NOTE: The PathTraceDisplay should be marked for an update being in process with
* `update_begin()`.
*
* Most portable implementation, which must be supported by all platforms. Might not be the most
* efficient one.
@@ -115,7 +89,8 @@ class GPUDisplay {
* This functionality is used to update GPU-side texture content without need to maintain CPU
* side buffer on the caller.
*
* NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`.
* NOTE: The PathTraceDisplay should be marked for an update being in process with
* `update_begin()`.
*
* NOTE: Texture buffer can not be mapped while graphics interoperability is active. This means
* that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and
@@ -145,14 +120,14 @@ class GPUDisplay {
* that `graphics_interop_get()` is not allowed between `map_texture_buffer()` and
* `unmap_texture_buffer()` calls. */
/* Get GPUDisplay graphics interoperability information which acts as a destination for the
/* Get PathTraceDisplay graphics interoperability information which acts as a destination for the
* device API. */
DeviceGraphicsInteropDestination graphics_interop_get();
DisplayDriver::GraphicsInterop graphics_interop_get();
/* (De)activate GPU display for graphics interoperability outside of regular display update
* routines. */
virtual void graphics_interop_activate();
virtual void graphics_interop_deactivate();
void graphics_interop_activate();
void graphics_interop_deactivate();
/* --------------------------------------------------------------------
* Drawing.
@@ -168,42 +143,21 @@ class GPUDisplay {
* after clear will write new pixel values for an updating area, leaving everything else zeroed.
*
* If the GPU display supports graphics interoperability then the zeroing the display is to be
* delegated to the device via the `DeviceGraphicsInteropDestination`. */
virtual void clear() = 0;
* delegated to the device via the `DisplayDriver::GraphicsInterop`. */
void clear();
/* Draw the current state of the texture.
*
* Returns true if this call did draw an updated state of the texture. */
bool draw();
protected:
/* Implementation-specific calls which subclasses are to implement.
* These `do_foo()` method corresponds to their `foo()` calls, but they are purely virtual to
* simplify their particular implementation. */
virtual bool do_update_begin(const GPUDisplayParams &params,
int texture_width,
int texture_height) = 0;
virtual void do_update_end() = 0;
virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels,
int texture_x,
int texture_y,
int pixels_width,
int pixels_height) = 0;
virtual half4 *do_map_texture_buffer() = 0;
virtual void do_unmap_texture_buffer() = 0;
/* Note that this might be called in parallel to do_update_begin() and do_update_end(),
* the subclass is responsible for appropriate mutex locks to avoid multiple threads
* editing and drawing the texture at the same time. */
virtual void do_draw(const GPUDisplayParams &params) = 0;
virtual DeviceGraphicsInteropDestination do_graphics_interop_get() = 0;
private:
/* Display driver implemented by the host application. */
unique_ptr<DisplayDriver> driver_;
/* Current display parameters */
thread_mutex mutex_;
GPUDisplayParams params_;
DisplayDriver::Params params_;
/* Mark texture as its content has been updated.
* Used from places which knows that the texture content has been brought up-to-date, so that the

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2021 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.
*/
#include "integrator/path_trace_tile.h"
#include "integrator/pass_accessor_cpu.h"
#include "integrator/path_trace.h"
#include "render/buffers.h"
#include "render/film.h"
#include "render/pass.h"
#include "render/scene.h"
CCL_NAMESPACE_BEGIN
PathTraceTile::PathTraceTile(PathTrace &path_trace)
: OutputDriver::Tile(path_trace.get_render_tile_offset(),
path_trace.get_render_tile_size(),
path_trace.get_render_size(),
path_trace.get_render_tile_params().layer,
path_trace.get_render_tile_params().view),
path_trace_(path_trace),
copied_from_device_(false)
{
}
bool PathTraceTile::get_pass_pixels(const string_view pass_name,
const int num_channels,
float *pixels) const
{
/* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
* is happening while this function runs. */
if (!copied_from_device_) {
/* Copy from device on demand. */
path_trace_.copy_render_tile_from_device();
const_cast<PathTraceTile *>(this)->copied_from_device_ = true;
}
const BufferParams &buffer_params = path_trace_.get_render_tile_params();
const BufferPass *pass = buffer_params.find_pass(pass_name);
if (pass == nullptr) {
return false;
}
const bool has_denoised_result = path_trace_.has_denoised_result();
if (pass->mode == PassMode::DENOISED && !has_denoised_result) {
pass = buffer_params.find_pass(pass->type);
if (pass == nullptr) {
/* Happens when denoised result pass is requested but is never written by the kernel. */
return false;
}
}
pass = buffer_params.get_actual_display_pass(pass);
const float exposure = buffer_params.exposure;
const int num_samples = path_trace_.get_num_render_tile_samples();
PassAccessor::PassAccessInfo pass_access_info(*pass);
pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher;
pass_access_info.use_approximate_shadow_catcher_background =
pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background;
const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
const PassAccessor::Destination destination(pixels, num_channels);
return path_trace_.get_render_tile_pixels(pass_accessor, destination);
}
bool PathTraceTile::set_pass_pixels(const string_view pass_name,
const int num_channels,
const float *pixels) const
{
/* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
* is happening while this function runs. */
const BufferParams &buffer_params = path_trace_.get_render_tile_params();
const BufferPass *pass = buffer_params.find_pass(pass_name);
if (!pass) {
return false;
}
const float exposure = buffer_params.exposure;
const int num_samples = 1;
const PassAccessor::PassAccessInfo pass_access_info(*pass);
PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
PassAccessor::Source source(pixels, num_channels);
return path_trace_.set_render_tile_pixels(pass_accessor, source);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2021 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
#include "render/output_driver.h"
CCL_NAMESPACE_BEGIN
/* PathTraceTile
*
* Implementation of OutputDriver::Tile interface for path tracer. */
class PathTrace;
class PathTraceTile : public OutputDriver::Tile {
public:
PathTraceTile(PathTrace &path_trace);
bool get_pass_pixels(const string_view pass_name, const int num_channels, float *pixels) const;
bool set_pass_pixels(const string_view pass_name,
const int num_channels,
const float *pixels) const;
private:
PathTrace &path_trace_;
bool copied_from_device_;
};
CCL_NAMESPACE_END

View File

@@ -16,12 +16,12 @@
#include "device/device.h"
#include "integrator/path_trace_display.h"
#include "integrator/path_trace_work.h"
#include "integrator/path_trace_work_cpu.h"
#include "integrator/path_trace_work_gpu.h"
#include "render/buffers.h"
#include "render/film.h"
#include "render/gpu_display.h"
#include "render/scene.h"
#include "kernel/kernel_types.h"
@@ -185,12 +185,12 @@ PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMod
return pass_access_info;
}
PassAccessor::Destination PathTraceWork::get_gpu_display_destination_template(
const GPUDisplay *gpu_display) const
PassAccessor::Destination PathTraceWork::get_display_destination_template(
const PathTraceDisplay *display) const
{
PassAccessor::Destination destination(film_->get_display_pass());
const int2 display_texture_size = gpu_display->get_texture_size();
const int2 display_texture_size = display->get_texture_size();
const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x;
const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y;

View File

@@ -28,7 +28,7 @@ class BufferParams;
class Device;
class DeviceScene;
class Film;
class GPUDisplay;
class PathTraceDisplay;
class RenderBuffers;
class PathTraceWork {
@@ -83,11 +83,9 @@ class PathTraceWork {
* noisy pass mode will be passed here when it is known that the buffer does not have denoised
* passes yet (because denoiser did not run). If the denoised pass is requested and denoiser is
* not used then this function will fall-back to the noisy pass instead. */
virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples) = 0;
virtual void copy_to_display(PathTraceDisplay *display, PassMode pass_mode, int num_samples) = 0;
virtual void destroy_gpu_resources(GPUDisplay *gpu_display) = 0;
virtual void destroy_gpu_resources(PathTraceDisplay *display) = 0;
/* Copy data from/to given render buffers.
* Will copy pixels from a corresponding place (from multi-device point of view) of the render
@@ -162,8 +160,8 @@ class PathTraceWork {
/* Get destination which offset and stride are configured so that writing to it will write to a
* proper location of GPU display texture, taking current tile and device slice into account. */
PassAccessor::Destination get_gpu_display_destination_template(
const GPUDisplay *gpu_display) const;
PassAccessor::Destination get_display_destination_template(
const PathTraceDisplay *display) const;
/* Device which will be used for path tracing.
* Note that it is an actual render device (and never is a multi-device). */

View File

@@ -22,9 +22,9 @@
#include "kernel/kernel_path_state.h"
#include "integrator/pass_accessor_cpu.h"
#include "integrator/path_trace_display.h"
#include "render/buffers.h"
#include "render/gpu_display.h"
#include "render/scene.h"
#include "util/util_atomic.h"
@@ -161,14 +161,14 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global
}
}
void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples)
void PathTraceWorkCPU::copy_to_display(PathTraceDisplay *display,
PassMode pass_mode,
int num_samples)
{
half4 *rgba_half = gpu_display->map_texture_buffer();
half4 *rgba_half = display->map_texture_buffer();
if (!rgba_half) {
/* TODO(sergey): Look into using copy_to_gpu_display() if mapping failed. Might be needed for
* some implementations of GPUDisplay which can not map memory? */
/* TODO(sergey): Look into using copy_to_display() if mapping failed. Might be needed for
* some implementations of PathTraceDisplay which can not map memory? */
return;
}
@@ -178,7 +178,7 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
const PassAccessorCPU pass_accessor(pass_access_info, kfilm.exposure, num_samples);
PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display);
PassAccessor::Destination destination = get_display_destination_template(display);
destination.pixels_half_rgba = rgba_half;
tbb::task_arena local_arena = local_tbb_arena_create(device_);
@@ -186,10 +186,10 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination);
});
gpu_display->unmap_texture_buffer();
display->unmap_texture_buffer();
}
void PathTraceWorkCPU::destroy_gpu_resources(GPUDisplay * /*gpu_display*/)
void PathTraceWorkCPU::destroy_gpu_resources(PathTraceDisplay * /*display*/)
{
}

View File

@@ -50,10 +50,10 @@ class PathTraceWorkCPU : public PathTraceWork {
int start_sample,
int samples_num) override;
virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples) override;
virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override;
virtual void copy_to_display(PathTraceDisplay *display,
PassMode pass_mode,
int num_samples) override;
virtual void destroy_gpu_resources(PathTraceDisplay *display) override;
virtual bool copy_render_buffers_from_device() override;
virtual bool copy_render_buffers_to_device() override;

View File

@@ -15,12 +15,12 @@
*/
#include "integrator/path_trace_work_gpu.h"
#include "integrator/path_trace_display.h"
#include "device/device.h"
#include "integrator/pass_accessor_gpu.h"
#include "render/buffers.h"
#include "render/gpu_display.h"
#include "render/scene.h"
#include "util/util_logging.h"
#include "util/util_tbb.h"
@@ -46,7 +46,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device,
queued_paths_(device, "queued_paths", MEM_READ_WRITE),
num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE),
work_tiles_(device, "work_tiles", MEM_READ_WRITE),
gpu_display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
max_num_paths_(queue_->num_concurrent_states(sizeof(IntegratorStateCPU))),
min_num_active_paths_(queue_->num_concurrent_busy_states()),
max_active_path_index_(0)
@@ -652,7 +652,7 @@ int PathTraceWorkGPU::get_num_active_paths()
bool PathTraceWorkGPU::should_use_graphics_interop()
{
/* There are few aspects with the graphics interop when using multiple devices caused by the fact
* that the GPUDisplay has a single texture:
* that the PathTraceDisplay has a single texture:
*
* CUDA will return `CUDA_ERROR_NOT_SUPPORTED` from `cuGraphicsGLRegisterBuffer()` when
* attempting to register OpenGL PBO which has been mapped. Which makes sense, because
@@ -678,9 +678,9 @@ bool PathTraceWorkGPU::should_use_graphics_interop()
return interop_use_;
}
void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples)
void PathTraceWorkGPU::copy_to_display(PathTraceDisplay *display,
PassMode pass_mode,
int num_samples)
{
if (device_->have_error()) {
/* Don't attempt to update GPU display if the device has errors: the error state will make
@@ -694,7 +694,7 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
}
if (should_use_graphics_interop()) {
if (copy_to_gpu_display_interop(gpu_display, pass_mode, num_samples)) {
if (copy_to_display_interop(display, pass_mode, num_samples)) {
return;
}
@@ -703,12 +703,12 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
interop_use_ = false;
}
copy_to_gpu_display_naive(gpu_display, pass_mode, num_samples);
copy_to_display_naive(display, pass_mode, num_samples);
}
void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples)
void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display,
PassMode pass_mode,
int num_samples)
{
const int full_x = effective_buffer_params_.full_x;
const int full_y = effective_buffer_params_.full_y;
@@ -725,44 +725,42 @@ void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display,
* NOTE: allocation happens to the final resolution so that no re-allocation happens on every
* change of the resolution divider. However, if the display becomes smaller, shrink the
* allocated memory as well. */
if (gpu_display_rgba_half_.data_width != final_width ||
gpu_display_rgba_half_.data_height != final_height) {
gpu_display_rgba_half_.alloc(final_width, final_height);
if (display_rgba_half_.data_width != final_width ||
display_rgba_half_.data_height != final_height) {
display_rgba_half_.alloc(final_width, final_height);
/* TODO(sergey): There should be a way to make sure device-side memory is allocated without
* transferring zeroes to the device. */
queue_->zero_to_device(gpu_display_rgba_half_);
queue_->zero_to_device(display_rgba_half_);
}
PassAccessor::Destination destination(film_->get_display_pass());
destination.d_pixels_half_rgba = gpu_display_rgba_half_.device_pointer;
destination.d_pixels_half_rgba = display_rgba_half_.device_pointer;
get_render_tile_film_pixels(destination, pass_mode, num_samples);
queue_->copy_from_device(gpu_display_rgba_half_);
queue_->copy_from_device(display_rgba_half_);
queue_->synchronize();
gpu_display->copy_pixels_to_texture(
gpu_display_rgba_half_.data(), texture_x, texture_y, width, height);
display->copy_pixels_to_texture(display_rgba_half_.data(), texture_x, texture_y, width, height);
}
bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples)
bool PathTraceWorkGPU::copy_to_display_interop(PathTraceDisplay *display,
PassMode pass_mode,
int num_samples)
{
if (!device_graphics_interop_) {
device_graphics_interop_ = queue_->graphics_interop_create();
}
const DeviceGraphicsInteropDestination graphics_interop_dst =
gpu_display->graphics_interop_get();
device_graphics_interop_->set_destination(graphics_interop_dst);
const DisplayDriver::GraphicsInterop graphics_interop_dst = display->graphics_interop_get();
device_graphics_interop_->set_display_interop(graphics_interop_dst);
const device_ptr d_rgba_half = device_graphics_interop_->map();
if (!d_rgba_half) {
return false;
}
PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display);
PassAccessor::Destination destination = get_display_destination_template(display);
destination.d_pixels_half_rgba = d_rgba_half;
get_render_tile_film_pixels(destination, pass_mode, num_samples);
@@ -772,14 +770,14 @@ bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display,
return true;
}
void PathTraceWorkGPU::destroy_gpu_resources(GPUDisplay *gpu_display)
void PathTraceWorkGPU::destroy_gpu_resources(PathTraceDisplay *display)
{
if (!device_graphics_interop_) {
return;
}
gpu_display->graphics_interop_activate();
display->graphics_interop_activate();
device_graphics_interop_ = nullptr;
gpu_display->graphics_interop_deactivate();
display->graphics_interop_deactivate();
}
void PathTraceWorkGPU::get_render_tile_film_pixels(const PassAccessor::Destination &destination,

View File

@@ -48,10 +48,10 @@ class PathTraceWorkGPU : public PathTraceWork {
int start_sample,
int samples_num) override;
virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
PassMode pass_mode,
int num_samples) override;
virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override;
virtual void copy_to_display(PathTraceDisplay *display,
PassMode pass_mode,
int num_samples) override;
virtual void destroy_gpu_resources(PathTraceDisplay *display) override;
virtual bool copy_render_buffers_from_device() override;
virtual bool copy_render_buffers_to_device() override;
@@ -88,16 +88,16 @@ class PathTraceWorkGPU : public PathTraceWork {
int get_num_active_paths();
/* Check whether graphics interop can be used for the GPUDisplay update. */
/* Check whether graphics interop can be used for the PathTraceDisplay update. */
bool should_use_graphics_interop();
/* Naive implementation of the `copy_to_gpu_display()` which performs film conversion on the
* device, then copies pixels to the host and pushes them to the `gpu_display`. */
void copy_to_gpu_display_naive(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples);
/* Naive implementation of the `copy_to_display()` which performs film conversion on the
* device, then copies pixels to the host and pushes them to the `display`. */
void copy_to_display_naive(PathTraceDisplay *display, PassMode pass_mode, int num_samples);
/* Implementation of `copy_to_gpu_display()` which uses driver's OpenGL/GPU interoperability
/* Implementation of `copy_to_display()` which uses driver's OpenGL/GPU interoperability
* functionality, avoiding copy of pixels to the host. */
bool copy_to_gpu_display_interop(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples);
bool copy_to_display_interop(PathTraceDisplay *display, PassMode pass_mode, int num_samples);
/* Synchronously run film conversion kernel and store display result in the given destination. */
void get_render_tile_film_pixels(const PassAccessor::Destination &destination,
@@ -139,9 +139,9 @@ class PathTraceWorkGPU : public PathTraceWork {
/* Temporary buffer for passing work tiles to kernel. */
device_vector<KernelWorkTile> work_tiles_;
/* Temporary buffer used by the copy_to_gpu_display() whenever graphics interoperability is not
/* Temporary buffer used by the copy_to_display() whenever graphics interoperability is not
* available. Is allocated on-demand. */
device_vector<half4> gpu_display_rgba_half_;
device_vector<half4> display_rgba_half_;
unique_ptr<DeviceGraphicsInterop> device_graphics_interop_;

View File

@@ -344,7 +344,7 @@ class RenderScheduler {
/* Number of rendered samples on top of the start sample. */
int num_rendered_samples = 0;
/* Point in time the latest GPUDisplay work has been scheduled. */
/* Point in time the latest PathTraceDisplay work has been scheduled. */
double last_display_update_time = 0.0;
/* Value of -1 means display was never updated. */
int last_display_update_sample = -1;

View File

@@ -103,7 +103,7 @@ ccl_device_inline void shader_setup_from_ray(const KernelGlobals *ccl_restrict k
sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags;
if (isect->object != OBJECT_NONE) {
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
/* instance transform */
object_normal_transform_auto(kg, sd, &sd->N);
object_normal_transform_auto(kg, sd, &sd->Ng);

View File

@@ -109,9 +109,17 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS,
}
/* Position and normal on triangle. */
const int object = kernel_data.bake.object_index;
float3 P, Ng;
int shader;
triangle_point_normal(kg, kernel_data.bake.object_index, prim, u, v, &P, &Ng, &shader);
triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader);
const int object_flag = kernel_tex_fetch(__object_flag, object);
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
P = transform_point_auto(&tfm, P);
}
if (kernel_data.film.pass_background != PASS_UNUSED) {
/* Environment baking. */
@@ -130,8 +138,13 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS,
}
else {
/* Surface baking. */
const float3 N = (shader & SHADER_SMOOTH_NORMAL) ? triangle_smooth_normal(kg, Ng, prim, u, v) :
Ng;
float3 N = (shader & SHADER_SMOOTH_NORMAL) ? triangle_smooth_normal(kg, Ng, prim, u, v) : Ng;
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
N = normalize(transform_direction_transposed(&itfm, N));
Ng = normalize(transform_direction_transposed(&itfm, Ng));
}
/* Setup ray. */
Ray ray ccl_optional_struct_init;
@@ -143,6 +156,12 @@ ccl_device bool integrator_init_from_bake(INTEGRATOR_STATE_ARGS,
/* Setup differentials. */
float3 dPdu, dPdv;
triangle_dPdudv(kg, prim, &dPdu, &dPdv);
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
dPdu = transform_direction(&tfm, dPdu);
dPdv = transform_direction(&tfm, dPdv);
}
differential3 dP;
dP.dx = dPdu * dudx + dPdv * dvdx;
dP.dy = dPdu * dudy + dPdv * dvdy;

View File

@@ -123,7 +123,7 @@ ccl_device_forceinline void integrator_intersect_shader_next_kernel(
#ifdef __SHADOW_CATCHER__
const int object_flags = intersection_get_object_flags(kg, isect);
if (kernel_shadow_catcher_split(INTEGRATOR_STATE_PASS, object_flags)) {
if (kernel_data.film.use_approximate_shadow_catcher && !kernel_data.background.transparent) {
if (kernel_data.film.pass_background != PASS_UNUSED && !kernel_data.background.transparent) {
INTEGRATOR_STATE_WRITE(path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND;
if (use_raytrace_kernel) {

View File

@@ -186,8 +186,8 @@ ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg,
float sum_sample_weight,
const uint light_shader_flags)
{
/* this is the veach one-sample model with balance heuristic, some pdf
* factors drop out when using balance heuristic weighting */
/* This is the veach one-sample model with balance heuristic,
* some PDF factors drop out when using balance heuristic weighting. */
for (int i = 0; i < sd->num_closure; i++) {
const ShaderClosure *sc = &sd->closure[i];

View File

@@ -360,7 +360,6 @@ typedef enum PassType {
PASS_MATERIAL_ID,
PASS_MOTION,
PASS_MOTION_WEIGHT,
PASS_RENDER_TIME,
PASS_CRYPTOMATTE,
PASS_AOV_COLOR,
PASS_AOV_VALUE,

View File

@@ -110,6 +110,7 @@ ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
ustring OSLRenderServices::u_curve_length("geom:curve_length");
ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
ustring OSLRenderServices::u_curve_random("geom:curve_random");
ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal");
ustring OSLRenderServices::u_path_ray_length("path:ray_length");
ustring OSLRenderServices::u_path_ray_depth("path:ray_depth");
ustring OSLRenderServices::u_path_diffuse_depth("path:diffuse_depth");
@@ -985,8 +986,18 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobals *kg,
float3 f = curve_tangent_normal(kg, sd);
return set_attribute_float3(f, type, derivatives, val);
}
else
else if (name == u_normal_map_normal) {
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v);
return set_attribute_float3(f, type, derivatives, val);
}
else {
return false;
}
}
else {
return false;
}
}
bool OSLRenderServices::get_background_attribute(const KernelGlobals *kg,

View File

@@ -297,6 +297,7 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_curve_length;
static ustring u_curve_tangent_normal;
static ustring u_curve_random;
static ustring u_normal_map_normal;
static ustring u_path_ray_length;
static ustring u_path_ray_depth;
static ustring u_path_diffuse_depth;

View File

@@ -41,6 +41,7 @@ set(SRC_OSL
node_vector_displacement.osl
node_emission.osl
node_environment_texture.osl
node_float_curve.osl
node_fresnel.osl
node_gamma.osl
node_geometry.osl

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2011-2021 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.
*/
#include "node_ramp_util.h"
#include "stdcycles.h"
shader node_float_curve(float ramp[] = {0.0},
float min_x = 0.0,
float max_x = 1.0,
float ValueIn = 0.0,
float Factor = 0.0,
output float ValueOut = 0.0)
{
float c = (ValueIn - min_x) / (max_x - min_x);
ValueOut = rgb_ramp_lookup(ramp, c, 1, 1);
ValueOut = mix(ValueIn, ValueOut, Factor);
}

View File

@@ -45,7 +45,7 @@ shader node_normal_map(normal NormalIn = N,
// get _unnormalized_ interpolated normal and tangent
if (getattribute(attr_name, tangent) && getattribute(attr_sign_name, tangent_sign) &&
(!is_smooth || getattribute("geom:N", ninterp))) {
(!is_smooth || getattribute("geom:normal_map_normal", ninterp))) {
// apply normal map
vector B = tangent_sign * cross(ninterp, tangent);
Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp);

View File

@@ -493,11 +493,13 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS,
case NODE_IES:
svm_node_ies(kg, sd, stack, node);
break;
case NODE_RGB_CURVES:
case NODE_VECTOR_CURVES:
offset = svm_node_curves(kg, sd, stack, node, offset);
break;
case NODE_FLOAT_CURVE:
offset = svm_node_curve(kg, sd, stack, node, offset);
break;
case NODE_TANGENT:
svm_node_tangent(kg, sd, stack, node);
break;

View File

@@ -21,6 +21,48 @@ CCL_NAMESPACE_BEGIN
/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
ccl_device_inline float fetch_float(const KernelGlobals *kg, int offset)
{
uint4 node = kernel_tex_fetch(__svm_nodes, offset);
return __uint_as_float(node.x);
}
ccl_device_inline float float_ramp_lookup(const KernelGlobals *kg,
int offset,
float f,
bool interpolate,
bool extrapolate,
int table_size)
{
if ((f < 0.0f || f > 1.0f) && extrapolate) {
float t0, dy;
if (f < 0.0f) {
t0 = fetch_float(kg, offset);
dy = t0 - fetch_float(kg, offset + 1);
f = -f;
}
else {
t0 = fetch_float(kg, offset + table_size - 1);
dy = t0 - fetch_float(kg, offset + table_size - 2);
f = f - 1.0f;
}
return t0 + dy * f * (table_size - 1);
}
f = saturate(f) * (table_size - 1);
/* clamp int as well in case of NaN */
int i = clamp(float_to_int(f), 0, table_size - 1);
float t = f - (float)i;
float a = fetch_float(kg, offset + i);
if (interpolate && t > 0.0f)
a = (1.0f - t) * a + t * fetch_float(kg, offset + i + 1);
return a;
}
ccl_device_inline float4 rgb_ramp_lookup(const KernelGlobals *kg,
int offset,
float f,
@@ -105,6 +147,30 @@ ccl_device_noinline int svm_node_curves(
return offset;
}
ccl_device_noinline int svm_node_curve(
const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset)
{
uint fac_offset, value_in_offset, out_offset;
svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset);
uint table_size = read_node(kg, &offset).x;
float fac = stack_load_float(stack, fac_offset);
float in = stack_load_float(stack, value_in_offset);
const float min = __int_as_float(node.z), max = __int_as_float(node.w);
const float range = max - min;
const float relpos = (in - min) / range;
float v = float_ramp_lookup(kg, offset, relpos, true, true, table_size);
in = (1.0f - fac) * in + fac * v;
stack_store_float(stack, out_offset, in);
offset += table_size;
return offset;
}
CCL_NAMESPACE_END
#endif /* __SVM_RAMP_H__ */

View File

@@ -122,6 +122,7 @@ typedef enum ShaderNodeType {
NODE_AOV_START,
NODE_AOV_COLOR,
NODE_AOV_VALUE,
NODE_FLOAT_CURVE,
/* NOTE: for best OpenCL performance, item definition in the enum must
* match the switch case order in svm.h. */
} ShaderNodeType;

View File

@@ -35,7 +35,6 @@ set(SRC
denoising.cpp
film.cpp
geometry.cpp
gpu_display.cpp
graph.cpp
hair.cpp
image.cpp
@@ -78,9 +77,10 @@ set(SRC_HEADERS
colorspace.h
constant_fold.h
denoising.h
display_driver.h
output_driver.h
film.h
geometry.h
gpu_display.h
graph.h
hair.h
image.h

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2021 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
#include "util/util_half.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
/* Display driver for efficient interactive display of renders.
*
* Host applications implement this interface for viewport rendering. For best performance, we
* recommend:
* - Allocating a texture on the GPU to be interactively updated
* - Using the graphics interop mechanism to avoid CPU-GPU copying overhead
* - Using a dedicated or thread-safe graphics API context for updates, to avoid
* blocking the host application.
*/
class DisplayDriver {
public:
DisplayDriver() = default;
virtual ~DisplayDriver() = default;
/* Render buffer parameters. */
struct Params {
public:
/* Render resolution, ignoring progressive resolution changes.
* The texture buffer should be allocated with this size. */
int2 size = make_int2(0, 0);
/* For border rendering, the full resolution of the render, and the offset within that larger
* render. */
int2 full_size = make_int2(0, 0);
int2 full_offset = make_int2(0, 0);
bool modified(const Params &other) const
{
return !(full_offset == other.full_offset && full_size == other.full_size &&
size == other.size);
}
};
/* Update the render from the rendering thread.
*
* Cycles periodically updates the render to be displayed. For multithreaded updates with
* potentially multiple rendering devices, it will call these methods as follows.
*
* if (driver.update_begin(params, width, height)) {
* parallel_for_each(rendering_device) {
* buffer = driver.map_texture_buffer();
* if (buffer) {
* fill(buffer);
* driver.unmap_texture_buffer();
* }
* }
* driver.update_end();
* }
*
* The parameters may dynamically change due to camera changes in the scene, and resources should
* be re-allocated accordingly.
*
* The width and height passed to update_begin() are the effective render resolution taking into
* account progressive resolution changes, which may be equal to or smaller than the params.size.
* For efficiency, changes in this resolution should be handled without re-allocating resources,
* but rather by using a subset of the full resolution buffer. */
virtual bool update_begin(const Params &params, int width, int height) = 0;
virtual void update_end() = 0;
virtual half4 *map_texture_buffer() = 0;
virtual void unmap_texture_buffer() = 0;
/* Optionally return a handle to a native graphics API texture buffer. If supported,
* the rendering device may write directly to this buffer instead of calling
* map_texture_buffer() and unmap_texture_buffer(). */
class GraphicsInterop {
public:
/* Dimensions of the buffer, in pixels. */
int buffer_width = 0;
int buffer_height = 0;
/* OpenGL pixel buffer object. */
int opengl_pbo_id = 0;
/* Clear the entire buffer before doing partial write to it. */
bool need_clear = false;
};
virtual GraphicsInterop graphics_interop_get()
{
return GraphicsInterop();
}
/* (De)activate graphics context required for editing or deleting the graphics interop
* object.
*
* For example, destruction of the CUDA object associated with an OpenGL requires the
* OpenGL context to be active. */
virtual void graphics_interop_activate(){};
virtual void graphics_interop_deactivate(){};
/* Clear the display buffer by filling it with zeros. */
virtual void clear() = 0;
/* Draw the render using the native graphics API.
*
* Note that this may be called in parallel to updates. The implementation is responsible for
* mutex locking or other mechanisms to avoid conflicts.
*
* The parameters may have changed since the last update. The implementation is responsible for
* deciding to skip or adjust render display for such changes.
*
* Host application drawing the render buffer should use Session.draw(), which will
* call this method. */
virtual void draw(const Params &params) = 0;
};
CCL_NAMESPACE_END

View File

@@ -326,8 +326,6 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
kfilm->pass_bake_differential = kfilm->pass_stride;
break;
case PASS_RENDER_TIME:
break;
case PASS_CRYPTOMATTE:
kfilm->pass_cryptomatte = have_cryptomatte ?
min(kfilm->pass_cryptomatte, kfilm->pass_stride) :

View File

@@ -6382,7 +6382,7 @@ void BumpNode::constant_fold(const ConstantFolder &folder)
/* TODO(sergey): Ignore bump with zero strength. */
}
/* Curve node */
/* Curves node */
CurvesNode::CurvesNode(const NodeType *node_type) : ShaderNode(node_type)
{
@@ -6531,6 +6531,83 @@ void VectorCurvesNode::compile(OSLCompiler &compiler)
CurvesNode::compile(compiler, "node_vector_curves");
}
/* FloatCurveNode */
NODE_DEFINE(FloatCurveNode)
{
NodeType *type = NodeType::add("float_curve", create, NodeType::SHADER);
SOCKET_FLOAT_ARRAY(curve, "Curve", array<float>());
SOCKET_FLOAT(min_x, "Min X", 0.0f);
SOCKET_FLOAT(max_x, "Max X", 1.0f);
SOCKET_IN_FLOAT(fac, "Factor", 0.0f);
SOCKET_IN_FLOAT(value, "Value", 0.0f);
SOCKET_OUT_FLOAT(value, "Value");
return type;
}
FloatCurveNode::FloatCurveNode() : ShaderNode(get_node_type())
{
}
void FloatCurveNode::constant_fold(const ConstantFolder &folder)
{
ShaderInput *value_in = input("Value");
ShaderInput *fac_in = input("Factor");
/* evaluate fully constant node */
if (folder.all_inputs_constant()) {
if (curve.size() == 0) {
return;
}
float pos = (value - min_x) / (max_x - min_x);
float result = float_ramp_lookup(curve.data(), pos, true, true, curve.size());
folder.make_constant(value + fac * (result - value));
}
/* remove no-op node */
else if (!fac_in->link && fac == 0.0f) {
/* link is not null because otherwise all inputs are constant */
folder.bypass(value_in->link);
}
}
void FloatCurveNode::compile(SVMCompiler &compiler)
{
if (curve.size() == 0)
return;
ShaderInput *value_in = input("Value");
ShaderInput *fac_in = input("Factor");
ShaderOutput *value_out = output("Value");
compiler.add_node(NODE_FLOAT_CURVE,
compiler.encode_uchar4(compiler.stack_assign(fac_in),
compiler.stack_assign(value_in),
compiler.stack_assign(value_out)),
__float_as_int(min_x),
__float_as_int(max_x));
compiler.add_node(curve.size());
for (int i = 0; i < curve.size(); i++)
compiler.add_node(make_float4(curve[i]));
}
void FloatCurveNode::compile(OSLCompiler &compiler)
{
if (curve.size() == 0)
return;
compiler.parameter_array("ramp", curve.data(), curve.size());
compiler.parameter(this, "min_x");
compiler.parameter(this, "max_x");
compiler.add(this, "node_float_curve");
}
/* RGBRampNode */
NODE_DEFINE(RGBRampNode)

View File

@@ -1398,6 +1398,18 @@ class VectorCurvesNode : public CurvesNode {
void constant_fold(const ConstantFolder &folder);
};
class FloatCurveNode : public ShaderNode {
public:
SHADER_NODE_CLASS(FloatCurveNode)
void constant_fold(const ConstantFolder &folder);
NODE_SOCKET_API_ARRAY(array<float>, curve)
NODE_SOCKET_API(float, min_x)
NODE_SOCKET_API(float, max_x)
NODE_SOCKET_API(float, fac)
NODE_SOCKET_API(float, value)
};
class RGBRampNode : public ShaderNode {
public:
SHADER_NODE_CLASS(RGBRampNode)

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2021 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
#include "util/util_math.h"
#include "util/util_string.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
/* Output driver for reading render buffers.
*
* Host applications implement this interface for outputting render buffers for offline rendering.
* Drivers can be used to copy the buffers into the host application or write them directly to
* disk. This interface may also be used for interactive display, however the DisplayDriver is more
* efficient for that purpose.
*/
class OutputDriver {
public:
OutputDriver() = default;
virtual ~OutputDriver() = default;
class Tile {
public:
Tile(const int2 offset,
const int2 size,
const int2 full_size,
const string_view layer,
const string_view view)
: offset(offset), size(size), full_size(full_size), layer(layer), view(view)
{
}
virtual ~Tile() = default;
const int2 offset;
const int2 size;
const int2 full_size;
const string layer;
const string view;
virtual bool get_pass_pixels(const string_view pass_name,
const int num_channels,
float *pixels) const = 0;
virtual bool set_pass_pixels(const string_view pass_name,
const int num_channels,
const float *pixels) const = 0;
};
/* Write tile once it has finished rendering. */
virtual void write_render_tile(const Tile &tile) = 0;
/* Update tile while rendering is in progress. Return true if any update
* was performed. */
virtual bool update_render_tile(const Tile & /* tile */)
{
return false;
}
/* For baking, read render pass PASS_BAKE_PRIMITIVE and PASS_BAKE_DIFFERENTIAL
* to determine which shading points to use for baking at each pixel. Return
* true if any data was read. */
virtual bool read_render_tile(const Tile & /* tile */)
{
return false;
}
};
CCL_NAMESPACE_END

View File

@@ -89,7 +89,6 @@ const NodeEnum *Pass::get_type_enum()
pass_type_enum.insert("material_id", PASS_MATERIAL_ID);
pass_type_enum.insert("motion", PASS_MOTION);
pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT);
pass_type_enum.insert("render_time", PASS_RENDER_TIME);
pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE);
pass_type_enum.insert("aov_color", PASS_AOV_COLOR);
pass_type_enum.insert("aov_value", PASS_AOV_VALUE);
@@ -217,10 +216,6 @@ PassInfo Pass::get_info(const PassType type, const bool include_albedo)
pass_info.num_components = 3;
pass_info.use_exposure = false;
break;
case PASS_RENDER_TIME:
/* This pass is handled entirely on the host side. */
pass_info.num_components = 0;
break;
case PASS_DIFFUSE_COLOR:
case PASS_GLOSSY_COLOR:

View File

@@ -25,12 +25,13 @@
#include "render/bake.h"
#include "render/buffers.h"
#include "render/camera.h"
#include "render/gpu_display.h"
#include "render/display_driver.h"
#include "render/graph.h"
#include "render/integrator.h"
#include "render/light.h"
#include "render/mesh.h"
#include "render/object.h"
#include "render/output_driver.h"
#include "render/scene.h"
#include "render/session.h"
@@ -64,25 +65,6 @@ Session::Session(const SessionParams &params_, const SceneParams &scene_params)
path_trace_ = make_unique<PathTrace>(
device, scene->film, &scene->dscene, render_scheduler_, tile_manager_);
path_trace_->set_progress(&progress);
path_trace_->tile_buffer_update_cb = [&]() {
if (!update_render_tile_cb) {
return;
}
update_render_tile_cb();
};
path_trace_->tile_buffer_write_cb = [&]() {
if (!write_render_tile_cb) {
return;
}
write_render_tile_cb();
};
path_trace_->tile_buffer_read_cb = [&]() -> bool {
if (!read_render_tile_cb) {
return false;
}
read_render_tile_cb();
return true;
};
path_trace_->progress_update_cb = [&]() { update_status_time(); };
tile_manager_.full_buffer_written_cb = [&](string_view filename) {
@@ -97,24 +79,6 @@ Session::~Session()
{
cancel();
/* TODO(sergey): Bring the passes in viewport back.
* It is unclear why there is such an exception needed though. */
#if 0
if (buffers && params.write_render_cb) {
/* Copy to display buffer and write out image if requested */
delete display;
display = new DisplayBuffer(device, false);
display->reset(buffers->params);
copy_to_display_buffer(params.samples);
int w = display->draw_width;
int h = display->draw_height;
uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h);
params.write_render_cb((uchar *)pixels, w, h, 4);
}
#endif
/* Make sure path tracer is destroyed before the device. This is needed because destruction might
* need to access device for device memory free. */
/* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the
@@ -162,7 +126,7 @@ bool Session::ready_to_reset()
void Session::run_main_render_loop()
{
path_trace_->clear_gpu_display();
path_trace_->clear_display();
while (true) {
RenderWork render_work = run_update_for_next_iteration();
@@ -514,9 +478,14 @@ void Session::set_pause(bool pause)
}
}
void Session::set_gpu_display(unique_ptr<GPUDisplay> gpu_display)
void Session::set_output_driver(unique_ptr<OutputDriver> driver)
{
path_trace_->set_gpu_display(move(gpu_display));
path_trace_->set_output_driver(move(driver));
}
void Session::set_display_driver(unique_ptr<DisplayDriver> driver)
{
path_trace_->set_display_driver(move(driver));
}
double Session::get_estimated_remaining_time() const
@@ -636,101 +605,6 @@ void Session::collect_statistics(RenderStats *render_stats)
}
}
/* --------------------------------------------------------------------
* Tile and tile pixels access.
*/
bool Session::has_multiple_render_tiles() const
{
return tile_manager_.has_multiple_tiles();
}
int2 Session::get_render_tile_size() const
{
return path_trace_->get_render_tile_size();
}
int2 Session::get_render_tile_offset() const
{
return path_trace_->get_render_tile_offset();
}
string_view Session::get_render_tile_layer() const
{
const BufferParams &buffer_params = path_trace_->get_render_tile_params();
return buffer_params.layer;
}
string_view Session::get_render_tile_view() const
{
const BufferParams &buffer_params = path_trace_->get_render_tile_params();
return buffer_params.view;
}
bool Session::copy_render_tile_from_device()
{
return path_trace_->copy_render_tile_from_device();
}
bool Session::get_render_tile_pixels(const string &pass_name, int num_components, float *pixels)
{
/* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
* is happening while this function runs. */
const BufferParams &buffer_params = path_trace_->get_render_tile_params();
const BufferPass *pass = buffer_params.find_pass(pass_name);
if (pass == nullptr) {
return false;
}
const bool has_denoised_result = path_trace_->has_denoised_result();
if (pass->mode == PassMode::DENOISED && !has_denoised_result) {
pass = buffer_params.find_pass(pass->type);
if (pass == nullptr) {
/* Happens when denoised result pass is requested but is never written by the kernel. */
return false;
}
}
pass = buffer_params.get_actual_display_pass(pass);
const float exposure = buffer_params.exposure;
const int num_samples = path_trace_->get_num_render_tile_samples();
PassAccessor::PassAccessInfo pass_access_info(*pass);
pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher;
pass_access_info.use_approximate_shadow_catcher_background =
pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background;
const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
const PassAccessor::Destination destination(pixels, num_components);
return path_trace_->get_render_tile_pixels(pass_accessor, destination);
}
bool Session::set_render_tile_pixels(const string &pass_name,
int num_components,
const float *pixels)
{
/* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
* is happening while this function runs. */
const BufferPass *pass = buffer_params_.find_pass(pass_name);
if (!pass) {
return false;
}
const float exposure = scene->film->get_exposure();
const int num_samples = render_scheduler_.get_num_rendered_samples();
const PassAccessor::PassAccessInfo pass_access_info(*pass);
PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
PassAccessor::Source source(pixels, num_components);
return path_trace_->set_render_tile_pixels(pass_accessor, source);
}
/* --------------------------------------------------------------------
* Full-frame on-disk storage.
*/

View File

@@ -35,9 +35,10 @@ CCL_NAMESPACE_BEGIN
class BufferParams;
class Device;
class DeviceScene;
class DisplayDriver;
class OutputDriver;
class PathTrace;
class Progress;
class GPUDisplay;
class RenderBuffers;
class Scene;
class SceneParams;
@@ -67,8 +68,6 @@ class SessionParams {
ShadingSystem shadingsystem;
function<bool(const uchar *pixels, int width, int height, int channels)> write_render_cb;
SessionParams()
{
headless = false;
@@ -114,10 +113,6 @@ class Session {
Stats stats;
Profiler profiler;
function<void(void)> write_render_tile_cb;
function<void(void)> update_render_tile_cb;
function<void(void)> read_render_tile_cb;
/* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after
* writing. Allows an engine integration to keep track of those files without worry about
* transferring the information when it needs to re-create session during rendering. */
@@ -143,7 +138,8 @@ class Session {
void set_samples(int samples);
void set_time_limit(double time_limit);
void set_gpu_display(unique_ptr<GPUDisplay> gpu_display);
void set_output_driver(unique_ptr<OutputDriver> driver);
void set_display_driver(unique_ptr<DisplayDriver> driver);
double get_estimated_remaining_time() const;
@@ -155,24 +151,6 @@ class Session {
void collect_statistics(RenderStats *stats);
/* --------------------------------------------------------------------
* Tile and tile pixels access.
*/
bool has_multiple_render_tiles() const;
/* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. */
int2 get_render_tile_size() const;
int2 get_render_tile_offset() const;
string_view get_render_tile_layer() const;
string_view get_render_tile_view() const;
bool copy_render_tile_from_device();
bool get_render_tile_pixels(const string &pass_name, int num_components, float *pixels);
bool set_render_tile_pixels(const string &pass_name, int num_components, const float *pixels);
/* --------------------------------------------------------------------
* Full-frame on-disk storage.
*/

View File

@@ -420,6 +420,11 @@ const Tile &TileManager::get_current_tile() const
return tile_state_.current_tile;
}
const int2 TileManager::get_size() const
{
return make_int2(buffer_params_.width, buffer_params_.height);
}
bool TileManager::open_tile_output()
{
write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" +

View File

@@ -82,6 +82,7 @@ class TileManager {
bool done();
const Tile &get_current_tile() const;
const int2 get_size() const;
/* Write render buffer of a tile to a file on disk.
*

View File

@@ -101,8 +101,7 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting(
uint8_t display, const GHOST_DisplaySetting &setting)
{
/*
* Mode switching code ported from Quake 2 version 3.21 and bzflag version
* 2.4.0:
* Mode switching code ported from Quake 2 version 3.21 and BZFLAG version 2.4.0:
* ftp://ftp.idsoftware.com/idstuff/source/q2source-3.21.zip
* See linux/gl_glx.c:GLimp_SetMode
* http://wiki.bzflag.org/BZFlag_Source

View File

@@ -1528,13 +1528,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
window->GetTabletData().Pressure = axis_value / ((float)xtablet.PressureLevels);
}
/* the (short) cast and the & 0xffff is bizarre and unexplained anywhere,
* but I got garbage data without it. Found it in the xidump.c source --matt
/* NOTE(@broken): the (short) cast and the & 0xffff is bizarre and unexplained anywhere,
* but I got garbage data without it. Found it in the `xidump.c` source.
*
* The '& 0xffff' just truncates the value to its two lowest bytes, this probably means
* some drivers do not properly set the whole int value? Since we convert to float
* afterward, I don't think we need to cast to short here, but do not have a device to
* check this. --mont29
* NOTE(@mont29): The '& 0xffff' just truncates the value to its two lowest bytes,
* this probably means some drivers do not properly set the whole int value?
* Since we convert to float afterward,
* I don't think we need to cast to short here, but do not have a device to check this.
*/
if (AXIS_VALUE_GET(3, axis_value)) {
window->GetTabletData().Xtilt = (short)(axis_value & 0xffff) /

View File

@@ -1092,9 +1092,9 @@ GHOST_TSuccess GHOST_WindowX11::setOrder(GHOST_TWindowOrder order)
XWindowAttributes attr;
Atom atom;
/* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some
* window managers ignore the former (e.g. kwin from kde) and others
* don't implement the latter (e.g. fluxbox pre 0.9.9) */
/* We use both #XRaiseWindow and #_NET_ACTIVE_WINDOW, since some
* window managers ignore the former (e.g. KWIN from KDE) and others
* don't implement the latter (e.g. FLUXBOX before 0.9.9). */
XRaiseWindow(m_display, m_window);

View File

@@ -89,7 +89,7 @@ typedef struct localListBase {
void *first, *last;
} localListBase;
/* note: keep this struct aligned (e.g., irix/gcc) - Hos */
/* NOTE(@hos): keep this struct aligned (e.g., IRIX/GCC). */
typedef struct MemHead {
int tag1;
size_t len;
@@ -98,9 +98,8 @@ typedef struct MemHead {
const char *nextname;
int tag2;
short pad1;
short alignment; /* if non-zero aligned alloc was used
* and alignment is stored here.
*/
/* if non-zero aligned allocation was used and alignment is stored here. */
short alignment;
#ifdef DEBUG_MEMCOUNTER
int _count;
#endif

View File

@@ -1112,7 +1112,7 @@ static tbool AssignRecur(const int piTriListIn[],
static tbool CompareSubGroups(const SSubGroup *pg1, const SSubGroup *pg2);
static void QuickSort(int *pSortBuffer, int iLeft, int iRight, unsigned int uSeed);
static STSpace EvalTspace(int face_indices[],
static STSpace EvalTspace(const int face_indices[],
const int iFaces,
const int piTriListIn[],
const STriInfo pTriInfos[],
@@ -1292,7 +1292,7 @@ static tbool GenerateTSpaces(STSpace psTspace[],
return TTRUE;
}
static STSpace EvalTspace(int face_indices[],
static STSpace EvalTspace(const int face_indices[],
const int iFaces,
const int piTriListIn[],
const STriInfo pTriInfos[],

View File

@@ -62,11 +62,11 @@ const bTheme U_theme_default = {
.roundness = 0.2f,
},
.wcol_radio = {
.outline = RGBA(0x373737ff),
.inner = RGBA(0x595959ff),
.inner_sel = RGBA(0x5680c2e6),
.item = RGBA(0xffffffff),
.text = RGBA(0xe6e6e6ff),
.outline = RGBA(0x2c2c2cff),
.inner = RGBA(0x2c2c2cff),
.inner_sel = RGBA(0x476da3ff),
.item = RGBA(0x2c2c2cff),
.text = RGBA(0xccccccff),
.text_sel = RGBA(0xffffffff),
.shadetop = 5,
.shadedown = -5,
@@ -74,9 +74,9 @@ const bTheme U_theme_default = {
},
.wcol_option = {
.outline = RGBA(0x373737ff),
.inner = RGBA(0x666666ff),
.inner_sel = RGBA(0x5680c2e6),
.item = RGBA(0xffffffff),
.inner = RGBA(0x1f1f1fff),
.inner_sel = RGBA(0x71a8ffe6),
.item = RGBA(0x1f1f1fff),
.text = RGBA(0xe6e6e6ff),
.text_sel = RGBA(0xffffffff),
.shadedown = -15,
@@ -84,8 +84,8 @@ const bTheme U_theme_default = {
},
.wcol_toggle = {
.outline = RGBA(0x373737ff),
.inner = RGBA(0x595959ff),
.inner_sel = RGBA(0x5680c2e6),
.inner = RGBA(0x2c2c2cff),
.inner_sel = RGBA(0x476da3ff),
.item = RGBA(0x191919ff),
.text = RGBA(0xe6e6e6ff),
.text_sel = RGBA(0xffffffff),
@@ -93,8 +93,8 @@ const bTheme U_theme_default = {
},
.wcol_num = {
.outline = RGBA(0x444444ff),
.inner = RGBA(0x595959ff),
.inner_sel = RGBA(0x505050ff),
.inner = RGBA(0x4f4f4fff),
.inner_sel = RGBA(0x1f1f1fff),
.item = RGBA(0x191919ff),
.text = RGBA(0xe6e6e6ff),
.text_sel = RGBA(0xffffffff),
@@ -102,9 +102,9 @@ const bTheme U_theme_default = {
},
.wcol_numslider = {
.outline = RGBA(0x444444ff),
.inner = RGBA(0x595959ff),
.inner = RGBA(0x2c2c2cff),
.inner_sel = RGBA(0x505050ff),
.item = RGBA(0x5680c2e6),
.item = RGBA(0x5680c2ff),
.text = RGBA(0xe6e6e6ff),
.text_sel = RGBA(0xffffffff),
.shaded = 1,
@@ -112,9 +112,9 @@ const bTheme U_theme_default = {
.roundness = 0.2f,
},
.wcol_tab = {
.outline = RGBA(0x202020ff),
.inner = RGBA(0x2b2b2bff),
.inner_sel = RGBA(0x424242ff),
.outline = RGBA(0x1a1a1aff),
.inner = RGBA(0x1a1a1aff),
.inner_sel = RGBA(0x2e2e2eff),
.item = RGBA(0x2d2d2dff),
.text = RGBA(0x989898ff),
.text_sel = RGBA(0xffffffff),
@@ -123,7 +123,7 @@ const bTheme U_theme_default = {
.wcol_menu = {
.outline = RGBA(0x444444ff),
.inner = RGBA(0x2c2c2cff),
.inner_sel = RGBA(0x696e76ff),
.inner_sel = RGBA(0x476da3ff),
.item = RGBA(0xd9d9d9ff),
.text = RGBA(0xd9d9d9ff),
.text_sel = RGBA(0xffffffff),
@@ -133,8 +133,8 @@ const bTheme U_theme_default = {
},
.wcol_pulldown = {
.outline = RGBA(0x4d4d4dff),
.inner = RGBA(0x2e2e2ecc),
.inner_sel = RGBA(0x5680c2e6),
.inner = RGBA(0x00000033),
.inner_sel = RGBA(0x476da3ff),
.item = RGBA(0x727272ff),
.text = RGBA(0xd9d9d9ff),
.text_sel = RGBA(0xffffffff),
@@ -144,7 +144,7 @@ const bTheme U_theme_default = {
},
.wcol_menu_back = {
.outline = RGBA(0x19191aff),
.inner = RGBA(0x1f1f1fef),
.inner = RGBA(0x1a1a1aff),
.inner_sel = RGBA(0x585858ff),
.item = RGBA(0x727272ff),
.text = RGBA(0xa5a5a5ff),
@@ -154,7 +154,8 @@ const bTheme U_theme_default = {
.roundness = 0.2f,
},
.wcol_menu_item = {
.inner_sel = RGBA(0x5680c2e6),
.outline = RGBA(0x37373700),
.inner_sel = RGBA(0x476da3ff),
.item = RGBA(0xffffff8f),
.text = RGBA(0xe6e6e6ff),
.text_sel = RGBA(0xffffffff),
@@ -173,7 +174,7 @@ const bTheme U_theme_default = {
.roundness = 0.2f,
},
.wcol_box = {
.outline = RGBA(0x444444ff),
.outline = RGBA(0x262626ff),
.inner = RGBA(0x00000033),
.inner_sel = RGBA(0x696e76ff),
.item = RGBA(0x191919ff),
@@ -237,7 +238,7 @@ const bTheme U_theme_default = {
.widget_emboss = RGBA(0x00000026),
.menu_shadow_fac = 0.3f,
.menu_shadow_width = 4,
.editor_outline = RGBA(0x1f1f1fff),
.editor_outline = RGBA(0x1a1a1aff),
.transparent_checker_primary = RGBA(0x333333ff),
.transparent_checker_secondary = RGBA(0x262626ff),
.transparent_checker_size = 8,
@@ -262,11 +263,11 @@ const bTheme U_theme_default = {
.icon_folder = RGBA(0xe3c16eff),
},
.space_properties = {
.back = RGBA(0x42424200),
.back = RGBA(0x2e2e2e00),
.title = RGBA(0xd4d4d4ff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x424242ff),
.header = RGBA(0x2e2e2eff),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
@@ -277,24 +278,24 @@ const bTheme U_theme_default = {
.button_title = RGBA(0xffffffff),
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.navigation_bar = RGBA(0x232323ff),
.navigation_bar = RGBA(0x1a1a1aff),
.panelcolors = {
.header = RGBA(0x424242ff),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x00000024),
.sub_back = RGBA(0x0000003e),
},
.active = RGBA(0x5680c2ff),
.vertex_size = 3,
.outline_width = 1,
.facedot_size = 4,
.match = RGBA(0x5680c2ff),
.active = RGBA(0x5680c2ff),
},
.space_view3d = {
.back = RGBA(0x393939ff),
.back = RGBA(0x2c2c2cff),
.title = RGBA(0xeeeeeeff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x42424200),
.header = RGBA(0x2e2e2e00),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
@@ -306,8 +307,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242e6),
.back = RGBA(0x333333f0),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.grid = RGBA(0x6666664d),
@@ -398,8 +399,8 @@ const bTheme U_theme_default = {
.button_text_hi = RGBA(0xffffffff),
.execution_buts = RGBA(0x444444ff),
.panelcolors = {
.header = RGBA(0x4b4b4bff),
.back = RGBA(0x404040ff),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.hilite = RGBA(0x4f76b3ff),
@@ -429,8 +430,8 @@ const bTheme U_theme_default = {
.list_text = RGBA(0xb8b8b8ff),
.list_text_hi = RGBA(0xffaf29ff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.shade1 = RGBA(0x96969600),
@@ -480,8 +481,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.vertex_size = 3,
@@ -523,8 +524,8 @@ const bTheme U_theme_default = {
.list_text = RGBA(0xb8b8b8ff),
.list_text_hi = RGBA(0xffaf29ff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.shade1 = RGBA(0xc0c0c000),
@@ -583,8 +584,8 @@ const bTheme U_theme_default = {
.list_text = RGBA(0xb8b8b8ff),
.list_text_hi = RGBA(0xffaf29ff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.shade1 = RGBA(0x96969600),
@@ -633,8 +634,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.shade1 = RGBA(0xa0a0a000),
@@ -663,16 +664,16 @@ const bTheme U_theme_default = {
.selected_strip = RGBA(0xff8f0dff),
.gp_vertex_size = 3,
.gp_vertex_select = RGBA(0xff8500ff),
.row_alternate = RGBA(0xffffff0d),
.anim_preview_range = RGBA(0xa14d0066),
.metadatatext = RGBA(0xffffffff),
.row_alternate = RGBA(0xffffff0d),
},
.space_image = {
.back = RGBA(0x44444400),
.back = RGBA(0x2e2e2e00),
.title = RGBA(0xeeeeeeff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x424242ff),
.header = RGBA(0x2e2e2eff),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
@@ -684,8 +685,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.grid = RGBA(0x505050ff),
@@ -738,8 +739,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x42424200),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.shade2 = RGBA(0x5680c2e6),
@@ -759,11 +760,11 @@ const bTheme U_theme_default = {
.line_numbers = RGBA(0xd0d0d0ff),
},
.space_outliner = {
.back = RGBA(0x28282800),
.back = RGBA(0x25252500),
.title = RGBA(0xffffffff),
.text = RGBA(0xc3c3c3ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x454545ff),
.header = RGBA(0x282828ff),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
@@ -775,34 +776,34 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.active = RGBA(0x3b5689ff),
.active = RGBA(0x4d341fff),
.vertex_size = 3,
.outline_width = 1,
.facedot_size = 4,
.match = RGBA(0x337f334c),
.selected_highlight = RGBA(0x223a5bff),
.match = RGBA(0x1466144c),
.selected_highlight = RGBA(0x4d433eff),
.selected_object = RGBA(0xe96a00ff),
.active_object = RGBA(0xffaf29ff),
.edited_object = RGBA(0x00806266),
.row_alternate = RGBA(0xffffff07),
.row_alternate = RGBA(0xffffff05),
},
.space_node = {
.back = RGBA(0x23232300),
.title = RGBA(0xeeeeeeff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x424242ff),
.header = RGBA(0x2e2e2eff),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
.tab_inactive = RGBA(0x2b2b2bff),
.tab_back = RGBA(0x232323ff),
.tab_outline = RGBA(0x232323ff),
.button = RGBA(0x42424200),
.button = RGBA(0x232323ff),
.button_title = RGBA(0xffffffff),
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
@@ -811,8 +812,8 @@ const bTheme U_theme_default = {
.list_text = RGBA(0xccccccff),
.list_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.shade2 = RGBA(0x7f707064),
@@ -851,11 +852,11 @@ const bTheme U_theme_default = {
.gp_vertex_select = RGBA(0xff8500ff),
},
.space_preferences = {
.back = RGBA(0x42424200),
.title = RGBA(0xeeeeeeff),
.back = RGBA(0x2e2e2e00),
.title = RGBA(0xd4d4d4ff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x424242ff),
.header = RGBA(0x2e2e2eff),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
@@ -866,11 +867,11 @@ const bTheme U_theme_default = {
.button_title = RGBA(0xffffffff),
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.navigation_bar = RGBA(0x4b4b4bff),
.execution_buts = RGBA(0x4b4b4bff),
.navigation_bar = RGBA(0x383838ff),
.execution_buts = RGBA(0x383838ff),
.panelcolors = {
.header = RGBA(0x42424200),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.vertex_size = 3,
@@ -894,8 +895,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.console_output = RGBA(0x71a8ffff),
@@ -928,8 +929,8 @@ const bTheme U_theme_default = {
.list_text = RGBA(0x000000ff),
.list_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.grid = RGBA(0x424242ff),
@@ -961,11 +962,11 @@ const bTheme U_theme_default = {
.metadatatext = RGBA(0xffffffff),
},
.space_topbar = {
.back = RGBA(0x42424200),
.back = RGBA(0x1a1a1a00),
.title = RGBA(0xffffffff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x232323ff),
.header = RGBA(0x1a1a1aff),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
@@ -977,8 +978,8 @@ const bTheme U_theme_default = {
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.vertex_size = 3,
@@ -987,12 +988,12 @@ const bTheme U_theme_default = {
.gp_vertex_size = 3,
},
.space_statusbar = {
.back = RGBA(0x2e2e2e00),
.back = RGBA(0x1a1a1a00),
.title = RGBA(0xffffffff),
.text = RGBA(0x838383ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x303030ff),
.header_text = RGBA(0xaaaaaaff),
.header = RGBA(0x1a1a1aff),
.header_text = RGBA(0x696969ff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x4b4b4bff),
.tab_inactive = RGBA(0x2b2b2bff),
@@ -1001,8 +1002,8 @@ const bTheme U_theme_default = {
.button = RGBA(0x353535ff),
.button_text_hi = RGBA(0xffffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.vertex_size = 3,
@@ -1026,11 +1027,16 @@ const bTheme U_theme_default = {
.button_title = RGBA(0xffffffff),
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.list = RGBA(0x424242ff),
.list_title = RGBA(0xc3c3c3ff),
.list_text = RGBA(0xc3c3c3ff),
.list_text_hi = RGBA(0x00ffffff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x383838ff),
.back = RGBA(0x383838ff),
.sub_back = RGBA(0x0000003e),
},
.hilite = RGBA(0x80808080),
.active = RGBA(0x3b5689ff),
.vertex_size = 3,
.outline_width = 1,
@@ -1041,11 +1047,6 @@ const bTheme U_theme_default = {
.active_object = RGBA(0xffaf29ff),
.edited_object = RGBA(0x00806266),
.row_alternate = RGBA(0xffffff07),
.list = RGBA(0x424242ff),
.list_title = RGBA(0xc3c3c3ff),
.list_text = RGBA(0xc3c3c3ff),
.list_text_hi = RGBA(0xffffff),
.hilite = RGBA(0x80808080),
},
.tarm = {
{

View File

@@ -378,7 +378,15 @@ def dump_rna_messages(msgs, reports, settings, verbose=False):
if cls in blacklist_rna_class:
return cls.__name__
cls_id = ""
bl_rna = cls.bl_rna
bl_rna = getattr(cls, "bl_rna", None)
# It seems that py-defined 'wrappers' RNA classes (like `MeshEdge` in `bpy_types.py`) need to be accessed
# once from `bpy.types` before they have a valid `bl_rna` member.
# Weirdly enough, this is only triggered on release builds, debug builds somehow do not have that issue.
if bl_rna is None:
if getattr(bpy.types, cls.__name__, None) is not None:
bl_rna = getattr(cls, "bl_rna", None)
if bl_rna is None:
raise TypeError("Unknown RNA class")
while bl_rna:
cls_id = bl_rna.identifier + "." + cls_id
bl_rna = bl_rna.base

View File

@@ -219,7 +219,7 @@ def enable_addons(addons=None, support=None, disable=False, check_only=False):
try:
import bpy
except ModuleNotFoundError:
print("Could not import bpy, enable_addons must be run from whithin Blender.")
print("Could not import bpy, enable_addons must be run from within Blender.")
return
if addons is None:

View File

@@ -176,6 +176,7 @@ class SpellChecker:
"precalculate",
"precomputing",
"prefetch",
"prefilter", "prefiltering",
"preload",
"premultiply", "premultiplied",
"prepass",
@@ -225,6 +226,7 @@ class SpellChecker:
"subpath",
"subsize",
"substep", "substeps",
"substring",
"targetless",
"textbox", "textboxes",
"tilemode",
@@ -731,6 +733,7 @@ class SpellChecker:
"tma",
"ui",
"unix",
"uuid",
"vbo", "vbos",
"vr",
"wxyz",

View File

@@ -77,10 +77,10 @@
</wcol_toolbar_item>
<wcol_radio>
<ThemeWidgetColors
outline="#434343"
outline="#3b3b3b"
inner="#3b3b3bff"
inner_sel="#5680c2e6"
item="#ffffffff"
item="#3b3b3bff"
text="#d9d9d9"
text_sel="#ffffff"
show_shaded="FALSE"
@@ -257,7 +257,7 @@
</wcol_tooltip>
<wcol_menu_item>
<ThemeWidgetColors
outline="#000000"
outline="#a6a6a6"
inner="#00000000"
inner_sel="#5680c2e6"
item="#ffffff8f"
@@ -879,7 +879,7 @@
>
<panelcolors>
<ThemePanelColors
header="#b3b3b300"
header="#a3a3a3cc"
back="#a3a3a3cc"
sub_back="#00000024"
>
@@ -1054,17 +1054,17 @@
<ThemeInfo
info_selected="#6080ff"
info_selected_text="#000000"
info_error="#FF0038ff"
info_error="#ff0038ff"
info_error_text="#000000"
info_warning="#FFE900ff"
info_warning="#ffe900ff"
info_warning_text="#000000"
info_info="#0068B3ff"
info_info="#0068b3ff"
info_info_text="#000000"
info_debug="#B30095ff"
info_debug="#b30095ff"
info_debug_text="#000000"
info_property="#44B300ff"
info_property="#44b300ff"
info_property_text="#000000"
info_operator="#44B300ff"
info_operator="#44b300ff"
info_operator_text="#000000"
>
<space>
@@ -1350,6 +1350,15 @@
</panelcolors>
</ThemeSpaceGeneric>
</space>
<space_list>
<ThemeSpaceListGeneric
list="#424242"
list_title="#c3c3c3"
list_text="#c3c3c3"
list_text_hi="#00ffff"
>
</ThemeSpaceListGeneric>
</space_list>
</ThemeSpreadsheet>
</spreadsheet>
<bone_color_sets>

View File

@@ -71,7 +71,7 @@ class Params:
# (derived from other settings).
#
# This case needs to be checked often,
# Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHT' else False)`.
# Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHTMOUSE' else False)`.
"use_fallback_tool_rmb",
# Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`.
"select_mouse_value_fallback",
@@ -195,7 +195,7 @@ class Params:
self.use_file_single_click = use_file_single_click
# Convenience variables:
self.use_fallback_tool_rmb = self.use_fallback_tool if self.select_mouse == 'RIGHT' else False
self.use_fallback_tool_rmb = self.use_fallback_tool if select_mouse == 'RIGHT' else False
self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value
self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS'
self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'}
@@ -4312,7 +4312,7 @@ def km_pose(params):
("pose.push", {"type": 'E', "value": 'PRESS', "ctrl": True}, None),
("pose.relax", {"type": 'E', "value": 'PRESS', "alt": True}, None),
("pose.breakdown", {"type": 'E', "value": 'PRESS', "shift": True}, None),
("pose.blend_to_neighbour", {"type": 'E', "value": 'PRESS', "shift": True, "alt": True}, None),
("pose.blend_to_neighbor", {"type": 'E', "value": 'PRESS', "shift": True, "alt": True}, None),
op_menu("VIEW3D_MT_pose_propagate", {"type": 'P', "value": 'PRESS', "alt": True}),
*(
(("object.hide_collection",

View File

@@ -54,11 +54,26 @@ def update_factory_startup_grease_pencils():
gpd.onion_keyframe_type = 'ALL'
def update_factory_startup_theme():
# To prevent saving over the current theme Preferences,
# store the current state of use_preferences_save to use later.
preferences = bpy.context.preferences
save_preferences_state = preferences.use_preferences_save
# Turn use_preferences_save off and set header background alpha.
preferences.use_preferences_save = False
preferences.themes['Default'].view_3d.space.header[3] = 0.8
# Restore the original use_preferences_save status.
preferences.use_preferences_save = save_preferences_state
@persistent
def load_handler(_):
update_factory_startup_screens()
update_factory_startup_scenes()
update_factory_startup_grease_pencils()
update_factory_startup_theme()
def register():

View File

@@ -46,44 +46,85 @@ def selected_sequences_len(context):
def draw_color_balance(layout, color_balance):
layout.prop(color_balance, "correction_method")
layout.use_property_split = False
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Lift:")
col.separator()
col.separator()
col.prop(color_balance, "lift", text="")
col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
if color_balance.correction_method == 'LIFT_GAMMA_GAIN':
col = flow.column()
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Lift:")
col.separator()
col.separator()
col.prop(color_balance, "lift", text="")
col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Gamma:")
col.separator()
col.separator()
col.prop(color_balance, "gamma", text="")
col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
col = flow.column()
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Gamma:")
col.separator()
col.separator()
col.prop(color_balance, "gamma", text="")
col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Gain:")
col.separator()
col.separator()
col.prop(color_balance, "gain", text="")
col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Gain:")
col.separator()
col.separator()
col.prop(color_balance, "gain", text="")
col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
elif color_balance.correction_method == 'OFFSET_POWER_SLOPE':
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Offset:")
col.separator()
col.separator()
col.prop(color_balance, "offset", text="")
col.prop(color_balance, "invert_offset", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "offset", value_slider=True, cubic=True)
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Power:")
col.separator()
col.separator()
col.prop(color_balance, "power", text="")
col.prop(color_balance, "invert_power", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "power", value_slider=True, cubic=True)
col = flow.column()
box = col.box()
split = box.split(factor=0.35)
col = split.column(align=True)
col.label(text="Slope:")
col.separator()
col.separator()
col.prop(color_balance, "slope", text="")
col.prop(color_balance, "invert_slope", text="Invert", icon='ARROW_LEFTRIGHT')
split.template_color_picker(color_balance, "slope", value_slider=True, cubic=True)
class SEQUENCER_PT_active_tool(ToolActivePanelHelper, Panel):
@@ -148,8 +189,12 @@ class SEQUENCER_HT_header(Header):
if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
row = layout.row(align=True)
row.prop(sequencer_tool_settings, "overlap_mode", text="")
if st.view_type == 'SEQUENCER_PREVIEW':
row = layout.row(align=True)
row.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True)
if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
row = layout.row(align=True)
row.prop(tool_settings, "use_snap_sequencer", text="")
sub = row.row(align=True)
@@ -1003,16 +1048,23 @@ class SequencerButtonsPanel_Output:
return cls.has_preview(context)
class SEQUENCER_PT_color_tag_picker(Panel):
bl_label = "Color Tag"
class SequencerColorTagPicker:
bl_space_type = 'SEQUENCE_EDITOR'
bl_region_type = 'UI'
bl_category = "Strip"
bl_options = {'HIDE_HEADER', 'INSTANCED'}
@staticmethod
def has_sequencer(context):
return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
@classmethod
def poll(cls, context):
return context.active_sequence_strip is not None
return cls.has_sequencer(context) and context.active_sequence_strip is not None
class SEQUENCER_PT_color_tag_picker(SequencerColorTagPicker, Panel):
bl_label = "Color Tag"
bl_category = "Strip"
bl_options = {'HIDE_HEADER', 'INSTANCED'}
def draw(self, context):
layout = self.layout
@@ -1024,13 +1076,9 @@ class SEQUENCER_PT_color_tag_picker(Panel):
row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_%02d' % i
class SEQUENCER_MT_color_tag_picker(Menu):
class SEQUENCER_MT_color_tag_picker(SequencerColorTagPicker, Menu):
bl_label = "Set Color Tag"
@classmethod
def poll(cls, context):
return context.active_sequence_strip is not None
def draw(self, context):
layout = self.layout

View File

@@ -3443,7 +3443,7 @@ class VIEW3D_MT_pose_slide(Menu):
layout.operator("pose.push")
layout.operator("pose.relax")
layout.operator("pose.breakdown")
layout.operator("pose.blend_to_neighbour")
layout.operator("pose.blend_to_neighbor")
class VIEW3D_MT_pose_propagate(Menu):
@@ -3596,7 +3596,7 @@ class VIEW3D_MT_pose_context_menu(Menu):
layout.operator("pose.push")
layout.operator("pose.relax")
layout.operator("pose.breakdown")
layout.operator("pose.blend_to_neighbour")
layout.operator("pose.blend_to_neighbor")
layout.separator()

View File

@@ -279,6 +279,7 @@ shader_node_categories = [
]),
ShaderNodeCategory("SH_NEW_CONVERTOR", "Converter", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeValToRGB"),
@@ -524,9 +525,14 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurveFill"),
NodeItem("GeometryNodeCurveTrim"),
NodeItem("GeometryNodeCurveLength"),
NodeItem("GeometryNodeCurveSplineType"),
NodeItem("GeometryNodeSplineLength"),
NodeItem("GeometryNodeCurveSubdivide"),
NodeItem("GeometryNodeCurveParameter"),
NodeItem("GeometryNodeCurveSetHandles"),
NodeItem("GeometryNodeInputTangent"),
NodeItem("GeometryNodeCurveSample"),
NodeItem("GeometryNodeCurveHandleTypeSelection"),
NodeItem("GeometryNodeCurveFillet"),
NodeItem("GeometryNodeCurveReverse"),
]),
@@ -615,9 +621,11 @@ geometry_node_categories = [
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeRotateEuler"),
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
@@ -639,6 +647,7 @@ geometry_node_categories = [
GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll),
NodeItem("GeometryNodePointsToVolume"),
NodeItem("GeometryNodeVolumeToMesh"),
]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),

View File

@@ -14,11 +14,17 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup blendthumb
*
* Thumbnail from Blend file extraction for MS-Windows (DLL).
*/
#include <new>
#include <objbase.h>
#include <shlobj.h> // For SHChangeNotify
#include <shlobj.h> /* For #SHChangeNotify */
#include <shlwapi.h>
#include <thumbcache.h> // For IThumbnailProvider.
#include <thumbcache.h> /* For IThumbnailProvider */
extern HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv);
@@ -33,16 +39,16 @@ struct CLASS_OBJECT_INIT {
PFNCREATEINSTANCE pfnCreate;
};
// add classes supported by this module here
/* Add classes supported by this module here. */
const CLASS_OBJECT_INIT c_rgClassObjectInit[] = {
{&CLSID_BlendThumbHandler, CBlendThumb_CreateInstance}};
long g_cRefModule = 0;
// Handle the DLL's module
HINSTANCE g_hInst = NULL;
/** Handle the DLL's module */
HINSTANCE g_hInst = nullptr;
// Standard DLL functions
/** Standard DLL functions. */
STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
{
if (dwReason == DLL_PROCESS_ATTACH) {
@@ -54,7 +60,7 @@ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
STDAPI DllCanUnloadNow()
{
// Only allow the DLL to be unloaded after all outstanding references have been released
/* Only allow the DLL to be unloaded after all outstanding references have been released. */
return (g_cRefModule == 0) ? S_OK : S_FALSE;
}
@@ -76,7 +82,7 @@ class CClassFactory : public IClassFactory {
REFIID riid,
void **ppv)
{
*ppv = NULL;
*ppv = nullptr;
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
for (size_t i = 0; i < cClassObjectInits; i++) {
if (clsid == *pClassObjectInits[i].pClsid) {
@@ -87,7 +93,8 @@ class CClassFactory : public IClassFactory {
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
break; // match found
/* Match found. */
break;
}
}
return hr;
@@ -98,7 +105,7 @@ class CClassFactory : public IClassFactory {
DllAddRef();
}
// IUnknown
/** #IUnknown */
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {QITABENT(CClassFactory, IClassFactory), {0}};
@@ -119,7 +126,7 @@ class CClassFactory : public IClassFactory {
return cRef;
}
// IClassFactory
/** #IClassFactory */
IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv);
@@ -152,33 +159,37 @@ STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv)
clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv);
}
// A struct to hold the information required for a registry entry
/**
* A struct to hold the information required for a registry entry.
*/
struct REGISTRY_ENTRY {
HKEY hkeyRoot;
PCWSTR pszKeyName;
PCWSTR pszValueName;
DWORD dwValueType;
PCWSTR pszData; // These two fields could/should have been a union, but C++
DWORD dwData; // only lets you initialize the first field in a union.
/** These two fields could/should have been a union, but C++ */
PCWSTR pszData;
/** Only lets you initialize the first field in a union. */
DWORD dwData;
};
// Creates a registry key (if needed) and sets the default value of the key
/**
* Creates a registry key (if needed) and sets the default value of the key.
*/
HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry)
{
HKEY hKey;
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot,
pRegistryEntry->pszKeyName,
0,
NULL,
nullptr,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
NULL,
nullptr,
&hKey,
NULL));
nullptr));
if (SUCCEEDED(hr)) {
// All this just to support REG_DWORD...
/* All this just to support #REG_DWORD. */
DWORD size;
DWORD data;
BYTE *lpData = (LPBYTE)pRegistryEntry->pszData;
@@ -202,9 +213,9 @@ HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry)
return hr;
}
//
// Registers this COM server
//
/**
* Registers this COM server.
*/
STDAPI DllRegisterServer()
{
HRESULT hr;
@@ -216,15 +227,15 @@ STDAPI DllRegisterServer()
}
else {
const REGISTRY_ENTRY rgRegistryEntries[] = {
// RootKey KeyName ValueName ValueType Data
/* `RootKey KeyName ValueName ValueType Data` */
{HKEY_CURRENT_USER,
L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER,
NULL,
nullptr,
REG_SZ,
SZ_BLENDTHUMBHANDLER},
{HKEY_CURRENT_USER,
L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32",
NULL,
nullptr,
REG_SZ,
szModuleName},
{HKEY_CURRENT_USER,
@@ -237,10 +248,10 @@ STDAPI DllRegisterServer()
L"Treatment",
REG_DWORD,
0,
0}, // doesn't appear to do anything...
0}, /* This doesn't appear to do anything. */
{HKEY_CURRENT_USER,
L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}",
NULL,
nullptr,
REG_SZ,
SZ_CLSID_BLENDTHUMBHANDLER},
};
@@ -251,17 +262,17 @@ STDAPI DllRegisterServer()
}
}
if (SUCCEEDED(hr)) {
// This tells the shell to invalidate the thumbnail cache. This is important because any
// .blend files viewed before registering this handler would otherwise show cached blank
// thumbnails.
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
/* This tells the shell to invalidate the thumbnail cache.
* This is important because any `.blend` files viewed before registering this handler
* would otherwise show cached blank thumbnails. */
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
}
return hr;
}
//
// Unregisters this COM server
//
/**
* Unregisters this COM server
*/
STDAPI DllUnregisterServer()
{
HRESULT hr = S_OK;
@@ -270,11 +281,11 @@ STDAPI DllUnregisterServer()
L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER,
L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"};
// Delete the registry entries
/* Delete the registry entries. */
for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++) {
hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i]));
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
// If the registry entry has already been deleted, say S_OK.
/* If the registry entry has already been deleted, say S_OK. */
hr = S_OK;
}
}

View File

@@ -26,10 +26,13 @@
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
#include "BLI_uuid.h"
#include "BLI_vector.hh"
#include "BKE_asset_catalog_path.hh"
#include <map>
#include <memory>
#include <set>
@@ -38,7 +41,6 @@
namespace blender::bke {
using CatalogID = bUUID;
using CatalogPath = std::string;
using CatalogPathComponent = std::string;
/* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not
* available on the minimum macOS target version. */
@@ -47,12 +49,12 @@ using CatalogFilePath = std::string;
class AssetCatalog;
class AssetCatalogDefinitionFile;
class AssetCatalogTree;
class AssetCatalogFilter;
/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single
* directory hierarchy). */
class AssetCatalogService {
public:
static const char PATH_SEPARATOR;
static const CatalogFilePath DEFAULT_CATALOG_FILENAME;
public:
@@ -94,15 +96,23 @@ class AssetCatalogService {
void merge_from_disk_before_writing();
/** Return catalog with the given ID. Return nullptr if not found. */
AssetCatalog *find_catalog(CatalogID catalog_id);
AssetCatalog *find_catalog(CatalogID catalog_id) const;
/** Return first catalog with the given path. Return nullptr if not found. This is not an
* efficient call as it's just a linear search over the catalogs. */
AssetCatalog *find_catalog_by_path(const CatalogPath &path) const;
AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const;
/**
* Create a filter object that can be used to determine whether an asset belongs to the given
* catalog, or any of the catalogs in the sub-tree rooted at the given catalog.
*
* \see #AssetCatalogFilter
*/
AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const;
/** Create a catalog with some sensible auto-generated catalog ID.
* The catalog will be saved to the default catalog file.*/
AssetCatalog *create_catalog(const CatalogPath &catalog_path);
AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path);
/**
* Soft-delete the catalog, ensuring it actually gets deleted when the catalog definition file is
@@ -112,7 +122,7 @@ class AssetCatalogService {
/**
* Update the catalog path, also updating the catalog path of all sub-catalogs.
*/
void update_catalog_path(CatalogID catalog_id, const CatalogPath &new_catalog_path);
void update_catalog_path(CatalogID catalog_id, const AssetCatalogPath &new_catalog_path);
AssetCatalogTree *get_catalog_tree();
@@ -124,7 +134,7 @@ class AssetCatalogService {
Map<CatalogID, std::unique_ptr<AssetCatalog>> catalogs_;
Map<CatalogID, std::unique_ptr<AssetCatalog>> deleted_catalogs_;
std::unique_ptr<AssetCatalogDefinitionFile> catalog_definition_file_;
std::unique_ptr<AssetCatalogTree> catalog_tree_;
std::unique_ptr<AssetCatalogTree> catalog_tree_ = std::make_unique<AssetCatalogTree>();
CatalogFilePath asset_library_root_;
void load_directory_recursive(const CatalogFilePath &directory_path);
@@ -150,6 +160,11 @@ class AssetCatalogService {
std::unique_ptr<AssetCatalogTree> read_into_tree();
void rebuild_tree();
/**
* For every catalog, ensure that its parent path also has a known catalog.
*/
void create_missing_catalogs();
};
/**
@@ -166,13 +181,15 @@ class AssetCatalogTreeItem {
AssetCatalogTreeItem(StringRef name,
CatalogID catalog_id,
StringRef simple_name,
const AssetCatalogTreeItem *parent = nullptr);
CatalogID get_catalog_id() const;
StringRef get_name() const;
StringRefNull get_simple_name() const;
StringRefNull get_name() const;
/** Return the full catalog path, defined as the name of this catalog prefixed by the full
* catalog path of its parent and a separator. */
CatalogPath catalog_path() const;
AssetCatalogPath catalog_path() const;
int count_parents() const;
bool has_children() const;
@@ -186,6 +203,8 @@ class AssetCatalogTreeItem {
/** The user visible name of this component. */
CatalogPathComponent name_;
CatalogID catalog_id_;
/** Copy of #AssetCatalog::simple_name. */
std::string simple_name_;
/** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to
* build a path). */
@@ -280,10 +299,10 @@ class AssetCatalogDefinitionFile {
class AssetCatalog {
public:
AssetCatalog() = default;
AssetCatalog(CatalogID catalog_id, const CatalogPath &path, const std::string &simple_name);
AssetCatalog(CatalogID catalog_id, const AssetCatalogPath &path, const std::string &simple_name);
CatalogID catalog_id;
CatalogPath path;
AssetCatalogPath path;
/**
* Simple, human-readable name for the asset catalog. This is stored on assets alongside the
* catalog ID; the catalog ID is a UUID that is not human-readable,
@@ -297,27 +316,17 @@ class AssetCatalog {
bool is_deleted = false;
} flags;
/**
* \return true only if this catalog's path is contained within the given path.
* When this catalog's path is equal to the given path, return true as well.
*
* Note that non-normalized paths (so for example starting or ending with a slash) are not
* supported, and result in undefined behavior.
*/
bool is_contained_in(const CatalogPath &other_path) const;
/**
* Create a new Catalog with the given path, auto-generating a sensible catalog simple-name.
*
* NOTE: the given path will be cleaned up (trailing spaces removed, etc.), so the returned
* `AssetCatalog`'s path differ from the given one.
*/
static std::unique_ptr<AssetCatalog> from_path(const CatalogPath &path);
static CatalogPath cleanup_path(const CatalogPath &path);
static std::unique_ptr<AssetCatalog> from_path(const AssetCatalogPath &path);
protected:
/** Generate a sensible catalog ID for the given path. */
static std::string sensible_simple_name_for_path(const CatalogPath &path);
static std::string sensible_simple_name_for_path(const AssetCatalogPath &path);
};
/** Comparator for asset catalogs, ordering by (path, UUID). */
@@ -336,4 +345,20 @@ struct AssetCatalogPathCmp {
* Being a set, duplicates are removed. The catalog's simple name is ignored in this. */
using AssetCatalogOrderedSet = std::set<const AssetCatalog *, AssetCatalogPathCmp>;
/**
* Filter that can determine whether an asset should be visible or not, based on its catalog ID.
*
* \see AssetCatalogService::create_filter()
*/
class AssetCatalogFilter {
public:
bool contains(CatalogID asset_catalog_id) const;
protected:
friend AssetCatalogService;
const Set<CatalogID> matching_catalog_ids;
explicit AssetCatalogFilter(Set<CatalogID> &&matching_catalog_ids);
};
} // namespace blender::bke

View File

@@ -0,0 +1,143 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup bke
*/
#pragma once
#ifndef __cplusplus
# error This is a C++ header.
#endif
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh"
#include "BLI_sys_types.h"
#include <string>
namespace blender::bke {
/**
* Location of an Asset Catalog in the catalog tree, denoted by slash-separated path components.
*
* Each path component is a string that is not allowed to have slashes or colons. The latter is to
* make things easy to save in the colon-delimited Catalog Definition File format.
*
* The path of a catalog determines where in the catalog hierarchy the catalog is shown. Examples
* are "Characters/Ellie/Poses/Hand" or "Kit_bash/City/Skyscrapers". The path looks like a
* file-system path, with a few differences:
*
* - Only slashes are used as path component separators.
* - All paths are absolute, so there is no need for a leading slash.
*
* See https://wiki.blender.org/wiki/Source/Architecture/Asset_System/Catalogs
*
* Paths are stored as byte sequences, and assumed to be UTF-8.
*/
class AssetCatalogPath {
friend std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append);
private:
/**
* The path itself, such as "Agents/Secret/327".
*/
std::string path_;
public:
static const char SEPARATOR;
AssetCatalogPath() = delete;
AssetCatalogPath(StringRef path);
AssetCatalogPath(const std::string &path);
AssetCatalogPath(const char *path);
AssetCatalogPath(const AssetCatalogPath &other_path) = default;
AssetCatalogPath(AssetCatalogPath &&other_path) noexcept;
~AssetCatalogPath() = default;
uint64_t hash() const;
uint64_t length() const; /* Length of the path in bytes. */
/** C-string representation of the path. */
const char *c_str() const;
const std::string &str() const;
/* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor.
* Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */
bool operator==(const AssetCatalogPath &other_path) const;
bool operator!=(const AssetCatalogPath &other_path) const;
bool operator<(const AssetCatalogPath &other_path) const;
AssetCatalogPath &operator=(const AssetCatalogPath &other_path) = default;
AssetCatalogPath &operator=(AssetCatalogPath &&other_path) = default;
/** Concatenate two paths, returning the new path. */
AssetCatalogPath operator/(const AssetCatalogPath &path_to_append) const;
/* False when the path is empty, true otherwise. */
operator bool() const;
/**
* Clean up the path. This ensures:
* - Every path component is stripped of its leading/trailing spaces.
* - Empty components (caused by double slashes or leading/trailing slashes) are removed.
* - Invalid characters are replaced with valid ones.
*/
[[nodiscard]] AssetCatalogPath cleanup() const;
/**
* \return true only if the given path is a parent of this catalog's path.
* When this catalog's path is equal to the given path, return true as well.
* In other words, this defines a weak subset.
*
* True: "some/path/there" is contained in "some/path" and "some".
* False: "path/there" is not contained in "some/path/there".
*
* Note that non-cleaned-up paths (so for example starting or ending with a
* slash) are not supported, and result in undefined behavior.
*/
bool is_contained_in(const AssetCatalogPath &other_path) const;
/**
* \return the parent path, or an empty path if there is no parent.
*/
AssetCatalogPath parent() const;
/**
* Change the initial part of the path from `from_path` to `to_path`.
* If this path does not start with `from_path`, return an empty path as result.
*
* Example:
*
* AssetCatalogPath path("some/path/to/some/catalog");
* path.rebase("some/path", "new/base") -> "new/base/to/some/catalog"
*/
AssetCatalogPath rebase(const AssetCatalogPath &from_path,
const AssetCatalogPath &to_path) const;
/** Call the callback function for each path component, in left-to-right order. */
using ComponentIteratorFn = FunctionRef<void(StringRef component_name, bool is_last_component)>;
void iterate_components(ComponentIteratorFn callback) const;
protected:
/** Strip leading/trailing spaces and replace disallowed characters. */
static std::string cleanup_component(StringRef component_name);
};
/** Output the path as string. */
std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append);
} // namespace blender::bke

View File

@@ -21,6 +21,7 @@
#pragma once
struct Main;
//
#ifdef __cplusplus
extern "C" {

View File

@@ -42,66 +42,21 @@ class AttributeIDRef {
const AnonymousAttributeID *anonymous_id_ = nullptr;
public:
AttributeIDRef() = default;
AttributeIDRef();
AttributeIDRef(StringRef name);
AttributeIDRef(StringRefNull name);
AttributeIDRef(const char *name);
AttributeIDRef(const std::string &name);
AttributeIDRef(const AnonymousAttributeID *anonymous_id);
AttributeIDRef(StringRef name) : name_(name)
{
}
AttributeIDRef(StringRefNull name) : name_(name)
{
}
AttributeIDRef(const char *name) : name_(name)
{
}
AttributeIDRef(const std::string &name) : name_(name)
{
}
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id)
{
}
operator bool() const
{
return this->is_named() || this->is_anonymous();
}
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
{
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
}
uint64_t hash() const
{
return get_default_hash_2(name_, anonymous_id_);
}
bool is_named() const
{
return !name_.is_empty();
}
bool is_anonymous() const
{
return anonymous_id_ != nullptr;
}
StringRef name() const
{
BLI_assert(this->is_named());
return name_;
}
const AnonymousAttributeID &anonymous_id() const
{
BLI_assert(this->is_anonymous());
return *anonymous_id_;
}
operator bool() const;
uint64_t hash() const;
bool is_named() const;
bool is_anonymous() const;
StringRef name() const;
const AnonymousAttributeID &anonymous_id() const;
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
};
@@ -259,73 +214,26 @@ class OutputAttribute {
bool save_has_been_called_ = false;
public:
OutputAttribute() = default;
OutputAttribute();
OutputAttribute(OutputAttribute &&other);
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)
{
}
OutputAttribute(OutputAttribute &&other) = default;
const bool ignore_old_values);
~OutputAttribute();
operator bool() const
{
return varray_.get() != nullptr;
}
operator bool() const;
GVMutableArray &operator*()
{
return *varray_;
}
GVMutableArray &operator*();
GVMutableArray *operator->();
GVMutableArray &varray();
AttributeDomain domain() const;
const CPPType &cpp_type() const;
CustomDataType custom_data_type() const;
GVMutableArray *operator->()
{
return varray_.get();
}
GVMutableArray &varray()
{
return *varray_;
}
AttributeDomain domain() const
{
return domain_;
}
const CPPType &cpp_type() const
{
return varray_->type();
}
CustomDataType custom_data_type() const
{
return cpp_type_to_custom_data_type(this->cpp_type());
}
fn::GMutableSpan as_span()
{
if (!optional_span_varray_) {
const bool materialize_old_values = !ignore_old_values_;
optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(*varray_,
materialize_old_values);
}
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
return span_varray;
}
template<typename T> MutableSpan<T> as_span()
{
return this->as_span().typed<T>();
}
fn::GMutableSpan as_span();
template<typename T> MutableSpan<T> as_span();
void save();
};
@@ -336,18 +244,32 @@ class OutputAttribute {
template<typename T> class OutputAttribute_Typed {
private:
OutputAttribute attribute_;
std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
std::unique_ptr<fn::GVMutableArray_Typed<T>> optional_varray_;
VMutableArray<T> *varray_ = nullptr;
public:
OutputAttribute_Typed();
OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
if (attribute_) {
optional_varray_.emplace(attribute_.varray());
optional_varray_ = std::make_unique<fn::GVMutableArray_Typed<T>>(attribute_.varray());
varray_ = &**optional_varray_;
}
}
OutputAttribute_Typed(OutputAttribute_Typed &&other);
~OutputAttribute_Typed();
OutputAttribute_Typed &operator=(OutputAttribute_Typed &&other)
{
if (this == &other) {
return *this;
}
this->~OutputAttribute_Typed();
new (this) OutputAttribute_Typed(std::move(other));
return *this;
}
operator bool() const
{
return varray_ != nullptr;
@@ -394,6 +316,13 @@ template<typename T> class OutputAttribute_Typed {
}
};
/* These are not defined in the class directly, because when defining them there, the external
* template instantiation does not work, resulting in longer compile times. */
template<typename T> inline OutputAttribute_Typed<T>::OutputAttribute_Typed() = default;
template<typename T>
inline OutputAttribute_Typed<T>::OutputAttribute_Typed(OutputAttribute_Typed &&other) = default;
template<typename T> inline OutputAttribute_Typed<T>::~OutputAttribute_Typed() = default;
/**
* A basic container around DNA CustomData so that its users
* don't have to implement special copy and move constructors.
@@ -444,4 +373,139 @@ class CustomDataAttributes {
const AttributeDomain domain) const;
};
/* --------------------------------------------------------------------
* #AttributeIDRef inline methods.
*/
inline AttributeIDRef::AttributeIDRef() = default;
inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name)
{
}
inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name)
{
}
inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name)
{
}
inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name)
{
}
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
: anonymous_id_(anonymous_id)
{
}
inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
{
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
}
inline AttributeIDRef::operator bool() const
{
return this->is_named() || this->is_anonymous();
}
inline uint64_t AttributeIDRef::hash() const
{
return get_default_hash_2(name_, anonymous_id_);
}
inline bool AttributeIDRef::is_named() const
{
return !name_.is_empty();
}
inline bool AttributeIDRef::is_anonymous() const
{
return anonymous_id_ != nullptr;
}
inline StringRef AttributeIDRef::name() const
{
BLI_assert(this->is_named());
return name_;
}
inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const
{
BLI_assert(this->is_anonymous());
return *anonymous_id_;
}
/* --------------------------------------------------------------------
* #OutputAttribute inline methods.
*/
inline OutputAttribute::OutputAttribute() = default;
inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default;
inline OutputAttribute::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)
{
}
inline OutputAttribute::operator bool() const
{
return varray_.get() != nullptr;
}
inline GVMutableArray &OutputAttribute::operator*()
{
return *varray_;
}
inline GVMutableArray *OutputAttribute::operator->()
{
return varray_.get();
}
inline GVMutableArray &OutputAttribute::varray()
{
return *varray_;
}
inline AttributeDomain OutputAttribute::domain() const
{
return domain_;
}
inline const CPPType &OutputAttribute::cpp_type() const
{
return varray_->type();
}
inline CustomDataType OutputAttribute::custom_data_type() const
{
return cpp_type_to_custom_data_type(this->cpp_type());
}
template<typename T> inline MutableSpan<T> OutputAttribute::as_span()
{
return this->as_span().typed<T>();
}
} // namespace blender::bke
/* --------------------------------------------------------------------
* Extern template instantiations that are defined in `intern/extern_implementations.cc`.
*/
namespace blender::bke {
extern template class OutputAttribute_Typed<float>;
extern template class OutputAttribute_Typed<int>;
extern template class OutputAttribute_Typed<float3>;
extern template class OutputAttribute_Typed<bool>;
extern template class OutputAttribute_Typed<ColorGeometry4f>;
} // namespace blender::bke

View File

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

View File

@@ -97,6 +97,7 @@ void BKE_curvemapping_evaluate_premulRGBF(const struct CurveMapping *cumap,
float vecout[3],
const float vecin[3]);
bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
/* non-const, these modify the curve */

View File

@@ -70,10 +70,7 @@ void BKE_curveprofile_reset_view(struct CurveProfile *profile);
void BKE_curveprofile_reset(struct CurveProfile *profile);
void BKE_curveprofile_create_samples(struct CurveProfile *profile,
int n_segments,
bool sample_straight_edges,
struct CurveProfilePoint *r_samples);
int BKE_curveprofile_table_size(const struct CurveProfile *profile);
void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len);
@@ -85,13 +82,6 @@ enum {
};
void BKE_curveprofile_update(struct CurveProfile *profile, const int update_flags);
/* Need to find the total length of the curve to sample a portion of it */
float BKE_curveprofile_total_length(const struct CurveProfile *profile);
void BKE_curveprofile_create_samples_even_spacing(struct CurveProfile *profile,
int n_segments,
struct CurveProfilePoint *r_samples);
/* Length portion is the fraction of the total path length where we want the location */
void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile,
float length_portion,

View File

@@ -253,6 +253,13 @@ struct GeometrySet {
blender::Map<GeometryComponentType, GeometryComponentPtr> components_;
public:
GeometrySet();
GeometrySet(const GeometrySet &other);
GeometrySet(GeometrySet &&other);
~GeometrySet();
GeometrySet &operator=(const GeometrySet &other);
GeometrySet &operator=(GeometrySet &&other);
GeometryComponent &get_component_for_write(GeometryComponentType component_type);
template<typename Component> Component &get_component_for_write()
{

View File

@@ -133,6 +133,9 @@ enum {
LIB_ID_COPY_SHAPEKEY = 1 << 26,
/** EXCEPTION! Specific deep-copy of node trees used e.g. for rendering purposes. */
LIB_ID_COPY_NODETREE_LOCALIZE = 1 << 27,
/** EXCEPTION! Specific handling of RB objects regarding collections differs depending whether we
duplicate scene/collections, or objects. */
LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28,
/* *** Helper 'defines' gathering most common flag sets. *** */
/** Shapekeys are not real ID's, more like local data to geometry IDs... */
@@ -261,7 +264,8 @@ struct ID *BKE_id_copy_ex(struct Main *bmain,
const int flag);
struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
struct ID *id,
const uint duplicate_flags);
const uint duplicate_flags,
const int copy_flags);
void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b);
void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b);

View File

@@ -651,9 +651,8 @@ extern void (*BKE_mesh_batch_cache_free_cb)(struct Mesh *me);
/* Inlines */
/* Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h,
* but I don't want to force every user of BKE_mesh.h to also include that file.
* ~~ Sybren */
/* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h,
* but I don't want to force every user of BKE_mesh.h to also include that file. */
BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly,
const int *index_mp_to_orig,
const int i)

View File

@@ -417,7 +417,7 @@ typedef struct bNodeTreeType {
void (*local_sync)(struct bNodeTree *localtree, struct bNodeTree *ntree);
void (*local_merge)(struct Main *bmain, struct bNodeTree *localtree, struct bNodeTree *ntree);
/* Tree update. Overrides nodetype->updatetreefunc! */
/* Tree update. Overrides `nodetype->updatetreefunc` ! */
void (*update)(struct bNodeTree *ntree);
bool (*validate_link)(struct bNodeTree *ntree, struct bNodeLink *link);
@@ -443,7 +443,7 @@ void ntreeTypeFreeLink(const struct bNodeTreeType *nt);
bool ntreeIsRegistered(struct bNodeTree *ntree);
struct GHashIterator *ntreeTypeGetIterator(void);
/* helper macros for iterating over tree types */
/* Helper macros for iterating over tree types. */
#define NODE_TREE_TYPES_BEGIN(ntype) \
{ \
GHashIterator *__node_tree_type_iter__ = ntreeTypeGetIterator(); \
@@ -548,7 +548,7 @@ void nodeUnregisterType(struct bNodeType *ntype);
bool nodeTypeUndefined(struct bNode *node);
struct GHashIterator *nodeTypeGetIterator(void);
/* helper macros for iterating over node types */
/* Helper macros for iterating over node types. */
#define NODE_TYPES_BEGIN(ntype) \
{ \
GHashIterator *__node_type_iter__ = nodeTypeGetIterator(); \
@@ -574,7 +574,7 @@ const char *nodeStaticSocketType(int type, int subtype);
const char *nodeStaticSocketInterfaceType(int type, int subtype);
const char *nodeStaticSocketLabel(int type, int subtype);
/* helper macros for iterating over node types */
/* Helper macros for iterating over node types. */
#define NODE_SOCKET_TYPES_BEGIN(stype) \
{ \
GHashIterator *__node_socket_type_iter__ = nodeSocketTypeGetIterator(); \
@@ -746,7 +746,8 @@ int BKE_node_clipboard_get_type(void);
/* Node Instance Hash */
typedef struct bNodeInstanceHash {
GHash *ghash; /* XXX should be made a direct member, GHash allocation needs to support it */
/** XXX should be made a direct member, #GHash allocation needs to support it */
GHash *ghash;
} bNodeInstanceHash;
typedef void (*bNodeInstanceValueFP)(void *value);
@@ -1102,6 +1103,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree,
#define SH_NODE_VERTEX_COLOR 706
#define SH_NODE_OUTPUT_AOV 707
#define SH_NODE_VECTOR_ROTATE 708
#define SH_NODE_CURVE_FLOAT 709
/* custom defines options for Material node */
// #define SH_NODE_MAT_DIFF 1
@@ -1346,7 +1348,7 @@ void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
const bNode *node,
char *r_prefix,
size_t prefix_len);
/* Update the runtime layer names with the cryptomatte layer names of the references
/* Update the runtime layer names with the crypto-matte layer names of the references
* render layer or image. */
void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node);
struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node);
@@ -1507,7 +1509,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_POINTS_TO_VERTICES 1094
#define GEO_NODE_CURVE_REVERSE 1095
#define GEO_NODE_PROXIMITY 1096
#define GEO_NODE_CURVE_SUBDIVIDE 1097
#define GEO_NODE_INPUT_SPLINE_LENGTH 1098
#define GEO_NODE_CURVE_SPLINE_TYPE 1099
#define GEO_NODE_CURVE_SET_HANDLES 1100
#define GEO_NODE_POINTS_TO_VOLUME 1101
#define GEO_NODE_CURVE_HANDLE_TYPE_SELECTION 1102
/** \} */
/* -------------------------------------------------------------------- */
@@ -1525,6 +1532,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_STRING_SUBSTRING 1212
#define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213
#define FN_NODE_RANDOM_VALUE 1214
#define FN_NODE_ROTATE_EULER 1215
/** \} */

View File

@@ -85,6 +85,7 @@ set(SRC
intern/armature_update.c
intern/asset.cc
intern/asset_catalog.cc
intern/asset_catalog_path.cc
intern/asset_library.cc
intern/attribute.c
intern/attribute_access.cc
@@ -119,7 +120,7 @@ set(SRC
intern/curve_deform.c
intern/curve_eval.cc
intern/curve_to_mesh_convert.cc
intern/curveprofile.c
intern/curveprofile.cc
intern/customdata.c
intern/customdata_file.c
intern/data_transfer.c
@@ -133,6 +134,7 @@ set(SRC
intern/editmesh_cache.c
intern/editmesh_tangent.c
intern/effect.c
intern/extern_implementations.cc
intern/fcurve.c
intern/fcurve_cache.c
intern/fcurve_driver.c
@@ -306,6 +308,7 @@ set(SRC
BKE_armature.hh
BKE_asset.h
BKE_asset_catalog.hh
BKE_asset_catalog_path.hh
BKE_asset_library.h
BKE_asset_library.hh
BKE_attribute.h
@@ -789,6 +792,7 @@ if(WITH_GTESTS)
intern/action_test.cc
intern/armature_test.cc
intern/asset_catalog_test.cc
intern/asset_catalog_path_test.cc
intern/asset_library_test.cc
intern/asset_test.cc
intern/cryptomatte_test.cc

View File

@@ -38,7 +38,6 @@
namespace blender::bke {
const char AssetCatalogService::PATH_SEPARATOR = '/';
const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt";
/* For now this is the only version of the catalog definition files that is supported.
@@ -66,16 +65,16 @@ bool AssetCatalogService::is_empty() const
return catalogs_.is_empty();
}
AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id)
AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const
{
std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
const std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
if (catalog_uptr_ptr == nullptr) {
return nullptr;
}
return catalog_uptr_ptr->get();
}
AssetCatalog *AssetCatalogService::find_catalog_by_path(const CatalogPath &path) const
AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const
{
for (const auto &catalog : catalogs_.values()) {
if (catalog->path == path) {
@@ -86,6 +85,33 @@ AssetCatalog *AssetCatalogService::find_catalog_by_path(const CatalogPath &path)
return nullptr;
}
AssetCatalogFilter AssetCatalogService::create_catalog_filter(
const CatalogID active_catalog_id) const
{
Set<CatalogID> matching_catalog_ids;
matching_catalog_ids.add(active_catalog_id);
const AssetCatalog *active_catalog = find_catalog(active_catalog_id);
if (!active_catalog) {
/* If the UUID is unknown (i.e. not mapped to an actual Catalog), it is impossible to determine
* its children. The filter can still work on the given UUID. */
return AssetCatalogFilter(std::move(matching_catalog_ids));
}
/* This cannot just iterate over tree items to get all the required data, because tree items only
* represent single UUIDs. It could be used to get the main UUIDs of the children, though, and
* then only do an exact match on the path (instead of the more complex `is_contained_in()`
* call). Without an extra indexed-by-path acceleration structure, this is still going to require
* a linear search, though. */
for (const auto &catalog_uptr : this->catalogs_.values()) {
if (catalog_uptr->path.is_contained_in(active_catalog->path)) {
matching_catalog_ids.add(catalog_uptr->catalog_id);
}
}
return AssetCatalogFilter(std::move(matching_catalog_ids));
}
void AssetCatalogService::delete_catalog(CatalogID catalog_id)
{
std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
@@ -108,25 +134,25 @@ void AssetCatalogService::delete_catalog(CatalogID catalog_id)
}
void AssetCatalogService::update_catalog_path(CatalogID catalog_id,
const CatalogPath &new_catalog_path)
const AssetCatalogPath &new_catalog_path)
{
AssetCatalog *renamed_cat = this->find_catalog(catalog_id);
const CatalogPath old_cat_path = renamed_cat->path;
const AssetCatalogPath old_cat_path = renamed_cat->path;
for (auto &catalog_uptr : catalogs_.values()) {
AssetCatalog *cat = catalog_uptr.get();
if (!cat->is_contained_in(old_cat_path)) {
const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path);
if (!new_path) {
continue;
}
const CatalogPath path_suffix = cat->path.substr(old_cat_path.length());
cat->path = new_catalog_path + path_suffix;
cat->path = new_path;
}
this->rebuild_tree();
}
AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_path)
AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalog_path)
{
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path(catalog_path);
@@ -145,11 +171,8 @@ AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_pat
catalog_definition_file_->add_new(catalog_ptr);
}
/* The tree may not exist; this happens when no catalog definition file has been loaded yet. When
* the tree is created any in-memory catalogs will be added, so it doesn't need to happen now. */
if (catalog_tree_) {
catalog_tree_->insert_item(*catalog_ptr);
}
BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist.");
catalog_tree_->insert_item(*catalog_ptr);
return catalog_ptr;
}
@@ -189,7 +212,7 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director
/* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */
catalog_tree_ = read_into_tree();
rebuild_tree();
}
void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path)
@@ -319,8 +342,7 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
/* - There's no definition file next to the .blend file.
* -> Ask the asset library API for an appropriate location. */
char suitable_root_path[PATH_MAX];
BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(),
suitable_root_path);
BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(), suitable_root_path);
char asset_lib_cdf_path[PATH_MAX];
BLI_path_join(asset_lib_cdf_path,
sizeof(asset_lib_cdf_path),
@@ -358,15 +380,55 @@ std::unique_ptr<AssetCatalogTree> AssetCatalogService::read_into_tree()
void AssetCatalogService::rebuild_tree()
{
create_missing_catalogs();
this->catalog_tree_ = read_into_tree();
}
void AssetCatalogService::create_missing_catalogs()
{
/* Construct an ordered set of paths to check, so that parents are ordered before children. */
std::set<AssetCatalogPath> paths_to_check;
for (auto &catalog : catalogs_.values()) {
paths_to_check.insert(catalog->path);
}
std::set<AssetCatalogPath> seen_paths;
/* The empty parent should never be created, so always be considered "seen". */
seen_paths.insert(AssetCatalogPath(""));
/* Find and create missing direct parents (so ignoring parents-of-parents). */
while (!paths_to_check.empty()) {
/* Pop the first path of the queue. */
const AssetCatalogPath path = *paths_to_check.begin();
paths_to_check.erase(paths_to_check.begin());
if (seen_paths.find(path) != seen_paths.end()) {
/* This path has been seen already, so it can be ignored. */
continue;
}
seen_paths.insert(path);
const AssetCatalogPath parent_path = path.parent();
if (seen_paths.find(parent_path) != seen_paths.end()) {
/* The parent exists, continue to the next path. */
continue;
}
/* The parent doesn't exist, so create it and queue it up for checking its parent. */
create_catalog(parent_path);
paths_to_check.insert(parent_path);
}
/* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */
}
/* ---------------------------------------------------------------------- */
AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
CatalogID catalog_id,
StringRef simple_name,
const AssetCatalogTreeItem *parent)
: name_(name), catalog_id_(catalog_id), parent_(parent)
: name_(name), catalog_id_(catalog_id), simple_name_(simple_name), parent_(parent)
{
}
@@ -375,16 +437,21 @@ CatalogID AssetCatalogTreeItem::get_catalog_id() const
return catalog_id_;
}
StringRef AssetCatalogTreeItem::get_name() const
StringRefNull AssetCatalogTreeItem::get_name() const
{
return name_;
}
CatalogPath AssetCatalogTreeItem::catalog_path() const
StringRefNull AssetCatalogTreeItem::get_simple_name() const
{
std::string current_path = name_;
return simple_name_;
}
AssetCatalogPath AssetCatalogTreeItem::catalog_path() const
{
AssetCatalogPath current_path = name_;
for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
current_path = parent->name_ + AssetCatalogService::PATH_SEPARATOR + current_path;
current_path = AssetCatalogPath(parent->name_) / current_path;
}
return current_path;
}
@@ -405,32 +472,6 @@ bool AssetCatalogTreeItem::has_children() const
/* ---------------------------------------------------------------------- */
/**
* Iterate over path components, calling \a callback for each component. E.g. "just/some/path"
* iterates over "just", then "some" then "path".
*/
static void iterate_over_catalog_path_components(
const CatalogPath &path,
FunctionRef<void(StringRef component_name, bool is_last_component)> callback)
{
const char *next_slash_ptr;
for (const char *path_component = path.data(); path_component && path_component[0];
/* Jump to one after the next slash if there is any. */
path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) {
next_slash_ptr = BLI_path_slash_find(path_component);
const bool is_last_component = next_slash_ptr == nullptr;
/* Note that this won't be null terminated. */
const StringRef component_name = is_last_component ?
path_component :
StringRef(path_component,
next_slash_ptr - path_component);
callback(component_name, is_last_component);
}
}
void AssetCatalogTree::insert_item(const AssetCatalog &catalog)
{
const AssetCatalogTreeItem *parent = nullptr;
@@ -438,30 +479,31 @@ void AssetCatalogTree::insert_item(const AssetCatalog &catalog)
* added to (if not there yet). */
AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_;
BLI_assert_msg(!ELEM(catalog.path[0], '/', '\\'),
BLI_assert_msg(!ELEM(catalog.path.str()[0], '/', '\\'),
"Malformed catalog path; should not start with a separator");
const CatalogID nil_id{};
iterate_over_catalog_path_components(
catalog.path, [&](StringRef component_name, const bool is_last_component) {
/* Insert new tree element - if no matching one is there yet! */
auto [key_and_item, was_inserted] = current_item_children->emplace(
component_name,
AssetCatalogTreeItem(
component_name, is_last_component ? catalog.catalog_id : nil_id, parent));
AssetCatalogTreeItem &item = key_and_item->second;
catalog.path.iterate_components([&](StringRef component_name, const bool is_last_component) {
/* Insert new tree element - if no matching one is there yet! */
auto [key_and_item, was_inserted] = current_item_children->emplace(
component_name,
AssetCatalogTreeItem(component_name,
is_last_component ? catalog.catalog_id : nil_id,
is_last_component ? catalog.simple_name : "",
parent));
AssetCatalogTreeItem &item = key_and_item->second;
/* If full path of this catalog already exists as parent path of a previously read catalog,
* we can ensure this tree item's UUID is set here. */
if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) {
item.catalog_id_ = catalog.catalog_id;
}
/* If full path of this catalog already exists as parent path of a previously read catalog,
* we can ensure this tree item's UUID is set here. */
if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) {
item.catalog_id_ = catalog.catalog_id;
}
/* Walk further into the path (no matter if a new item was created or not). */
parent = &item;
current_item_children = &item.children_;
});
/* Walk further into the path (no matter if a new item was created or not). */
parent = &item;
current_item_children = &item.children_;
});
}
void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback)
@@ -592,7 +634,7 @@ std::unique_ptr<AssetCatalog> AssetCatalogDefinitionFile::parse_catalog_line(con
const StringRef path_and_simple_name = line.substr(first_delim + 1);
const int64_t second_delim = path_and_simple_name.find_first_of(delim);
CatalogPath catalog_path;
std::string path_in_file;
std::string simple_name;
if (second_delim == 0) {
/* Delimiter as first character means there is no path. These lines are to be ignored. */
@@ -601,16 +643,16 @@ std::unique_ptr<AssetCatalog> AssetCatalogDefinitionFile::parse_catalog_line(con
if (second_delim == StringRef::not_found) {
/* No delimiter means no simple name, just treat it as all "path". */
catalog_path = path_and_simple_name;
path_in_file = path_and_simple_name;
simple_name = "";
}
else {
catalog_path = path_and_simple_name.substr(0, second_delim);
path_in_file = path_and_simple_name.substr(0, second_delim);
simple_name = path_and_simple_name.substr(second_delim + 1).trim();
}
catalog_path = AssetCatalog::cleanup_path(catalog_path);
return std::make_unique<AssetCatalog>(catalog_id, catalog_path, simple_name);
AssetCatalogPath catalog_path = path_in_file;
return std::make_unique<AssetCatalog>(catalog_id, catalog_path.cleanup(), simple_name);
}
bool AssetCatalogDefinitionFile::write_to_disk() const
@@ -716,25 +758,25 @@ bool AssetCatalogDefinitionFile::ensure_directory_exists(
}
AssetCatalog::AssetCatalog(const CatalogID catalog_id,
const CatalogPath &path,
const AssetCatalogPath &path,
const std::string &simple_name)
: catalog_id(catalog_id), path(path), simple_name(simple_name)
{
}
std::unique_ptr<AssetCatalog> AssetCatalog::from_path(const CatalogPath &path)
std::unique_ptr<AssetCatalog> AssetCatalog::from_path(const AssetCatalogPath &path)
{
const CatalogPath clean_path = cleanup_path(path);
const AssetCatalogPath clean_path = path.cleanup();
const CatalogID cat_id = BLI_uuid_generate_random();
const std::string simple_name = sensible_simple_name_for_path(clean_path);
auto catalog = std::make_unique<AssetCatalog>(cat_id, clean_path, simple_name);
return catalog;
}
std::string AssetCatalog::sensible_simple_name_for_path(const CatalogPath &path)
std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath &path)
{
std::string name = path;
std::replace(name.begin(), name.end(), AssetCatalogService::PATH_SEPARATOR, '-');
std::string name = path.str();
std::replace(name.begin(), name.end(), AssetCatalogPath::SEPARATOR, '-');
if (name.length() < MAX_NAME - 1) {
return name;
}
@@ -744,33 +786,14 @@ std::string AssetCatalog::sensible_simple_name_for_path(const CatalogPath &path)
return "..." + name.substr(name.length() - 60);
}
CatalogPath AssetCatalog::cleanup_path(const CatalogPath &path)
AssetCatalogFilter::AssetCatalogFilter(Set<CatalogID> &&matching_catalog_ids)
: matching_catalog_ids(std::move(matching_catalog_ids))
{
/* TODO(@sybren): maybe go over each element of the path, and trim those? */
CatalogPath clean_path = StringRef(path).trim().trim(AssetCatalogService::PATH_SEPARATOR).trim();
return clean_path;
}
bool AssetCatalog::is_contained_in(const CatalogPath &other_path) const
bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const
{
if (other_path.empty()) {
return true;
}
if (this->path == other_path) {
return true;
}
/* To be a child path of 'other_path', our path must be at least a separator and another
* character longer. */
if (this->path.length() < other_path.length() + 2) {
return false;
}
const StringRef this_path(this->path);
const bool prefix_ok = this_path.startswith(other_path);
const char next_char = this_path[other_path.length()];
return prefix_ok && next_char == AssetCatalogService::PATH_SEPARATOR;
return matching_catalog_ids.contains(asset_catalog_id);
}
} // namespace blender::bke

View File

@@ -0,0 +1,228 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup bke
*/
#include "BKE_asset_catalog_path.hh"
#include "BLI_path_util.h"
namespace blender::bke {
const char AssetCatalogPath::SEPARATOR = '/';
AssetCatalogPath::AssetCatalogPath(const std::string &path) : path_(path)
{
}
AssetCatalogPath::AssetCatalogPath(StringRef path) : path_(path)
{
}
AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path)
{
}
AssetCatalogPath::AssetCatalogPath(AssetCatalogPath &&other_path) noexcept
: path_(std::move(other_path.path_))
{
}
uint64_t AssetCatalogPath::hash() const
{
std::hash<std::string> hasher{};
return hasher(this->path_);
}
uint64_t AssetCatalogPath::length() const
{
return this->path_.length();
}
const char *AssetCatalogPath::c_str() const
{
return this->path_.c_str();
}
const std::string &AssetCatalogPath::str() const
{
return this->path_;
}
/* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor.
* Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */
bool AssetCatalogPath::operator==(const AssetCatalogPath &other_path) const
{
return this->path_ == other_path.path_;
}
bool AssetCatalogPath::operator!=(const AssetCatalogPath &other_path) const
{
return !(*this == other_path);
}
bool AssetCatalogPath::operator<(const AssetCatalogPath &other_path) const
{
return this->path_ < other_path.path_;
}
AssetCatalogPath AssetCatalogPath::operator/(const AssetCatalogPath &path_to_append) const
{
/* `"" / "path"` or `"path" / ""` should just result in `"path"` */
if (!*this) {
return path_to_append;
}
if (!path_to_append) {
return *this;
}
std::stringstream new_path;
new_path << this->path_ << SEPARATOR << path_to_append.path_;
return AssetCatalogPath(new_path.str());
}
AssetCatalogPath::operator bool() const
{
return !this->path_.empty();
}
std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append)
{
stream << path_to_append.path_;
return stream;
}
AssetCatalogPath AssetCatalogPath::cleanup() const
{
std::stringstream clean_components;
bool first_component_seen = false;
this->iterate_components([&clean_components, &first_component_seen](StringRef component_name,
bool /*is_last_component*/) {
const std::string clean_component = cleanup_component(component_name);
if (clean_component.empty()) {
/* These are caused by leading, trailing, or double slashes. */
return;
}
/* If a previous path component has been streamed already, we need a path separator. This
* cannot use the `is_last_component` boolean, because the last component might be skipped due
* to the condition above. */
if (first_component_seen) {
clean_components << SEPARATOR;
}
first_component_seen = true;
clean_components << clean_component;
});
return AssetCatalogPath(clean_components.str());
}
std::string AssetCatalogPath::cleanup_component(StringRef component)
{
std::string cleaned = component.trim();
/* Replace colons with something else, as those are used in the CDF file as delimiter. */
std::replace(cleaned.begin(), cleaned.end(), ':', '-');
return cleaned;
}
bool AssetCatalogPath::is_contained_in(const AssetCatalogPath &other_path) const
{
if (!other_path) {
/* The empty path contains all other paths. */
return true;
}
if (this->path_ == other_path.path_) {
/* Weak is-in relation: equal paths contain each other. */
return true;
}
/* To be a child path of 'other_path', our path must be at least a separator and another
* character longer. */
if (this->length() < other_path.length() + 2) {
return false;
}
/* Create StringRef to be able to use .startswith(). */
const StringRef this_path(this->path_);
const bool prefix_ok = this_path.startswith(other_path.path_);
const char next_char = this_path[other_path.length()];
return prefix_ok && next_char == SEPARATOR;
}
AssetCatalogPath AssetCatalogPath::parent() const
{
if (!*this) {
return AssetCatalogPath("");
}
std::string::size_type last_sep_index = this->path_.rfind(SEPARATOR);
if (last_sep_index == std::string::npos) {
return AssetCatalogPath("");
}
return AssetCatalogPath(this->path_.substr(0, last_sep_index));
}
void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const
{
const char *next_slash_ptr;
for (const char *path_component = this->path_.data(); path_component && path_component[0];
/* Jump to one after the next slash if there is any. */
path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) {
next_slash_ptr = BLI_path_slash_find(path_component);
const bool is_last_component = next_slash_ptr == nullptr;
/* Note that this won't be null terminated. */
const StringRef component_name = is_last_component ?
path_component :
StringRef(path_component,
next_slash_ptr - path_component);
callback(component_name, is_last_component);
}
}
AssetCatalogPath AssetCatalogPath::rebase(const AssetCatalogPath &from_path,
const AssetCatalogPath &to_path) const
{
if (!from_path) {
if (!to_path) {
return AssetCatalogPath("");
}
return to_path / *this;
}
if (!this->is_contained_in(from_path)) {
return AssetCatalogPath("");
}
if (*this == from_path) {
/* Early return, because otherwise the length+1 below is going to cause problems. */
return to_path;
}
/* When from_path = "test", we need to skip "test/" to get the rest of the path, hence the +1. */
const StringRef suffix = StringRef(this->path_).substr(from_path.length() + 1);
const AssetCatalogPath path_suffix(suffix);
return to_path / path_suffix;
}
} // namespace blender::bke

View File

@@ -0,0 +1,251 @@
/*
* 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.
*
* The Original Code is Copyright (C) 2020 Blender Foundation
* All rights reserved.
*/
#include "BKE_asset_catalog_path.hh"
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include <set>
#include <sstream>
#include "testing/testing.h"
namespace blender::bke::tests {
TEST(AssetCatalogPathTest, construction)
{
AssetCatalogPath from_char_literal("the/path");
const std::string str_const = "the/path";
AssetCatalogPath from_string_constant(str_const);
std::string str_variable = "the/path";
AssetCatalogPath from_string_variable(str_variable);
std::string long_string = "this is a long/string/with/a/path in the middle";
StringRef long_string_ref(long_string);
StringRef middle_bit = long_string_ref.substr(10, 23);
AssetCatalogPath from_string_ref(middle_bit);
EXPECT_EQ(from_string_ref, "long/string/with/a/path");
}
TEST(AssetCatalogPathTest, length)
{
const AssetCatalogPath one("1");
EXPECT_EQ(1, one.length());
const AssetCatalogPath empty("");
EXPECT_EQ(0, empty.length());
const AssetCatalogPath utf8("some/родитель");
EXPECT_EQ(21, utf8.length()) << "13 characters should be 21 bytes.";
}
TEST(AssetCatalogPathTest, comparison_operators)
{
const AssetCatalogPath empty("");
const AssetCatalogPath the_path("the/path");
const AssetCatalogPath the_path_child("the/path/child");
const AssetCatalogPath unrelated_path("unrelated/path");
const AssetCatalogPath other_instance_same_path("the/path");
EXPECT_LT(empty, the_path);
EXPECT_LT(the_path, the_path_child);
EXPECT_LT(the_path, unrelated_path);
EXPECT_EQ(empty, empty) << "Identical empty instances should compare equal.";
EXPECT_EQ(empty, "") << "Comparison to empty string should be possible.";
EXPECT_EQ(the_path, the_path) << "Identical non-empty instances should compare equal.";
EXPECT_EQ(the_path, "the/path") << "Comparison to string should be possible.";
EXPECT_EQ(the_path, other_instance_same_path)
<< "Different instances with equal path should compare equal.";
EXPECT_NE(the_path, the_path_child);
EXPECT_NE(the_path, unrelated_path);
EXPECT_NE(the_path, empty);
EXPECT_FALSE(empty);
EXPECT_TRUE(the_path);
}
TEST(AssetCatalogPathTest, move_semantics)
{
AssetCatalogPath source_path("source/path");
EXPECT_TRUE(source_path);
AssetCatalogPath dest_path = std::move(source_path);
EXPECT_FALSE(source_path); /* NOLINT: bugprone-use-after-move */
EXPECT_TRUE(dest_path);
}
TEST(AssetCatalogPathTest, concatenation)
{
AssetCatalogPath some_parent("some/родитель");
AssetCatalogPath child = some_parent / "ребенок";
EXPECT_EQ(some_parent, "some/родитель")
<< "Appending a child path should not modify the parent.";
EXPECT_EQ(child, "some/родитель/ребенок");
AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук";
EXPECT_EQ(appended_compound_path, "some/родитель/ребенок/внук");
AssetCatalogPath empty("");
AssetCatalogPath child_of_the_void = empty / "child";
EXPECT_EQ(child_of_the_void, "child")
<< "Appending to an empty path should not create an initial slash.";
AssetCatalogPath parent_of_the_void = some_parent / empty;
EXPECT_EQ(parent_of_the_void, "some/родитель")
<< "Prepending to an empty path should not create a trailing slash.";
std::string subpath = "child";
AssetCatalogPath concatenated_with_string = some_parent / subpath;
EXPECT_EQ(concatenated_with_string, "some/родитель/child");
}
TEST(AssetCatalogPathTest, hashable)
{
AssetCatalogPath path("heyyyyy");
std::set<AssetCatalogPath> path_std_set;
path_std_set.insert(path);
blender::Set<AssetCatalogPath> path_blender_set;
path_blender_set.add(path);
}
TEST(AssetCatalogPathTest, stream_operator)
{
AssetCatalogPath path("путь/в/Пермь");
std::stringstream sstream;
sstream << path;
EXPECT_EQ("путь/в/Пермь", sstream.str());
}
TEST(AssetCatalogPathTest, is_contained_in)
{
const AssetCatalogPath catpath("simple/path/child");
EXPECT_FALSE(catpath.is_contained_in("unrelated"));
EXPECT_FALSE(catpath.is_contained_in("sim"));
EXPECT_FALSE(catpath.is_contained_in("simple/pathx"));
EXPECT_FALSE(catpath.is_contained_in("simple/path/c"));
EXPECT_FALSE(catpath.is_contained_in("simple/path/child/grandchild"));
EXPECT_FALSE(catpath.is_contained_in("simple/path/"))
<< "Non-normalized paths are not expected to work.";
EXPECT_TRUE(catpath.is_contained_in(""));
EXPECT_TRUE(catpath.is_contained_in("simple"));
EXPECT_TRUE(catpath.is_contained_in("simple/path"));
/* Test with some UTF8 non-ASCII characters. */
AssetCatalogPath some_parent("some/родитель");
AssetCatalogPath child = some_parent / "ребенок";
EXPECT_TRUE(child.is_contained_in(some_parent));
EXPECT_TRUE(child.is_contained_in("some"));
AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук";
EXPECT_TRUE(appended_compound_path.is_contained_in(some_parent));
EXPECT_TRUE(appended_compound_path.is_contained_in(child));
/* Test "going up" directory-style. */
AssetCatalogPath child_with_dotdot = some_parent / "../../other/hierarchy/part";
EXPECT_TRUE(child_with_dotdot.is_contained_in(some_parent))
<< "dotdot path components should have no meaning";
}
TEST(AssetCatalogPathTest, cleanup)
{
AssetCatalogPath ugly_path("/ some / родитель / ");
AssetCatalogPath clean_path = ugly_path.cleanup();
EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path)
<< "cleanup should not modify the path instance itself";
EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path);
AssetCatalogPath double_slashed("some//родитель");
EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup());
AssetCatalogPath with_colons("some/key:subkey=value/path");
EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup());
}
TEST(AssetCatalogPathTest, iterate_components)
{
AssetCatalogPath path("путь/в/Пермь");
Vector<std::pair<std::string, bool>> seen_components;
path.iterate_components([&seen_components](StringRef component_name, bool is_last_component) {
std::pair<std::string, bool> parameter_pair = std::make_pair<std::string, bool>(
component_name, bool(is_last_component));
seen_components.append(parameter_pair);
});
ASSERT_EQ(3, seen_components.size());
EXPECT_EQ("путь", seen_components[0].first);
EXPECT_EQ("в", seen_components[1].first);
EXPECT_EQ("Пермь", seen_components[2].first);
EXPECT_FALSE(seen_components[0].second);
EXPECT_FALSE(seen_components[1].second);
EXPECT_TRUE(seen_components[2].second);
}
TEST(AssetCatalogPathTest, rebase)
{
AssetCatalogPath path("some/path/to/some/catalog");
EXPECT_EQ(path.rebase("some/path", "new/base"), "new/base/to/some/catalog");
EXPECT_EQ(path.rebase("", "new/base"), "new/base/some/path/to/some/catalog");
EXPECT_EQ(path.rebase("some/path/to/some/catalog", "some/path/to/some/catalog"),
"some/path/to/some/catalog")
<< "Rebasing to itself should not change the path.";
EXPECT_EQ(path.rebase("path/to", "new/base"), "")
<< "Non-matching base path should return empty string to indicate 'NO'.";
/* Empty strings should be handled without crashing or other nasty side-effects. */
AssetCatalogPath empty("");
EXPECT_EQ(empty.rebase("path/to", "new/base"), "");
EXPECT_EQ(empty.rebase("", "new/base"), "new/base");
EXPECT_EQ(empty.rebase("", ""), "");
}
TEST(AssetCatalogPathTest, parent)
{
const AssetCatalogPath ascii_path("path/with/missing/parents");
EXPECT_EQ(ascii_path.parent(), "path/with/missing");
const AssetCatalogPath path("путь/в/Пермь/долог/и/далек");
EXPECT_EQ(path.parent(), "путь/в/Пермь/долог/и");
EXPECT_EQ(path.parent().parent(), "путь/в/Пермь/долог");
EXPECT_EQ(path.parent().parent().parent(), "путь/в/Пермь");
const AssetCatalogPath one_level("one");
EXPECT_EQ(one_level.parent(), "");
const AssetCatalogPath empty("");
EXPECT_EQ(empty.parent(), "");
}
} // namespace blender::bke::tests

View File

@@ -57,6 +57,22 @@ class TestableAssetCatalogService : public AssetCatalogService {
{
return catalog_definition_file_.get();
}
void create_missing_catalogs()
{
AssetCatalogService::create_missing_catalogs();
}
int64_t count_catalogs_with_path(const CatalogFilePath &path)
{
int64_t count = 0;
for (auto &catalog_uptr : catalogs_.values()) {
if (catalog_uptr->path == path) {
count++;
}
}
return count;
}
};
class AssetCatalogTest : public testing::Test {
@@ -106,7 +122,7 @@ class AssetCatalogTest : public testing::Test {
EXPECT_EQ(expected_filename, actual_item.get_name());
/* Does the computed number of parents match? */
EXPECT_EQ(expected_path.parent_count, actual_item.count_parents());
EXPECT_EQ(expected_path.name, actual_item.catalog_path());
EXPECT_EQ(expected_path.name, actual_item.catalog_path().str());
}
/**
@@ -186,21 +202,21 @@ TEST_F(AssetCatalogTest, load_single_file)
AssetCatalog *poses_ellie = service.find_catalog(UUID_POSES_ELLIE);
ASSERT_NE(nullptr, poses_ellie);
EXPECT_EQ(UUID_POSES_ELLIE, poses_ellie->catalog_id);
EXPECT_EQ("character/Ellie/poselib", poses_ellie->path);
EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str());
EXPECT_EQ("POSES_ELLIE", poses_ellie->simple_name);
/* Test white-space stripping and support in the path. */
AssetCatalog *poses_whitespace = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE);
ASSERT_NE(nullptr, poses_whitespace);
EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, poses_whitespace->catalog_id);
EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path);
EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path.str());
EXPECT_EQ("POSES_ELLIE WHITESPACE", poses_whitespace->simple_name);
/* Test getting a UTF-8 catalog ID. */
AssetCatalog *poses_ruzena = service.find_catalog(UUID_POSES_RUZENA);
ASSERT_NE(nullptr, poses_ruzena);
EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id);
EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path);
EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path.str());
EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name);
}
@@ -429,7 +445,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf)
const AssetCatalog *cat = service.create_catalog("some/catalog/path");
const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend";
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path);
/* Test that the CDF was created in the expected location. */
@@ -456,7 +472,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory)
const AssetCatalog *cat = service.create_catalog("some/catalog/path");
const CatalogFilePath blendfilename = target_dir + "some_file.blend";
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
/* Test that the CDF was created in the expected location. */
const CatalogFilePath expected_cdf_path = target_dir +
@@ -489,7 +505,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_me
/* Mock that the blend file is written to a subdirectory of the asset library. */
const CatalogFilePath blendfilename = target_dir + "some_file.blend";
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
/* Test that the CDF still exists in the expected location. */
const CatalogFilePath backup_filename = writable_cdf_file + "~";
@@ -534,7 +550,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib)
const AssetCatalog *cat = service.create_catalog("some/catalog/path");
/* Mock that the blend file is written to the directory already containing a CDF. */
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
/* Test that the CDF still exists in the expected location. */
EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str()));
@@ -588,7 +604,7 @@ TEST_F(AssetCatalogTest, create_first_catalog_from_scratch)
AssetCatalog *written_cat = loaded_service.find_catalog(cat->catalog_id);
ASSERT_NE(nullptr, written_cat);
EXPECT_EQ(written_cat->catalog_id, cat->catalog_id);
EXPECT_EQ(written_cat->path, cat->path);
EXPECT_EQ(written_cat->path, cat->path.str());
}
TEST_F(AssetCatalogTest, create_catalog_after_loading_file)
@@ -640,7 +656,7 @@ TEST_F(AssetCatalogTest, create_catalog_path_cleanup)
AssetCatalog *cat = service.create_catalog(" /some/path / ");
EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id));
EXPECT_EQ("some/path", cat->path);
EXPECT_EQ("some/path", cat->path.str());
EXPECT_EQ("some-path", cat->simple_name);
}
@@ -652,7 +668,7 @@ TEST_F(AssetCatalogTest, create_catalog_simple_name)
EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id));
EXPECT_EQ("production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands",
cat->path);
cat->path.str());
EXPECT_EQ("...ht-Characters-Victora-Pose Library-Approved-Body Parts-Hands", cat->simple_name);
}
@@ -718,7 +734,7 @@ TEST_F(AssetCatalogTest, update_catalog_path)
AssetCatalogService::DEFAULT_CATALOG_FILENAME);
const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA);
const CatalogPath orig_path = orig_cat->path;
const AssetCatalogPath orig_path = orig_cat->path;
service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena");
@@ -733,12 +749,12 @@ TEST_F(AssetCatalogTest, update_catalog_path)
EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id)
<< "Changing the path should not change the catalog ID.";
EXPECT_EQ("charlib/Ružena", renamed_cat->path)
EXPECT_EQ("charlib/Ružena", renamed_cat->path.str())
<< "Changing the path should change the path. Surprise.";
EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path)
EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str())
<< "Changing the path should update children.";
EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path)
EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str())
<< "Changing the path should update children.";
}
@@ -775,7 +791,7 @@ TEST_F(AssetCatalogTest, merge_catalog_files)
/* When there are overlaps, the in-memory (i.e. last-saved) paths should win. */
const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE);
EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path);
EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path.str());
}
TEST_F(AssetCatalogTest, backups)
@@ -846,21 +862,104 @@ TEST_F(AssetCatalogTest, order_by_path)
}
}
TEST_F(AssetCatalogTest, is_contained_in)
TEST_F(AssetCatalogTest, create_missing_catalogs)
{
const AssetCatalog cat(BLI_uuid_generate_random(), "simple/path/child", "");
TestableAssetCatalogService new_service;
new_service.create_catalog("path/with/missing/parents");
EXPECT_FALSE(cat.is_contained_in("unrelated"));
EXPECT_FALSE(cat.is_contained_in("sim"));
EXPECT_FALSE(cat.is_contained_in("simple/pathx"));
EXPECT_FALSE(cat.is_contained_in("simple/path/c"));
EXPECT_FALSE(cat.is_contained_in("simple/path/child/grandchild"));
EXPECT_FALSE(cat.is_contained_in("simple/path/"))
<< "Non-normalized paths are not expected to work.";
EXPECT_EQ(nullptr, new_service.find_catalog_by_path("path/with/missing"))
<< "Missing parents should not be immediately created.";
EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) << "Empty path should never be valid";
EXPECT_TRUE(cat.is_contained_in(""));
EXPECT_TRUE(cat.is_contained_in("simple"));
EXPECT_TRUE(cat.is_contained_in("simple/path"));
new_service.create_missing_catalogs();
EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with/missing"));
EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with"));
EXPECT_NE(nullptr, new_service.find_catalog_by_path("path"));
EXPECT_EQ(nullptr, new_service.find_catalog_by_path(""))
<< "Empty path should never be valid, even when after missing catalogs";
}
TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading)
{
TestableAssetCatalogService loaded_service(asset_library_root_);
loaded_service.load_from_disk();
const AssetCatalog *cat_char = loaded_service.find_catalog_by_path("character");
const AssetCatalog *cat_ellie = loaded_service.find_catalog_by_path("character/Ellie");
const AssetCatalog *cat_ruzena = loaded_service.find_catalog_by_path("character/Ružena");
ASSERT_NE(nullptr, cat_char) << "Missing parents should be created immediately after loading.";
ASSERT_NE(nullptr, cat_ellie) << "Missing parents should be created immediately after loading.";
ASSERT_NE(nullptr, cat_ruzena) << "Missing parents should be created immediately after loading.";
AssetCatalogDefinitionFile *cdf = loaded_service.get_catalog_definition_file();
ASSERT_NE(nullptr, cdf);
EXPECT_TRUE(cdf->contains(cat_char->catalog_id)) << "Missing parents should be saved to a CDF.";
EXPECT_TRUE(cdf->contains(cat_ellie->catalog_id)) << "Missing parents should be saved to a CDF.";
EXPECT_TRUE(cdf->contains(cat_ruzena->catalog_id))
<< "Missing parents should be saved to a CDF.";
/* Check that each missing parent is only created once. The CDF contains multiple paths that
* could trigger the creation of missing parents, so this test makes sense. */
EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character"));
EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ellie"));
EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ružena"));
}
TEST_F(AssetCatalogTest, create_catalog_filter)
{
AssetCatalogService service(asset_library_root_);
service.load_from_disk();
/* Alias for the same catalog as the main one. */
AssetCatalog *alias_ruzena = service.create_catalog("character/Ružena/poselib");
/* Alias for a sub-catalog. */
AssetCatalog *alias_ruzena_hand = service.create_catalog("character/Ružena/poselib/hand");
AssetCatalogFilter filter = service.create_catalog_filter(UUID_POSES_RUZENA);
/* Positive test for loaded-from-disk catalogs. */
EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA))
<< "Main catalog should be included in the filter.";
EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_HAND))
<< "Sub-catalog should be included in the filter.";
EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_FACE))
<< "Sub-catalog should be included in the filter.";
/* Positive test for newly-created catalogs. */
EXPECT_TRUE(filter.contains(alias_ruzena->catalog_id))
<< "Alias of main catalog should be included in the filter.";
EXPECT_TRUE(filter.contains(alias_ruzena_hand->catalog_id))
<< "Alias of sub-catalog should be included in the filter.";
/* Negative test for unrelated catalogs. */
EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included.";
EXPECT_FALSE(filter.contains(UUID_ID_WITHOUT_PATH));
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_WHITESPACE));
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_TRAILING_SLASH));
EXPECT_FALSE(filter.contains(UUID_WITHOUT_SIMPLENAME));
}
TEST_F(AssetCatalogTest, create_catalog_filter_for_unknown_uuid)
{
AssetCatalogService service;
const bUUID unknown_uuid = BLI_uuid_generate_random();
AssetCatalogFilter filter = service.create_catalog_filter(unknown_uuid);
EXPECT_TRUE(filter.contains(unknown_uuid));
EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included.";
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
}
TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets)
{
AssetCatalogService service;
AssetCatalogFilter filter = service.create_catalog_filter(BLI_uuid_nil());
EXPECT_TRUE(filter.contains(BLI_uuid_nil()));
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
}
} // namespace blender::bke::tests

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