1
1

Compare commits

...

138 Commits

Author SHA1 Message Date
83d7dd528d Geometry Nodes: Update boilerplate code for attributes 2020-11-17 09:22:33 -05:00
46e2f4761c Merge branch 'geometry-nodes' into geometry-nodes-attribute-nodes 2020-11-17 09:03:04 -05:00
b26cbb5d53 Merge branch 'master' into geometry-nodes 2020-11-16 12:03:01 -05:00
52e3608fe9 Geometry Nodes: simplify GeometrySet ownership handling
Previously, GeometrySets and GeometryComponents has reference
counters and could be shared. This commit changes it so that
only GeometryComponents are shared. A GeometrySet is a fairly
small type that is cheap to copy.

A lot of code simplifies when we can assume that GeometrySet
is cheap to copy.
2020-11-16 13:48:33 +01:00
6ba6e97407 Geometry Nodes: Move GeometrySet classes to global namespace
Brecht and I decided to move the GeometrySet classes
to the global namespace for now. This way we can use
the same type in C and C++ and don't have to use
reinterpret_cast as often.
2020-11-16 12:07:51 +01:00
4913b624d8 Merge branch 'master' into geometry-nodes 2020-11-16 11:49:14 +01:00
96ce1e9a95 Geometry Nodes: Improve node group output modifier error
More than output for the modifier's node group is fine, because the
node group may be used in other situations, but the modifier will
not work if there is no geometry output socket.
2020-11-14 20:09:27 -05:00
a3efa1d798 Merge branch 'master' into geometry-nodes 2020-11-14 15:17:46 -05:00
e50553c61a Empty Modifier: Fix build and put in the right order
Bypassing the macro that creates modifiers to allow the enum to be
referring to the Empty modifier, but the internal struct in the code to
be Nodes.

Also fix the name of the new created modifiers to be "Empty".

Fix was built with rBb458ea6b23381a9acb90dbbd73ced678e1d404c5.
2020-11-14 01:51:06 +01:00
b458ea6b23 Rename: "Nodes" modifier to "Empty" (take 2)
Leave the internal DNA data struct as "Nodes", and only rename the
modifier and user interface to Empty.

See 4a2734a835 for the original take and the reasoning
for renaming from Nodes to Empty.
2020-11-13 23:29:09 +01:00
056d7bb175 Revert "Rename: "Nodes" modifier to "Empty""
This reverts commit 4a2734a835.

Reverting this so that all we change is the UI name, not the DNA struct
and the internal variable names.
2020-11-13 23:19:40 +01:00
Léo Depoix
9769cf1ee6 Geometry Nodes : Removing "Combine Strings" and Group Instance ID from UI
This diff remove "Combine Strings" and "Group Instance ID" nodes from the UI (but keep them in the source code to be able to bring them back if needed).

This is part of "Cleanup 1st Sprint Nodes" (https://developer.blender.org/T82370).

Differential Revision: https://developer.blender.org/D9558
2020-11-13 22:23:37 +01:00
4a2734a835 Rename: "Nodes" modifier to "Empty"
This introduces a regression. Users need to re-create their modifiers
and set their values again. The NodeGroups are intact so all they need
is to create new "Empty" modifiers and set the node group.

---

Original problem: Eventually most modifiers will be node-based, so naming
this new modifier "Nodes" will get outdated pretty quickly.

Calling it "Empty" it's fairly descriptive, since the node tree simply connects
the modifier Input to an Output, without any effect.

There is a potential problem, that it could be associated with the
Empty object type. Like some sort of Hook.

This is the task T82700, and the design task T82537.
2020-11-13 22:16:28 +01:00
1ddd717803 Geometry Nodes: Improve error messages for property mismatches
Related to T82438. This adds some basic checks for common errors and
currently unsupported socket types in the modifier.
2020-11-13 12:35:10 -05:00
0066e59f2d Merge branch 'master' into geometry-nodes 2020-11-13 11:54:29 -05:00
ec7fffb033 Cleanup: Remove unused variable 2020-11-12 20:38:51 -05:00
956cf9a48d Cleanup: Clang Tidy 2020-11-12 16:06:12 -05:00
c27095b5cb Cleanup: Spelling
Also move some input extraction after the special case check.
2020-11-12 16:04:45 -05:00
Léo Depoix
8ef8cb7e34 Geometry Nodes: Add method enums to mesh triangulate node
This commit adds the remaining "method" enums from the triangulate modifier.

Differential Revision: https://developer.blender.org/D9502
2020-11-12 15:49:59 -05:00
0f6bee8e54 Merge branch 'master' into geometry-nodes 2020-11-12 15:19:09 -05:00
3093f89498 Geometry Nodes: Add null check for modifier property UI functions
An ID IDProperty exposed to the modifier  (object, material, etc) does not
have min, max, etc. values, so they should not be required.
2020-11-12 14:47:17 -05:00
f211030344 Cleanup: Fix typo 2020-11-12 12:19:44 -05:00
b277025d8e Geometry Nodes: Add modifier disabled check 2020-11-12 12:16:32 -05:00
94572a4e30 Geometry Nodes: use density attribute name instead of index as node input
This implements a workaround for the issue that (for historical reasons)
the names of vertex groups are stored on the object while the actual vertex
group data is stored on the mesh.

The solution is to copy the vertex group names from the object into
the `MeshComponent` so that the information is not lost, when the
object cannot be accessed.
2020-11-12 16:58:30 +01:00
bc2230df71 Geometry Nodes: cleanup geometry node interface
Previously, the execution function of a geometry node has three parameters.
Now it has only one. This makes it easier to pass more information to the
execution function, that might only be used by a few nodes, because we
don't have to add more parameters that are unused in most cases.
2020-11-12 16:31:32 +01:00
caa942b033 Geometry Nodes: actually create instances in Point Instance node 2020-11-12 13:28:33 +01:00
5dff952b67 Geometry Nodes: support instances in Transform node 2020-11-12 13:27:56 +01:00
5877e34eb4 Geometry Nodes: new DupliGenerator for instances component
With this, instances generated in a node tree can be rendered.
2020-11-12 13:27:20 +01:00
770bcfac9b Geometry Nodes: improve point distribute node
* Support vertex weights to control density.
* O(n) performance instead of O(n^2).
* More stable when density weights are changed.

The vertex group has to be specified using an index for now.
This is a technical limitation that will resolved a bit later.
2020-11-12 13:24:46 +01:00
b081108819 Geometry Nodes: support geometry components in depsgraph object iterator
Objects can evaluate to a geometry set instead of a single ID (only point cloud
objects for now). In the depsgraph object iterator, those geometry components
are expanded into temporary objects.

It's important to note that instanced objects can also contain geometry
components. Therefore, they have to be split up into multiple objects
as well in some cases.

At a high level the iterator works like so:
```
for object in depsgraph:
    for component in object:
        yield object_from_component(component)
    for dupli in make_duplis_list(object):
        for component in dupli:
            yield object_from_component(component)
```

DEG_iterator_objects_next has been cleaned up, to make this structure
a bit more apparent.
2020-11-12 13:20:23 +01:00
2be7b2aaf9 Geometry Nodes: better handle link cycles 2020-11-12 12:58:27 +01:00
912b38001f Geometry Nodes: use GeometrySet when evaluating pointcloud modifiers
This changes the signature of the modifyPointCloud function.
I'm doing that instead of making a new callback, because it requires
changes to significantly fewer files. Eventually it would be good
combine modifyMesh, modifyHair, modifyPointCloud and modifyVolume
into one modifyGeometrySet.

I temporarily disabled the displacement only modifiers for point clouds.
Support can be added back a bit later. I assume those have not been
used anywhere anyway.

The output of point cloud modifiers can not only be another point cloud,
but also a mesh and/or some instances. I added a new geometry_set_eval
field to Object_Runtime. For point cloud objects, the final geometry is
now referenced by that pointer instead of data_eval. The data_eval field
is still initialized after modifier evaluation to make some other code happy.

The evaluated geometry set is not yet passed to the renderer, so a point
cloud is currently rendered empty.
2020-11-12 12:57:52 +01:00
c4352f44bc Geometry Nodes: initial Object Info node
This node takes an object as input and outputs its location, rotation,
scale and geometry. Right now the loc/rot/scale are extracted from the
objects `obmat`. The geometry is just the mesh in the local space of the
source object.

We will likely need some more control over space transformations
using enums in the node, but those can be added a bit later.
2020-11-12 12:24:07 +01:00
0feca5f07d Geometry Nodes: initial object socket support
The fundamental difference between object sockets and the
other existing data sockets is that an object is an ID data block.
Changing the value of an object socket also changes the depsgraph.

The modifier has to analyse the node tree to figure out which other
objects it depends on. Currently, this is done very simply by just
looping over all sockets and collecting the objects. In the future
this can be improved by also figuring out what components of
an object are needed.

Instead of passing object pointers around in the node tree, we actually
use a handle. This handle is just a number internally that identifies
a specific object. The conversion between handles and object pointers
is done using a map that is provided by the modifier.

This approach has a couple of benefits. It protects us a bit from passing
around pointers that are not known to the modifier and therefore are
not in the depsgraph. Furthermore, the object pointer can change
while the handle stays the same. This is not important right now, but
is not unlikely to become useful in the future.

The API for how nodes access object pointers is not ideal yet and
will be improved in the future.
2020-11-12 12:20:59 +01:00
2984fb2b49 Geometry Nodes: crash when using Vector Math node 2020-11-12 11:51:12 +01:00
9e6553c0d4 Geometry Nodes: change color of object socket 2020-11-12 11:50:23 +01:00
107a0894cc Geometry Nodes: improve GeometrySet
This commits implements multiple changes:
* Adds a simple C API.
* Improves the ownership handling by introducing GeometryOwnershipType.
* Adds an InstancesComponent that stores positions and Object pointers.
2020-11-12 11:47:48 +01:00
8ecc1bea4c Nodes: add utility to check for link cycles in derived node trees 2020-11-12 11:35:46 +01:00
91ad33ef8f Merge branch 'master' into geometry-nodes 2020-11-12 11:25:25 +01:00
fa5190e742 fix after merge 2020-11-09 15:55:24 +01:00
74ed591f62 Merge branch 'master' into geometry-nodes 2020-11-09 15:48:18 +01:00
3d5efb4335 Geometry Nodes: rename Geometry type to GeometrySet
This should not change any functionality.

After talking to Brecht, we agreed that it might be good
not to have a class called Geometry for now. In the future
we might want to use a Geometry class as base class for
meshes, curves, etc.

This commit renames the Geometry class to GeometrySet,
because it is essentially a container that can contain
multiple geometries of different types.
2020-11-09 13:08:17 +01:00
Léo Depoix
140b7cfe0d Geometry Nodes: cleanup Subdivision Surface node inputs
Ref T82370.

Differential Revision: https://developer.blender.org/D9487
2020-11-09 12:49:33 +01:00
af7cc3f8bb Fix merge issues 2020-11-09 12:46:04 +01:00
d2c4af9865 Merge branch 'master' into geometry-nodes 2020-11-09 12:44:45 +01:00
e5c637f5fe Merge remote-tracking branch 'origin/master' into geometry-nodes 2020-11-06 18:22:36 +01:00
11d12d543d Fix build error on windows: Don't use designated initializers in C++ 2020-11-02 08:20:54 -06:00
2c6114b238 Merge branch 'master' into geometry-nodes 2020-11-02 08:15:15 -06:00
152bd43ae1 Merge branch 'master' into geometry-nodes 2020-11-02 11:44:46 +01:00
24474a60b6 Geometry Nodes: Use std::string for string sockets 2020-10-31 23:57:14 -05:00
0bd0e08790 Merge branch 'geometry-nodes' into geometry-nodes-attribute-nodes 2020-10-31 23:24:22 -05:00
45012c3309 Geometry Nodes: Fix property values reset when input socket is added
The names of the IDProperties are the socket identifiers, not their names.
2020-10-31 23:24:06 -05:00
faac263d97 Merge branch 'geometry-nodes' into geometry-nodes-attribute-nodes 2020-10-31 23:01:31 -05:00
5177c35498 Merge branch 'master' into geometry-nodes 2020-10-31 22:58:39 -05:00
49c8042f29 Merge branch 'master' into geometry-nodes 2020-10-31 14:21:14 +01:00
Léo Depoix
f8c52590c0 Geometry Nodes: new Subdivision Surface node
This node is similar to the subdivision surface modifier. It subdivides
the mesh component of a geometry. The options have the same meaning
as in the modifier.

Differential Revision: https://developer.blender.org/D9364
2020-10-31 10:51:09 +01:00
0b897e0308 Merge branch 'master' into geometry-nodes 2020-10-31 10:43:52 +01:00
97c0158cda Merge branch 'geometry-nodes' into geometry-nodes-attribute-nodes 2020-10-31 00:37:41 -05:00
c34438dd78 Geometry Nodes: Rename node category based on functionality
Naming based on supported geometry components only works in some cases.
For nodes that work with multiple data types, it's better to categorize
them based on their functionality.
2020-10-31 00:37:04 -05:00
e0a4dc9396 Merge branch 'master' into geometry-nodes 2020-10-31 00:30:10 -05:00
97a9576959 Geometry Nodes: Add initial empty nodes for basic attribute workflow
This adds the boilerplate code for three nodes: "Create Attribute,"
"Random Attribute," and "Attribute Math." Combined, they should
be enough for the process of creating randomized "Rotation" and
"Scale" attributes on point clouds for instancing done later. The
implementation details are not resolved at this point, but this
starting point should make sense.
2020-10-31 00:21:05 -05:00
51fa44522f Geometry Nodes: Add errors for out of sync sockets and properties
Theoretically the modifier's properties could be changed or removed by
Python, so it may be useful to have errors printed and added to the
modifier in that case.

I kept the check as a separate step from the `compute_geometry`
pass because the object is needed to set the modifier error message.
But it could easily be moved there in the future.
2020-10-30 11:42:11 -05:00
343e13f464 Geometry Nodes: change geometry socket color
Ref T81848.
2020-10-30 17:29:36 +01:00
1103809fab Merge branch 'master' into geometry-nodes 2020-10-30 16:40:41 +01:00
5b89d49b0d Geometry Nodes: Allow node group inputs with the same name
If we add the "name" label manually with uiItemR, we can use the socket
identifier instead of the name for the IDProperty name. This will also
allow us more flexibility in how to draw the settings in the future, and
removes the empty _RNA_UI proprety that was drawn before.
2020-10-30 09:57:54 -05:00
e805bfc768 Cleanup: Return early in edge split node 2020-10-29 15:53:32 -05:00
67cb4fdbdc Cleanup: Don't use unsupported struct initialization
Although this worked for me, it appears this isn't supported until C++20.
2020-10-29 15:48:38 -05:00
8ed74c9daf Geometry Nodes: Use UI settings for properties in modifier
The system for exposing property settings like min, max, default, subtype,
etc. for ID properties is quite convoluted currently, so I won't give a
full description here, but this commit creates the tree of ID properties
needed to store that information. This means that property subtypes like
"angle" or "XYZ" will affect the display in the modifier.

Limitations:
 - The _RNA_UI property is displayed in the modifier. This may require a
   modification to uiDefAutoButsRNA to fix.
 - IDProperties must have unique names, but node sockets don't have
   that limitation. This can be solved by adding a "UI name" field to
   IDProperties.
2020-10-29 14:11:32 -05:00
d7cb25e028 Fixup for cmake header cleanup
Issue introduced on f73a420e5a.
2020-10-29 18:27:31 +01:00
e3395093ff Geometry Nodes: Create the node group when adding a new Nodes modifier 2020-10-29 17:36:16 +01:00
f73a420e5a Cleanup: Nodes Modifier header to its proper location 2020-10-29 17:36:16 +01:00
b8b240b329 Geometry Nodes: do not reference custom data layers when copying mesh
We can't use that for now, because the original mesh might be freed
before the new copied mesh. When the original mesh is freed, the shared
layers will be freed as well.

This can probably be improved in the future, so that we can actually
share attribute arrays between meshes. An approach similar to how
geometries and components are shared should work as well.
2020-10-29 15:47:19 +01:00
ae2827bb52 Geometry Nodes: add comment mentioning that custom data might be shared 2020-10-29 15:42:01 +01:00
bb97284f8a Geometry Nodes: refactor Geometry type
A geometry now contains zero or more geometry components.
A geometry component is a subclass off GeometryComponent.
Currently, there is a MeshComponent and PointCloudComponent.

A geometry contains at most one component of each type.
Individual components can be shared between multiple geometries
to avoid unnecessary copies. For that, each component has a user
count that determines when the component will be freed and whether
it is mutable.

Code working with geometries can either work with the components
directly, or use the utility functions on the geometry that cover the
most common operations.

In the future, additional component types can be added. For example,
we'll probably need components for curves and volumes.
Furthermore, something like an InstancesComponent can be added,
which contains points with attributes and references a geometry/object/collection
that is instanced on all the points.
2020-10-29 15:38:15 +01:00
209e82da45 Merge branch 'master' into geometry-nodes 2020-10-29 15:22:59 +01:00
e63d43e6b9 Merge branch 'master' into geometry-nodes 2020-10-29 11:31:55 +01:00
802ba35654 Geometry Nodes: make default group output node active
Otherwise some updates were missing when sockets are changed.
2020-10-28 20:27:57 +01:00
79c9c11b35 Merge branch 'master' into geometry-nodes 2020-10-28 20:21:52 +01:00
e1d0ab2dbc Geometry Nodes: fix missing update when inserting a node between two nodes 2020-10-28 14:53:06 +01:00
687f994251 Geometry Nodes: improve api for nodes
The execute callback of a geometry node gets more domain specific
types as parameters now: GeoNodeInputs and GeoNodeOutputs.

Those types are also aware of what node is being executed and can
provide better error messages when they are used incorrectly.
2020-10-28 14:05:07 +01:00
5c7767e0e0 Merge branch 'master' into geometry-nodes 2020-10-28 14:01:05 +01:00
af9ee8e2bc Merge branch 'master' into geometry-nodes 2020-10-28 10:40:35 +01:00
562f2e604a Merge branch 'master' into geometry-nodes 2020-10-27 21:21:03 -05:00
84e81b8cc3 Merge branch 'master' into geometry-nodes 2020-10-27 14:01:48 -05:00
5ae9527770 Geometry Nodes: Support pointcloud in transform node
This is likely not the final implementation of the transform node, but
it's  a good trivial case for supporting multiple geometry data types.
2020-10-27 12:59:58 -05:00
082c17a2d2 Fix group input is resetting in modifier 2020-10-27 17:01:03 +01:00
09677d737c Fix missing null check in recent commit 2020-10-27 15:53:17 +01:00
bec7248178 Cleanup: Use proper C++ types 2020-10-27 09:43:38 -05:00
0103cb2051 Geometry Nodes: initial support for bool group inputs
Still need to work on the uilayout inside the modifier.
2020-10-27 14:59:26 +01:00
9f38ebefcb Geometry Nodes: support vector inputs to geometry group 2020-10-27 14:54:41 +01:00
8f934852f0 Geometry Nodes: initial support for displaying group inputs in modifier
This uses the previously added id properties on the nodes modifier
to store values that are passed into the geometry node group.
2020-10-27 13:44:46 +01:00
cb16db7803 Geometry Nodes: enable nodes modifier in edit mode 2020-10-27 12:09:13 +01:00
f8965f12a1 Geometry Nodes: Fix memory leak 2020-10-26 23:15:05 -05:00
7f2a20f89b Merge branch 'master' into geometry-nodes 2020-10-26 23:12:45 -05:00
5566818603 Geometry Nodes: Add initial scattering nodes
The first is a "Point Distribute" node, which takes a mesh and outputs a
pointcloud, scattering points randomly on the surface based on the input
density. The distribution algorithm is extremely basic at this point, and
doesn't take into account an attribute weight at each vertex.

The second node is a "Point Instance" node, which is mainly a placeholder
until the engineering design for how to work with instancing in geometry
nodes is decided. For now it just runs the same code that the convert
pointcloud to mesh operator uses.
2020-10-26 23:00:31 -05:00
9a06c6a040 Geometry Nodes: Add function to create a temporary pointcloud
We needed a "nomain" funciton similar to the one we have for meshes in
order to add a working pointcloud for the geometry nodes.
2020-10-26 22:54:55 -05:00
e097116072 Geometry Nodes: Add utility to position point randomly on 3D triangle 2020-10-26 22:44:35 -05:00
b844caca6d Geometry Nodes: Add pointcloud support to geometry class
This adds another set of the same mesh functions but for pointclouds.
There are probably better ways to generalize this functionality, but that
may have to be rethought in the future anyway if we want to store
multiple of each type. And anyway it's handy to have a specific set of
"pointcloud" functions available in the node implementations.
2020-10-26 22:41:57 -05:00
046ac5fccd Geometry Nodes: Initial mesh boolean node
This uses the code from the rewritten boolean modifier from 2.91 as a node.
The implementation is about as minimal as it can get, since for now the use
case of this node is mostly to test multiple geometry outputs and inputs.

The boolean code requires a BMesh, so the node converts the input meshes
to BMesh, so the bmesh is added as a dependency to the nodes module.
2020-10-26 14:27:19 -05:00
ef4fbba596 Cleanup: remove unused modifier property 2020-10-26 18:27:44 +01:00
aa8360d0fa Geometry Nodes: initial test trying to use id properties to initialize group inputs
The settings can only be set via Python currently. The matching between properties
and group inputs is based on the socket identifier (which is e.g. `Input_5`).
Maybe we'll have to use a different matching strategy in the future, will see.
2020-10-26 18:23:51 +01:00
76c356c12e Geometry Nodes: store id properties in nodes modifier
The properties are not used yet, but can already be accessed with Python
using `modifier.settings['setting name']`.

The plan is to use id properties to store the parameters that the modifier
passes into the geometry node group.
2020-10-26 17:53:51 +01:00
e04491073d Geometry Nodes: support evaluation with more than one group input
Group inputs are not yet exposed in the modifier. For now I just added
a simple float setting that will be passed to every float input of the group.
Inputs of other types have some default value.
2020-10-26 14:16:26 +01:00
a5dda5996e Geometry Nodes: connect group input and output by default 2020-10-26 13:29:03 +01:00
c48e4b7a44 Geometry Nodes: improve node tree evaluation
This change reduces the number of unnecessary copies of data
and avoids computing the same value more than once.
2020-10-26 13:27:02 +01:00
4ae2d6206a Geometry Nodes: initial Transform node
Most of this code has been written by @HooglyBoogly.
I just changed the exec funtion so that it does not have to make
a copy of the mesh.
2020-10-26 12:27:51 +01:00
47f5a635da Geometry Nodes: add utility method to check if a geometry has a mesh 2020-10-26 12:25:46 +01:00
eb8574bc8b Merge branch 'master' into geometry-nodes 2020-10-26 12:10:12 +01:00
bc4e31afb6 Merge branch 'master' into geometry-nodes 2020-10-24 14:38:00 +02:00
9d7672be71 Merge branch 'master' into geometry-nodes 2020-10-23 15:18:20 +02:00
994e7178bb Geometry Nodes: make some function nodes available
We might not want to have all those nodes in a final version.
Some of them have been added with particle nodes in mind.
However, to test the evaluation system it is useful to have a
couple of nodes available.

Those nodes should "just" work, because their implementation
is reused from the particle nodes project.
2020-10-23 15:13:19 +02:00
1719743066 Geometry Nodes: improve node group evaluation
This adds support for nodes that have a multi-function implementation.
That includes various function nodes like Math, Combine Vector, ...

Furthermore, there is support for implicit conversions now. So it should
work when one connects e.g. a float to an integer and vice versa.
2020-10-23 15:09:55 +02:00
8910033f57 Nodes: add utility methods 2020-10-23 15:05:01 +02:00
2a4c6c612a Functions: add utility method 2020-10-23 15:01:07 +02:00
b062b922f9 Geometry Nodes: Resolve some missing 3D viewport updates
These two functions "snode_notify" and "ED_node_tag_update_id" appear to
be mostly duplicates. However, there is already a case for each type of
built-in node tree, so it makes sense to add one for the geometry node
tree as well. This doesn't solve the update issues for changing number
in buttons, that must be handled somewhere else.
2020-10-22 22:59:40 -05:00
895f4620a0 Merge branch 'master' into geometry-nodes 2020-10-22 22:03:28 -05:00
fafed6234b Geometry Nodes: Add edge split node functionality 2020-10-22 13:48:56 -05:00
7ff8094a8b Geometry Nodes: expose minimum vertices input of Triangulate node 2020-10-22 18:24:05 +02:00
5aabf67a9c Fix error in previous commit
That should not have happened -.-
2020-10-22 18:23:39 +02:00
ab8c7fe946 Fix previous comment 2020-10-22 18:20:17 +02:00
a05012d500 Geometry Nodes: simplify and deduplicate callbacks on sockets
This adds a layer of abstraction between the code calling callbacks
on sockets and the implementation of those callbacks.
This abstraction layer allows some sockets to not implement all
callbacks when those can be derived from some other callback.
2020-10-22 18:08:27 +02:00
97a93566e9 Geometry Nodes: change "Node Tree" to "Node Group" 2020-10-22 15:52:15 +02:00
da4d697772 Geometry Nodes: initial support for evaluating geometry node groups
This is still very basic and does quite a few unnecessary computations.
Also the error handling is quite weak currently, so when invalid things are
connected, it will probably just crash.

Also the interface that individual nodes have to implement will have to change,
but the current solution is a good starting point.

Only the triangulate node is implemented for now.
2020-10-22 15:05:41 +02:00
87218899be Geometry Nodes: add an initial geometry class 2020-10-22 15:02:27 +02:00
ffa0a6df9d Functions: add generic pointer class
This class represents a pointer whose type is only known at runtime.
2020-10-22 15:01:31 +02:00
706fa5ad76 Functions: add move operations to CPPType 2020-10-22 15:00:07 +02:00
b7f6de490d Geometry Nodes: Add initial node definition for edge split
This is just based on rBa7dba81aab22, and contains no funcionality at all.
2020-10-21 16:11:09 -05:00
7e485b4620 Merge branch 'master' into geometry-nodes 2020-10-21 08:54:39 -05:00
a7dba81aab Nodes: add initial UI for Triangulate node 2020-10-21 14:14:09 +02:00
4606e83a75 Merge branch 'master' into geometry-nodes 2020-10-21 14:00:32 +02:00
1d28de57a4 Nodes: improve dependency between modifier and node group 2020-10-21 13:16:19 +02:00
3cfcfb938d Nodes: support creating geometry node groups 2020-10-21 12:32:02 +02:00
bcdc6910a0 Nodes: show header in geometry node editor 2020-10-21 12:16:57 +02:00
7793e8c884 Modifiers: add node_tree to NodesModifierData 2020-10-21 12:13:13 +02:00
05d9bd7c4a Modifiers: rename Simulation to Nodes modifier 2020-10-21 12:03:06 +02:00
9255ce9247 Nodes: rename Simulation to Geometry node tree 2020-10-21 11:39:42 +02:00
a0ce0154e7 Merge branch 'master' into geometry-nodes 2020-10-21 11:11:16 +02:00
0cd7f7ddd1 Nodes: add geometry socket type
We still have to pick a color for this socket.

Ref T81848.
2020-10-20 15:31:59 +02:00
92 changed files with 4938 additions and 626 deletions

View File

@@ -31,6 +31,7 @@ _modules = [
"console",
"constraint",
"file",
"geometry_nodes",
"image",
"mesh",
"node",
@@ -42,7 +43,6 @@ _modules = [
"rigidbody",
"screen_play_rendered_anim",
"sequencer",
"simulation",
"userpref",
"uvcalc_follow_active",
"uvcalc_lightmap",

View File

@@ -19,23 +19,34 @@
import bpy
class NewSimulation(bpy.types.Operator):
"""Create a new simulation data block and edit it in the opened simulation editor"""
class NewGeometryNodeTree(bpy.types.Operator):
"""Create a new geometry node tree"""
bl_idname = "simulation.new"
bl_label = "New Simulation"
bl_idname = "node.new_geometry_node_tree"
bl_label = "New Geometry Node Tree"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.area.type == 'NODE_EDITOR' and context.space_data.tree_type == 'SimulationNodeTree'
return context.area.type == 'NODE_EDITOR' and context.space_data.tree_type == 'GeometryNodeTree'
def execute(self, context):
simulation = bpy.data.simulations.new("Simulation")
context.space_data.simulation = simulation
group = bpy.data.node_groups.new("Geometry Node Group", 'GeometryNodeTree')
group.inputs.new('NodeSocketGeometry', "Geometry")
group.outputs.new('NodeSocketGeometry', "Geometry")
input_node = group.nodes.new('NodeGroupInput')
output_node = group.nodes.new('NodeGroupOutput')
output_node.is_active_output = True
input_node.location.x = -200 - input_node.width
output_node.location.x = 200
group.links.new(output_node.inputs[0], input_node.outputs[0])
context.space_data.node_tree = group
return {'FINISHED'}
classes = (
NewSimulation,
NewGeometryNodeTree,
)

View File

@@ -151,13 +151,10 @@ class NODE_HT_header(Header):
if snode_id:
layout.prop(snode_id, "use_nodes")
elif snode.tree_type == 'SimulationNodeTree':
row = layout.row(align=True)
row.prop(snode, "simulation", text="")
row.operator("simulation.new", text="", icon='ADD')
simulation = snode.simulation
if simulation:
row.prop(snode.simulation, "use_fake_user", text="")
elif snode.tree_type == 'GeometryNodeTree':
NODE_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
layout.template_ID(snode, "node_tree", new="node.new_geometry_node_tree")
else:
# Custom node tree is edited as independent ID block

View File

@@ -58,11 +58,11 @@ class TextureNodeCategory(SortedNodeCategory):
context.space_data.tree_type == 'TextureNodeTree')
class SimulationNodeCategory(SortedNodeCategory):
class GeometryNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'SimulationNodeTree')
context.space_data.tree_type == 'GeometryNodeTree')
# menu entry for node group tools
@@ -77,11 +77,11 @@ node_tree_group_type = {
'CompositorNodeTree': 'CompositorNodeGroup',
'ShaderNodeTree': 'ShaderNodeGroup',
'TextureNodeTree': 'TextureNodeGroup',
'SimulationNodeTree': 'SimulationNodeGroup',
'GeometryNodeTree': 'GeometryNodeGroup',
}
# generic node group items generator for shader, compositor, simulation and texture node groups
# generic node group items generator for shader, compositor, geometry and texture node groups
def node_group_items(context):
if context is None:
return
@@ -483,10 +483,45 @@ def not_implemented_node(idname):
return NodeItem(idname, label=label)
simulation_node_categories = [
# Simulation Nodes
SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items),
SimulationNodeCategory("SIM_LAYOUT", "Layout", items=[
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
NodeItem("GeometryNodeTriangulate"),
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeTransform"),
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeSubdivisionSurface"),
]),
GeometryNodeCategory("GEO_ATTRIBUTES", "Attributes", items=[
NodeItem("GeometryNodeRandomAttribute"),
NodeItem("GeometryNodeAttributeMath"),
]),
GeometryNodeCategory("GEO_SCATTERING", "Scattering", items=[
NodeItem("GeometryNodePointDistribute"),
NodeItem("GeometryNodePointInstance"),
]),
GeometryNodeCategory("GEO_MATH", "Misc", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
# NodeItem("FunctionNodeCombineStrings"),
NodeItem("FunctionNodeRandomFloat"),
NodeItem("ShaderNodeValue"),
# NodeItem("FunctionNodeGroupInstanceID"),
]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
]),
@@ -497,14 +532,14 @@ def register():
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
nodeitems_utils.register_node_categories('SIMULATION', simulation_node_categories)
nodeitems_utils.register_node_categories('GEOMETRY', geometry_node_categories)
def unregister():
nodeitems_utils.unregister_node_categories('SHADER')
nodeitems_utils.unregister_node_categories('COMPOSITING')
nodeitems_utils.unregister_node_categories('TEXTURE')
nodeitems_utils.unregister_node_categories('SIMULATION')
nodeitems_utils.unregister_node_categories('GEOMETRY')
if __name__ == "__main__":

View File

@@ -0,0 +1,40 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup bke
*/
#ifdef __cplusplus
extern "C" {
#endif
struct Object;
struct GeometrySet;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
int BKE_geometry_set_instances(const struct GeometrySet *geometry_set,
float (**r_positions)[3],
struct Object ***r_objects);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,212 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup bke
*/
#include <atomic>
#include <iostream>
#include "BLI_float3.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_user_counter.hh"
#include "BKE_geometry_set.h"
struct Mesh;
struct PointCloud;
struct Object;
/* Each geometry component has a specific type. The type determines what kind of data the component
* stores. Functions modifying a geometry will usually just modify a subset of the component types.
*/
enum class GeometryComponentType {
Mesh = 0,
PointCloud = 1,
Instances = 2,
};
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
Owned = 0,
/* The geometry can be changed, but someone else is responsible for freeing it. */
Editable = 1,
/* The geometry cannot be changed and someone else is responsible for freeing it. */
ReadOnly = 2,
};
/* Make it possible to use the component type as key in hash tables. */
namespace blender {
template<> struct DefaultHash<GeometryComponentType> {
uint64_t operator()(const GeometryComponentType &value) const
{
return (uint64_t)value;
}
};
} // namespace blender
/**
* This is the base class for specialized geometry component types.
*/
class GeometryComponent {
private:
/* The reference count has two purposes. When it becomes zero, the component is freed. When it is
* larger than one, the component becomes immutable. */
std::atomic<int> users_ = 1;
public:
virtual ~GeometryComponent();
static GeometryComponent *create(GeometryComponentType component_type);
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
void user_add();
void user_remove();
bool is_mutable() const;
};
template<typename T>
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
/**
* A geometry set contains zero or more geometry components. There is at most one component of each
* type. Individual components might be shared between multiple geometries. Shared components are
* copied automatically when write access is requested.
*
* Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
* geometry components.
*/
class GeometrySet {
private:
using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
blender::Map<GeometryComponentType, GeometryComponentPtr> components_;
public:
GeometryComponent &get_component_for_write(GeometryComponentType component_type);
template<typename Component> Component &get_component_for_write()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<Component &>(this->get_component_for_write(Component::type));
}
const GeometryComponent *get_component_for_read(GeometryComponentType component_type) const;
template<typename Component> const Component *get_component_for_read() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<const Component *>(get_component_for_read(Component::type));
}
friend std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set);
friend bool operator==(const GeometrySet &a, const GeometrySet &b);
uint64_t hash() const;
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
class MeshComponent : public GeometryComponent {
private:
Mesh *mesh_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
/* Due to historical design choices, vertex group data is stored in the mesh, but the vertex
* group names are stored on an object. Since we don't have an object here, we copy over the
* names into this map. */
blender::Map<std::string, int> vertex_group_names_;
public:
~MeshComponent();
GeometryComponent *copy() const override;
void clear();
bool has_mesh() const;
void replace(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
Mesh *release();
void copy_vertex_group_names_from_object(const struct Object &object);
int vertex_group_index(blender::StringRef vertex_group_name) const;
const Mesh *get_for_read() const;
Mesh *get_for_write();
static constexpr inline GeometryComponentType type = GeometryComponentType::Mesh;
};
/** A geometry component that stores a point cloud. */
class PointCloudComponent : public GeometryComponent {
private:
PointCloud *pointcloud_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
~PointCloudComponent();
GeometryComponent *copy() const override;
void clear();
bool has_pointcloud() const;
void replace(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
PointCloud *release();
const PointCloud *get_for_read() const;
PointCloud *get_for_write();
static constexpr inline GeometryComponentType type = GeometryComponentType::PointCloud;
};
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:
blender::Vector<blender::float3> positions_;
blender::Vector<const Object *> objects_;
public:
~InstancesComponent() = default;
GeometryComponent *copy() const override;
void replace(blender::Vector<blender::float3> positions,
blender::Vector<const Object *> objects);
void replace(blender::Vector<blender::float3> positions, const Object *object);
blender::Span<const Object *> objects() const;
blender::Span<blender::float3> positions() const;
blender::MutableSpan<blender::float3> positions();
int instances_amount() const;
static constexpr inline GeometryComponentType type = GeometryComponentType::Instances;
};

View File

@@ -43,6 +43,7 @@ struct ModifierData;
struct Object;
struct Scene;
struct bArmature;
struct GeometrySet;
typedef enum {
/* Should not be used, only for None modifier type */
@@ -246,9 +247,9 @@ typedef struct ModifierTypeInfo {
struct Hair *(*modifyHair)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Hair *hair);
struct PointCloud *(*modifyPointCloud)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct PointCloud *pointcloud);
void (*modifyPointCloud)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct GeometrySet *geometry_set);
struct Volume *(*modifyVolume)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Volume *volume);

View File

@@ -112,20 +112,26 @@ namespace blender {
namespace nodes {
class SocketMFNetworkBuilder;
class NodeMFNetworkBuilder;
class GeoNodeExecParams;
} // namespace nodes
namespace fn {
class CPPType;
class MFDataType;
}
} // namespace fn
} // namespace blender
using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)();
using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)();
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder);
#else
typedef void *NodeExpandInMFNetworkFunction;
typedef void *SocketGetMFDataTypeFunction;
typedef void *SocketExpandInMFNetworkFunction;
typedef void *NodeGeometryExecFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetCPPValueFunction;
#endif
/**
@@ -181,10 +187,12 @@ typedef struct bNodeSocketType {
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
/* Returns the multi-function data type of this socket type. */
SocketGetMFDataTypeFunction get_mf_data_type;
/* Expands the socket into a multi-function node that outputs the socket value. */
SocketExpandInMFNetworkFunction expand_in_mf_network;
/* Return the CPPType of this socket. */
SocketGetCPPTypeFunction get_cpp_type;
/* Get the value of this socket in a generic way. */
SocketGetCPPValueFunction get_cpp_value;
} bNodeSocketType;
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
@@ -302,6 +310,9 @@ typedef struct bNodeType {
/* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
NodeExpandInMFNetworkFunction expand_in_mf_network;
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeType;
@@ -438,7 +449,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type);
bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup);
void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree);
void ntreeUpdateAllNew(struct Main *main);
void ntreeUpdateAllUsers(struct Main *main, struct ID *ngroup);
void ntreeUpdateAllUsers(struct Main *main, struct bNodeTree *ngroup);
void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes);
@@ -1322,6 +1333,23 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
struct MTex *mtex);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Geometry Nodes
* \{ */
#define GEO_NODE_TRIANGULATE 1000
#define GEO_NODE_EDGE_SPLIT 1001
#define GEO_NODE_TRANSFORM 1002
#define GEO_NODE_BOOLEAN 1003
#define GEO_NODE_POINT_DISTRIBUTE 1004
#define GEO_NODE_POINT_INSTANCE 1005
#define GEO_NODE_SUBDIVISION_SURFACE 1006
#define GEO_NODE_OBJECT_INFO 1007
#define GEO_NODE_ATTRIBUTE_RANDOM 1008
#define GEO_NODE_ATTRIBUTE_MATH 1009
/** \} */
/* -------------------------------------------------------------------- */
/** \name Function Nodes
* \{ */

View File

@@ -38,6 +38,7 @@ extern const char *POINTCLOUD_ATTR_RADIUS;
void *BKE_pointcloud_add(struct Main *bmain, const char *name);
void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
struct PointCloud *BKE_pointcloud_new_nomain(const int totpoint);
struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);

View File

@@ -124,6 +124,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
intern/geometry_set.cc
intern/gpencil.c
intern/gpencil_curve.c
intern/gpencil_geom.c
@@ -311,6 +312,8 @@ set(SRC
BKE_fluid.h
BKE_font.h
BKE_freestyle.h
BKE_geometry_set.h
BKE_geometry_set.hh
BKE_global.h
BKE_gpencil.h
BKE_gpencil_curve.h

View File

@@ -0,0 +1,467 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_pointcloud.h"
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
using blender::float3;
using blender::MutableSpan;
using blender::Span;
using blender::StringRef;
using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name Geometry Component
* \{ */
GeometryComponent ::~GeometryComponent()
{
}
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
case GeometryComponentType::Mesh:
return new MeshComponent();
case GeometryComponentType::PointCloud:
return new PointCloudComponent();
case GeometryComponentType::Instances:
return new InstancesComponent();
}
BLI_assert(false);
return nullptr;
}
void GeometryComponent::user_add()
{
users_.fetch_add(1);
}
void GeometryComponent::user_remove()
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
delete this;
}
}
bool GeometryComponent::is_mutable() const
{
/* If the item is shared, it is read-only. */
/* The user count can be 0, when this is called from the destructor. */
return users_ <= 1;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Geometry Set
* \{ */
/* This method can only be used when the geometry set is mutable. It returns a mutable geometry
* component of the given type.
*/
GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType component_type)
{
return components_.add_or_modify(
component_type,
[&](GeometryComponentPtr *value_ptr) -> GeometryComponent & {
/* If the component did not exist before, create a new one. */
new (value_ptr) GeometryComponentPtr(GeometryComponent::create(component_type));
return **value_ptr;
},
[&](GeometryComponentPtr *value_ptr) -> GeometryComponent & {
GeometryComponentPtr &value = *value_ptr;
if (value->is_mutable()) {
/* If the referenced component is already mutable, return it directly. */
return *value;
}
/* If the referenced component is shared, make a copy. The copy is not shared and is
* therefore mutable. */
GeometryComponent *copied_component = value->copy();
value = GeometryComponentPtr{copied_component};
return *copied_component;
});
}
/* Get the component of the given type. Might return null if the component does not exist yet. */
const GeometryComponent *GeometrySet::get_component_for_read(
GeometryComponentType component_type) const
{
const GeometryComponentPtr *component = components_.lookup_ptr(component_type);
if (component != nullptr) {
return component->get();
}
return nullptr;
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
{
stream << "<GeometrySet at " << &geometry_set << ", " << geometry_set.components_.size()
<< " components>";
return stream;
}
/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by
* the CPPType system. */
bool operator==(const GeometrySet &UNUSED(a), const GeometrySet &UNUSED(b))
{
return false;
}
/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by
* the CPPType system. */
uint64_t GeometrySet::hash() const
{
return reinterpret_cast<uint64_t>(this);
}
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{
const MeshComponent *component = this->get_component_for_read<MeshComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns true when the geometry set has a mesh component that has a mesh. */
bool GeometrySet::has_mesh() const
{
const MeshComponent *component = this->get_component_for_read<MeshComponent>();
return component != nullptr && component->has_mesh();
}
/* Returns a read-only point cloud of null. */
const PointCloud *GeometrySet::get_pointcloud_for_read() const
{
const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
return component != nullptr && component->has_pointcloud();
}
/* Returns true when the geometry set has an instances component that has at least one instance. */
bool GeometrySet::has_instances() const
{
const InstancesComponent *component = this->get_component_for_read<InstancesComponent>();
return component != nullptr && component->instances_amount() >= 1;
}
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
component.replace(mesh, ownership);
return geometry_set;
}
/* Create a new geometry set that only contains the given point cloud. */
GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>();
component.replace(pointcloud, ownership);
return geometry_set;
}
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
MeshComponent &component = this->get_component_for_write<MeshComponent>();
component.replace(mesh, ownership);
}
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
PointCloudComponent &pointcloud_component = this->get_component_for_write<PointCloudComponent>();
pointcloud_component.replace(pointcloud, ownership);
}
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
MeshComponent &component = this->get_component_for_write<MeshComponent>();
return component.get_for_write();
}
/* Returns a mutable point cloud or null. No ownership is transferred. */
PointCloud *GeometrySet::get_pointcloud_for_write()
{
PointCloudComponent &component = this->get_component_for_write<PointCloudComponent>();
return component.get_for_write();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Component
* \{ */
MeshComponent::~MeshComponent()
{
this->clear();
}
GeometryComponent *MeshComponent::copy() const
{
MeshComponent *new_component = new MeshComponent();
if (mesh_ != nullptr) {
new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void MeshComponent::clear()
{
BLI_assert(this->is_mutable());
if (mesh_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, mesh_);
}
mesh_ = nullptr;
}
vertex_group_names_.clear();
}
bool MeshComponent::has_mesh() const
{
return mesh_ != nullptr;
}
/* Clear the component and replace it with the new mesh. */
void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
mesh_ = mesh;
ownership_ = ownership;
}
/* Return the mesh and clear the component. The caller takes over responsibility for freeing the
* mesh (if the component was responsible before). */
Mesh *MeshComponent::release()
{
BLI_assert(this->is_mutable());
Mesh *mesh = mesh_;
mesh_ = nullptr;
return mesh;
}
void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
{
BLI_assert(this->is_mutable());
vertex_group_names_.clear();
int index = 0;
LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
vertex_group_names_.add(group->name, index);
index++;
}
}
int MeshComponent::vertex_group_index(StringRef vertex_group_name) const
{
return vertex_group_names_.lookup_default_as(vertex_group_name, -1);
}
/* Get the mesh from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
const Mesh *MeshComponent::get_for_read() const
{
return mesh_;
}
/* Get the mesh from this component. This method can only be used when the component is mutable,
* i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
Mesh *MeshComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return mesh_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pointcloud Component
* \{ */
PointCloudComponent::~PointCloudComponent()
{
this->clear();
}
GeometryComponent *PointCloudComponent::copy() const
{
PointCloudComponent *new_component = new PointCloudComponent();
if (pointcloud_ != nullptr) {
new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void PointCloudComponent::clear()
{
BLI_assert(this->is_mutable());
if (pointcloud_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, pointcloud_);
}
pointcloud_ = nullptr;
}
}
bool PointCloudComponent::has_pointcloud() const
{
return pointcloud_ != nullptr;
}
/* Clear the component and replace it with the new point cloud. */
void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
pointcloud_ = pointcloud;
ownership_ = ownership;
}
/* Return the point cloud and clear the component. The caller takes over responsibility for freeing
* the point cloud (if the component was responsible before). */
PointCloud *PointCloudComponent::release()
{
BLI_assert(this->is_mutable());
PointCloud *pointcloud = pointcloud_;
pointcloud_ = nullptr;
return pointcloud;
}
/* Get the point cloud from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned point cloud should not be modified. No ownership is transferred.
*/
const PointCloud *PointCloudComponent::get_for_read() const
{
return pointcloud_;
}
/* Get the point cloud from this component. This method can only be used when the component is
* mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
* transferred. */
PointCloud *PointCloudComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return pointcloud_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Instances Component
* \{ */
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
new_component->positions_ = positions_;
new_component->objects_ = objects_;
return new_component;
}
void InstancesComponent::replace(Vector<float3> positions, Vector<const Object *> objects)
{
BLI_assert(positions.size() == objects.size());
positions_ = std::move(positions);
objects_ = std::move(objects);
}
void InstancesComponent::replace(Vector<float3> positions, const Object *object)
{
positions_ = std::move(positions);
objects_.clear();
objects_.append_n_times(object, positions_.size());
}
Span<const Object *> InstancesComponent::objects() const
{
return objects_;
}
Span<float3> InstancesComponent::positions() const
{
return positions_;
}
MutableSpan<float3> InstancesComponent::positions()
{
return positions_;
}
int InstancesComponent::instances_amount() const
{
BLI_assert(positions_.size() == objects_.size());
return objects_.size();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C API
* \{ */
void BKE_geometry_set_free(GeometrySet *geometry_set)
{
delete geometry_set;
}
bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set)
{
return geometry_set->get_component_for_read<InstancesComponent>() != nullptr;
}
int BKE_geometry_set_instances(const GeometrySet *geometry_set,
float (**r_positions)[3],
Object ***r_objects)
{
const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
if (component == nullptr) {
return 0;
}
*r_positions = (float(*)[3])component->positions().data();
*r_objects = (Object **)component->objects().data();
return component->instances_amount();
}
/** \} */

View File

@@ -342,7 +342,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o
static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id)
{
/* Update all group nodes using a node group. */
ntreeUpdateAllUsers(bmain, new_id);
ntreeUpdateAllUsers(bmain, (bNodeTree *)new_id);
}
/**

View File

@@ -39,6 +39,7 @@
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
@@ -65,7 +66,6 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_simulation.h"
#include "BLI_ghash.h"
#include "BLI_threads.h"
@@ -75,8 +75,8 @@
#include "NOD_common.h"
#include "NOD_composite.h"
#include "NOD_function.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_simulation.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
@@ -85,6 +85,8 @@
#include "BLO_read_write.h"
#include "MOD_nodes.h"
#define NODE_DEFAULT_MAX_WIDTH 700
/* Fallback types for undefined tree, nodes, sockets */
@@ -281,6 +283,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
break;
}
}
@@ -373,6 +376,7 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
BLI_assert(false);
break;
}
@@ -714,6 +718,7 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
break;
}
}
@@ -792,6 +797,7 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
break;
}
}
@@ -1346,6 +1352,7 @@ static void socket_id_user_increment(bNodeSocket *sock)
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
break;
}
}
@@ -1372,6 +1379,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
break;
}
}
@@ -1499,6 +1507,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketObject";
case SOCK_IMAGE:
return "NodeSocketImage";
case SOCK_GEOMETRY:
return "NodeSocketGeometry";
}
return NULL;
}
@@ -1564,6 +1574,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceObject";
case SOCK_IMAGE:
return "NodeSocketInterfaceImage";
case SOCK_GEOMETRY:
return "NodeSocketInterfaceGeometry";
}
return NULL;
}
@@ -3946,14 +3958,18 @@ void ntreeUpdateAllNew(Main *main)
FOREACH_NODETREE_END;
}
void ntreeUpdateAllUsers(Main *main, ID *ngroup)
void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup)
{
if (ngroup == NULL) {
return;
}
/* Update all users of ngroup, to add/remove sockets as needed. */
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
bool need_update = false;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->id == ngroup) {
if (node->id == &ngroup->id) {
if (node->typeinfo->group_update_func) {
node->typeinfo->group_update_func(ntree, node);
}
@@ -3967,6 +3983,19 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup)
}
}
FOREACH_NODETREE_END;
if (ngroup->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (Object *, object, &main->objects) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Empty) {
NodesModifierData *nmd = (NodesModifierData *)md;
if (nmd->node_group == ngroup) {
MOD_nodes_update_interface(object, nmd);
}
}
}
}
}
}
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
@@ -4010,7 +4039,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
}
if (bmain) {
ntreeUpdateAllUsers(bmain, &ntree->id);
ntreeUpdateAllUsers(bmain, ntree);
}
if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) {
@@ -4647,9 +4676,20 @@ static void registerTextureNodes(void)
register_node_type_tex_proc_distnoise();
}
static void registerSimulationNodes(void)
static void registerGeometryNodes(void)
{
register_node_type_sim_group();
register_node_type_geo_group();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_random();
register_node_type_geo_triangulate();
register_node_type_geo_edge_split();
register_node_type_geo_transform();
register_node_type_geo_subdivision_surface();
register_node_type_geo_boolean();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
register_node_type_geo_object_info();
}
static void registerFunctionNodes(void)
@@ -4676,7 +4716,7 @@ void BKE_node_system_init(void)
register_node_tree_type_cmp();
register_node_tree_type_sh();
register_node_tree_type_tex();
register_node_tree_type_sim();
register_node_tree_type_geo();
register_node_type_frame();
register_node_type_reroute();
@@ -4686,7 +4726,7 @@ void BKE_node_system_init(void)
registerCompositNodes();
registerShaderNodes();
registerTextureNodes();
registerSimulationNodes();
registerGeometryNodes();
registerFunctionNodes();
}

View File

@@ -94,6 +94,7 @@
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_font.h"
#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
@@ -1284,8 +1285,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
if (ob->type == OB_POINTCLOUD) {
return (mti->modifyPointCloud != NULL) ||
(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
return (mti->modifyPointCloud != NULL);
}
if (ob->type == OB_VOLUME) {
return (mti->modifyVolume != NULL);
@@ -1507,6 +1507,9 @@ void BKE_object_eval_assign_data(Object *object_eval, ID *data_eval, bool is_own
object_eval->data = data_eval;
}
}
/* Is set separately currently. */
object_eval->runtime.geometry_set_eval = NULL;
}
/**
@@ -1551,6 +1554,11 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_gpencil_eval_delete(ob->runtime.gpd_eval);
ob->runtime.gpd_eval = NULL;
}
if (ob->runtime.geometry_set_eval != NULL) {
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = NULL;
}
}
void BKE_object_free_caches(Object *object)
@@ -1771,6 +1779,10 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode)
visibility |= OB_VISIBLE_INSTANCES;
}
if (ob->runtime.geometry_set_eval != NULL) {
visibility |= OB_VISIBLE_INSTANCES;
}
/* Optional hiding of self if there are particles or instancers. */
if (visibility & (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) {
switch ((eEvaluationMode)dag_eval_mode) {
@@ -4876,6 +4888,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
runtime->mesh_deform_eval = NULL;
runtime->curve_cache = NULL;
runtime->object_as_temp_mesh = NULL;
runtime->geometry_set_eval = NULL;
}
/**

View File

@@ -47,6 +47,7 @@
#include "BKE_editmesh.h"
#include "BKE_editmesh_cache.h"
#include "BKE_font.h"
#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lattice.h"
@@ -806,6 +807,38 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Instances Geometry Component Implementation
* \{ */
static void make_duplis_instances_component(const DupliContext *ctx)
{
float(*positions)[3];
Object **objects;
const int amount = BKE_geometry_set_instances(
ctx->object->runtime.geometry_set_eval, &positions, &objects);
for (int i = 0; i < amount; i++) {
Object *object = objects[i];
if (object == NULL) {
continue;
}
float mat[4][4];
unit_m4(mat);
copy_v3_v3(mat[3], positions[i]);
mul_m4_m4_pre(mat, ctx->object->obmat);
make_dupli(ctx, object, mat, i);
make_recursive_duplis(ctx, object, mat, i);
}
}
static const DupliGenerator gen_dupli_instances_component = {
0,
make_duplis_instances_component,
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dupli-Faces Implementation (#OB_DUPLIFACES)
* \{ */
@@ -1473,7 +1506,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
int transflag = ctx->object->transflag;
int restrictflag = ctx->object->restrictflag;
if ((transflag & OB_DUPLI) == 0) {
if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == NULL) {
return NULL;
}
@@ -1483,6 +1516,12 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return NULL;
}
if (ctx->object->runtime.geometry_set_eval != NULL) {
if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) {
return &gen_dupli_instances_component;
}
}
if (transflag & OB_DUPLIPARTS) {
return &gen_dupli_particles;
}

View File

@@ -1198,9 +1198,6 @@ static bool foreach_object_modifier_ptcache(Object *object,
}
}
}
else if (md->type == eModifierType_Simulation) {
/* TODO(jacques): */
}
}
return true;
}

View File

@@ -33,6 +33,7 @@
#include "BKE_anim_data.h"
#include "BKE_customdata.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -86,6 +87,8 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s
alloc_type,
pointcloud_dst->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
pointcloud_dst->batch_cache = nullptr;
}
static void pointcloud_free_data(ID *id)
@@ -230,6 +233,29 @@ void *BKE_pointcloud_add_default(Main *bmain, const char *name)
return pointcloud;
}
PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
{
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(
nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
pointcloud_init_data(&pointcloud->id);
pointcloud->totpoint = totpoint;
CustomData_add_layer_named(&pointcloud->pdata,
CD_PROP_FLOAT,
CD_CALLOC,
nullptr,
pointcloud->totpoint,
POINTCLOUD_ATTR_RADIUS);
pointcloud->totpoint = totpoint;
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud);
return pointcloud;
}
BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_POINTCLOUD);
@@ -306,13 +332,11 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool
return result;
}
static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
PointCloud *pointcloud_input)
static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
GeometrySet &geometry_set)
{
PointCloud *pointcloud = pointcloud_input;
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -332,40 +356,10 @@ static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
if ((mti->type == eModifierTypeType_OnlyDeform) &&
(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
/* Ensure we are not modifying the input. */
if (pointcloud == pointcloud_input) {
pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
}
/* Ensure we are not overwriting referenced data. */
CustomData_duplicate_referenced_layer_named(
&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION, pointcloud->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud);
/* Created deformed coordinates array on demand. */
mti->deformVerts(md, &mectx, nullptr, pointcloud->co, pointcloud->totpoint);
}
else if (mti->modifyPointCloud) {
/* Ensure we are not modifying the input. */
if (pointcloud == pointcloud_input) {
pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
}
PointCloud *pointcloud_next = mti->modifyPointCloud(md, &mectx, pointcloud);
if (pointcloud_next && pointcloud_next != pointcloud) {
/* If the modifier returned a new pointcloud, release the old one. */
if (pointcloud != pointcloud_input) {
BKE_id_free(nullptr, pointcloud);
}
pointcloud = pointcloud_next;
}
if (mti->modifyPointCloud) {
mti->modifyPointCloud(md, &mectx, &geometry_set);
}
}
return pointcloud;
}
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
@@ -375,12 +369,14 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene
/* Evaluate modifiers. */
PointCloud *pointcloud = static_cast<PointCloud *>(object->data);
PointCloud *pointcloud_eval = pointcloud_evaluate_modifiers(
depsgraph, scene, object, pointcloud);
GeometrySet geometry_set = GeometrySet::create_with_pointcloud(pointcloud,
GeometryOwnershipType::ReadOnly);
pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set);
/* Assign evaluated object. */
const bool is_owned = (pointcloud != pointcloud_eval);
BKE_object_eval_assign_data(object, &pointcloud_eval->id, is_owned);
PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0);
BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true);
object->runtime.geometry_set_eval = new GeometrySet(geometry_set);
}
/* Draw Cache */

View File

@@ -48,8 +48,8 @@
#include "BKE_pointcache.h"
#include "BKE_simulation.h"
#include "NOD_geometry.h"
#include "NOD_node_tree_multi_function.hh"
#include "NOD_simulation.h"
#include "BLI_map.hh"
#include "BLT_translation.h"
@@ -70,7 +70,7 @@ static void simulation_init_data(ID *id)
MEMCPY_STRUCT_AFTER(simulation, DNA_struct_default_get(Simulation), id);
bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname);
bNodeTree *ntree = ntreeAddTree(nullptr, "Geometry Nodetree", ntreeType_Geometry->idname);
simulation->nodetree = ntree;
}

View File

@@ -60,6 +60,12 @@ void BLI_rng_get_tri_sample_float_v2(struct RNG *rng,
const float v2[2],
const float v3[2],
float r_pt[2]) ATTR_NONNULL();
void BLI_rng_get_tri_sample_float_v3(RNG *rng,
const float v1[3],
const float v2[3],
const float v3[3],
float r_pt[3]) ATTR_NONNULL();
void BLI_rng_shuffle_array(struct RNG *rng,
void *data,
unsigned int elem_size_i,

View File

@@ -118,6 +118,7 @@ class RandomNumberGenerator {
float2 get_unit_float2();
float3 get_unit_float3();
float2 get_triangle_sample(float2 v1, float2 v2, float2 v3);
float3 get_triangle_sample_3d(float3 v1, float3 v2, float3 v3);
void get_bytes(MutableSpan<char> r_bytes);
/**

View File

@@ -0,0 +1,158 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup bli
*/
#include <atomic>
namespace blender {
/**
* A simple automatic reference counter. This should probably be moved to another file eventually.
* It is similar to std::shared_ptr, but expects that the reference count is inside the object.
*/
template<typename T> class UserCounter {
private:
T *data_ = nullptr;
public:
UserCounter() = default;
UserCounter(T *data) : data_(data)
{
}
UserCounter(const UserCounter &other) : data_(other.data_)
{
this->user_add(data_);
}
UserCounter(UserCounter &&other) : data_(other.data_)
{
other.data_ = nullptr;
}
~UserCounter()
{
this->user_remove(data_);
}
UserCounter &operator=(const UserCounter &other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
data_ = other.data_;
this->user_add(data_);
return *this;
}
UserCounter &operator=(UserCounter &&other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
data_ = other.data_;
other.data_ = nullptr;
return *this;
}
T *operator->()
{
BLI_assert(data_ != nullptr);
return data_;
}
T &operator*()
{
BLI_assert(data_ != nullptr);
return *data_;
}
operator bool() const
{
return data_ != nullptr;
}
T *get()
{
return data_;
}
const T *get() const
{
return data_;
}
T *release()
{
T *data = data_;
data_ = nullptr;
return data;
}
void reset()
{
this->user_remove(data_);
data_ = nullptr;
}
bool has_value() const
{
return data_ != nullptr;
}
uint64_t hash() const
{
return DefaultHash<T *>{}(data_);
}
friend bool operator==(const UserCounter &a, const UserCounter &b)
{
return a.data_ == b.data_;
}
friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
{
stream << value.data_;
return stream;
}
private:
static void user_add(T *data)
{
if (data != nullptr) {
data->user_add();
}
}
static void user_remove(T *data)
{
if (data != nullptr) {
data->user_remove();
}
}
};
} // namespace blender

View File

@@ -279,6 +279,7 @@ set(SRC
BLI_timecode.h
BLI_timeit.hh
BLI_timer.h
BLI_user_counter.hh
BLI_utildefines.h
BLI_utildefines_iter.h
BLI_utildefines_stack.h

View File

@@ -141,6 +141,12 @@ void BLI_rng_get_tri_sample_float_v2(
copy_v2_v2(r_pt, rng->rng.get_triangle_sample(v1, v2, v3));
}
void BLI_rng_get_tri_sample_float_v3(
RNG *rng, const float v1[3], const float v2[3], const float v3[3], float r_pt[3])
{
copy_v3_v3(r_pt, rng->rng.get_triangle_sample_3d(v1, v2, v3));
}
void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsigned int elem_tot)
{
const uint elem_size = elem_size_i;
@@ -425,6 +431,25 @@ float2 RandomNumberGenerator::get_triangle_sample(float2 v1, float2 v2, float2 v
return sample;
}
float3 RandomNumberGenerator::get_triangle_sample_3d(float3 v1, float3 v2, float3 v3)
{
float u = this->get_float();
float v = this->get_float();
if (u + v > 1.0f) {
u = 1.0f - u;
v = 1.0f - v;
}
float3 side_u = v2 - v1;
float3 side_v = v3 - v1;
float3 sample = v1;
sample += side_u * u;
sample += side_v * v;
return sample;
}
void RandomNumberGenerator::get_bytes(MutableSpan<char> r_bytes)
{
constexpr int64_t mask_bytes = 2;

View File

@@ -89,6 +89,7 @@
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_workspace.h"
#include "DRW_engine.h"

View File

@@ -141,6 +141,9 @@ void DEG_add_object_relation(struct DepsNodeHandle *node_handle,
void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle,
struct Simulation *simulation,
const char *description);
void DEG_add_node_tree_relation(struct DepsNodeHandle *node_handle,
struct bNodeTree *node_tree,
const char *description);
void DEG_add_bone_relation(struct DepsNodeHandle *handle,
struct Object *object,
const char *bone_name,

View File

@@ -145,6 +145,16 @@ typedef struct DEGObjectIterData {
eEvaluationMode eval_mode;
/* **** Iteration over geometry components **** */
/* The object whose components we currently iterate over.
* This might point to #temp_dupli_object. */
struct Object *geometry_component_owner;
/* Some identifier that is used to determine which geometry component should be returned next. */
int geometry_component_id;
/* Temporary storage for an object that is created from a component. */
struct Object temp_geometry_component_object;
/* **** Iteration over dupli-list. *** */
/* Object which created the dupli-list. */

View File

@@ -32,6 +32,7 @@
#include "PIL_time_utildefines.h"
#include "DNA_cachefile_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
@@ -116,6 +117,17 @@ void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
}
void DEG_add_node_tree_relation(DepsNodeHandle *node_handle,
bNodeTree *node_tree,
const char *description)
{
/* Using shading key, because that's the one that exists right now. Should use something else in
* the future. */
deg::ComponentKey shading_key(&node_tree->id, deg::NodeType::SHADING);
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(shading_key, deg_node_handle, description);
}
void DEG_add_object_cache_relation(DepsNodeHandle *node_handle,
CacheFile *cache_file,
eDepsObjectComponentType component,

View File

@@ -29,6 +29,7 @@
#include "MEM_guardedalloc.h"
#include "BKE_duplilist.h"
#include "BKE_geometry_set.hh"
#include "BKE_idprop.h"
#include "BKE_layer.h"
#include "BKE_node.h"
@@ -121,9 +122,82 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject
return false;
}
bool deg_objects_dupli_iterator_next(BLI_Iterator *iter)
void deg_iterator_components_init(DEGObjectIterData *data, Object *object)
{
data->geometry_component_owner = object;
data->geometry_component_id = 0;
}
/* Returns false when iterator is exhausted. */
bool deg_iterator_components_step(BLI_Iterator *iter)
{
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
if (data->geometry_component_owner == nullptr) {
return false;
}
if (data->geometry_component_owner->type != OB_POINTCLOUD) {
/* Only point clouds support multiple geometry components currently. */
iter->current = data->geometry_component_owner;
data->geometry_component_owner = nullptr;
return true;
}
GeometrySet *geometry_set = data->geometry_component_owner->runtime.geometry_set_eval;
if (geometry_set == nullptr) {
data->geometry_component_owner = nullptr;
return false;
}
if (data->geometry_component_id == 0) {
data->geometry_component_id++;
/* The mesh component. */
const Mesh *mesh = geometry_set->get_mesh_for_read();
if (mesh != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_MESH;
temp_object->data = (void *)mesh;
iter->current = temp_object;
return true;
}
}
if (data->geometry_component_id == 1) {
data->geometry_component_id++;
/* The pointcloud component. */
const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read();
if (pointcloud != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_POINTCLOUD;
temp_object->data = (void *)pointcloud;
iter->current = temp_object;
return true;
}
}
data->geometry_component_owner = nullptr;
return false;
}
void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object)
{
if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) &&
((object->transflag & OB_DUPLI) || object->runtime.geometry_set_eval != nullptr)) {
data->dupli_parent = object;
data->dupli_list = object_duplilist(data->graph, data->scene, object);
data->dupli_object_next = (DupliObject *)data->dupli_list->first;
}
}
/* Returns false when iterator is exhausted. */
bool deg_iterator_duplis_step(DEGObjectIterData *data)
{
if (data->dupli_list == nullptr) {
return false;
}
while (data->dupli_object_next != nullptr) {
DupliObject *dob = data->dupli_object_next;
Object *obd = dob->ob;
@@ -170,72 +244,80 @@ bool deg_objects_dupli_iterator_next(BLI_Iterator *iter)
copy_m4_m4(data->temp_dupli_object.obmat, dob->mat);
invert_m4_m4(data->temp_dupli_object.imat, data->temp_dupli_object.obmat);
iter->current = &data->temp_dupli_object;
deg_iterator_components_init(data, &data->temp_dupli_object);
BLI_assert(deg::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id));
return true;
}
verify_id_properties_freed(data);
free_object_duplilist(data->dupli_list);
data->dupli_parent = nullptr;
data->dupli_list = nullptr;
data->dupli_object_next = nullptr;
data->dupli_object_current = nullptr;
deg_invalidate_iterator_work_data(data);
return false;
}
void deg_iterator_objects_step(BLI_Iterator *iter, deg::IDNode *id_node)
/* Returns false when iterator is exhausted. */
bool deg_iterator_objects_step(DEGObjectIterData *data)
{
/* Set it early in case we need to exit and we are running from within a loop. */
iter->skip = true;
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(data->graph);
if (!id_node->is_directly_visible) {
return;
}
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
const ID_Type id_type = GS(id_node->id_orig->name);
if (id_type != ID_OB) {
return;
}
switch (id_node->linked_state) {
case deg::DEG_ID_LINKED_DIRECTLY:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
return;
}
break;
case deg::DEG_ID_LINKED_VIA_SET:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
return;
}
break;
case deg::DEG_ID_LINKED_INDIRECTLY:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
return;
}
break;
}
Object *object = (Object *)id_node->id_cow;
BLI_assert(deg::deg_validate_copy_on_write_datablock(&object->id));
int ob_visibility = OB_VISIBLE_ALL;
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
ob_visibility = BKE_object_visibility(object, data->eval_mode);
if (object->type != OB_MBALL && deg_object_hide_original(data->eval_mode, object, nullptr)) {
return;
if (!id_node->is_directly_visible) {
continue;
}
}
if (ob_visibility & OB_VISIBLE_INSTANCES) {
if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && (object->transflag & OB_DUPLI)) {
data->dupli_parent = object;
data->dupli_list = object_duplilist(data->graph, data->scene, object);
data->dupli_object_next = (DupliObject *)data->dupli_list->first;
const ID_Type id_type = GS(id_node->id_orig->name);
if (id_type != ID_OB) {
continue;
}
}
if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) {
iter->current = object;
iter->skip = false;
switch (id_node->linked_state) {
case deg::DEG_ID_LINKED_DIRECTLY:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
continue;
}
break;
case deg::DEG_ID_LINKED_VIA_SET:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
continue;
}
break;
case deg::DEG_ID_LINKED_INDIRECTLY:
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
continue;
}
break;
}
Object *object = (Object *)id_node->id_cow;
BLI_assert(deg::deg_validate_copy_on_write_datablock(&object->id));
int ob_visibility = OB_VISIBLE_ALL;
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
ob_visibility = BKE_object_visibility(object, data->eval_mode);
if (object->type != OB_MBALL && deg_object_hide_original(data->eval_mode, object, nullptr)) {
continue;
}
}
if (ob_visibility & OB_VISIBLE_INSTANCES) {
deg_iterator_duplis_init(data, object);
}
if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) {
deg_iterator_components_init(data, object);
}
data->id_node_index++;
return true;
}
return false;
}
} // namespace
@@ -261,46 +343,29 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
data->id_node_index = 0;
data->num_id_nodes = num_id_nodes;
data->eval_mode = DEG_get_mode(depsgraph);
data->geometry_component_id = 0;
data->geometry_component_owner = nullptr;
deg_invalidate_iterator_work_data(data);
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
deg_iterator_objects_step(iter, id_node);
if (iter->skip) {
DEG_iterator_objects_next(iter);
}
DEG_iterator_objects_next(iter);
}
void DEG_iterator_objects_next(BLI_Iterator *iter)
{
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
Depsgraph *depsgraph = data->graph;
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
do {
iter->skip = false;
if (data->dupli_list) {
if (deg_objects_dupli_iterator_next(iter)) {
return;
}
verify_id_properties_freed(data);
free_object_duplilist(data->dupli_list);
data->dupli_parent = nullptr;
data->dupli_list = nullptr;
data->dupli_object_next = nullptr;
data->dupli_object_current = nullptr;
deg_invalidate_iterator_work_data(data);
}
++data->id_node_index;
if (data->id_node_index == data->num_id_nodes) {
iter->valid = false;
while (true) {
if (deg_iterator_components_step(iter)) {
return;
}
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
deg_iterator_objects_step(iter, id_node);
} while (iter->skip);
if (deg_iterator_duplis_step(data)) {
continue;
}
if (deg_iterator_objects_step(data)) {
continue;
}
iter->valid = false;
break;
}
}
void DEG_iterator_objects_end(BLI_Iterator *iter)

View File

@@ -96,7 +96,7 @@ void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typein
bool ED_node_is_compositor(struct SpaceNode *snode);
bool ED_node_is_shader(struct SpaceNode *snode);
bool ED_node_is_texture(struct SpaceNode *snode);
bool ED_node_is_simulation(struct SpaceNode *snode);
bool ED_node_is_geometry(struct SpaceNode *snode);
void ED_node_shader_default(const struct bContext *C, struct ID *id);
void ED_node_composit_default(const struct bContext *C, struct Scene *scene);

View File

@@ -90,6 +90,8 @@
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "MOD_nodes.h"
#include "UI_interface.h"
#include "WM_api.h"
@@ -232,6 +234,9 @@ ModifierData *ED_object_modifier_add(
/* ensure skin-node customdata exists */
BKE_mesh_ensure_skin_customdata(ob->data);
}
else if (type == eModifierType_Empty) {
MOD_nodes_init(bmain, (NodesModifierData *)new_md);
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);

View File

@@ -72,5 +72,8 @@ if(WITH_OPENIMAGEDENOISE)
add_definitions(-DWITH_OPENIMAGEDENOISE)
endif()
if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -68,8 +68,8 @@
#include "IMB_imbuf_types.h"
#include "NOD_composite.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_simulation.h"
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
@@ -3139,10 +3139,64 @@ static void node_texture_set_butfunc(bNodeType *ntype)
}
}
/* ****************** BUTTON CALLBACKS FOR SIMULATION NODES ***************** */
/* ****************** BUTTON CALLBACKS FOR GEOMETRY NODES ***************** */
static void node_simulation_set_butfunc(bNodeType *UNUSED(ntype))
static void node_geometry_buts_attribute_math(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_attribute_random(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_boolean_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_subdivision_surface(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *UNUSED(ptr))
{
#ifndef WITH_OPENSUBDIV
uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
#else
UNUSED_VARS(layout);
#endif
}
static void node_geometry_buts_triangulate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "quad_method", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "ngon_method", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
case GEO_NODE_ATTRIBUTE_MATH:
ntype->draw_buttons = node_geometry_buts_attribute_math;
break;
case GEO_NODE_ATTRIBUTE_RANDOM:
ntype->draw_buttons = node_geometry_buts_attribute_random;
break;
case GEO_NODE_BOOLEAN:
ntype->draw_buttons = node_geometry_buts_boolean_math;
break;
case GEO_NODE_SUBDIVISION_SURFACE:
ntype->draw_buttons = node_geometry_buts_subdivision_surface;
break;
case GEO_NODE_TRIANGULATE:
ntype->draw_buttons = node_geometry_buts_triangulate;
break;
}
}
/* ****************** BUTTON CALLBACKS FOR FUNCTION NODES ***************** */
@@ -3287,7 +3341,7 @@ void ED_node_init_butfuncs(void)
node_composit_set_butfunc(ntype);
node_shader_set_butfunc(ntype);
node_texture_set_butfunc(ntype);
node_simulation_set_butfunc(ntype);
node_geometry_set_butfunc(ntype);
node_function_set_butfunc(ntype);
/* define update callbacks for socket properties */
@@ -3299,7 +3353,7 @@ void ED_node_init_butfuncs(void)
ntreeType_Composite->ui_icon = ICON_NODE_COMPOSITING;
ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL;
ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE;
ntreeType_Simulation->ui_icon = ICON_PHYSICS; /* TODO: Use correct icon. */
ntreeType_Geometry->ui_icon = ICON_PHYSICS; /* TODO: Use correct icon. */
}
void ED_init_custom_node_type(bNodeType *ntype)
@@ -3328,8 +3382,9 @@ static const float std_node_socket_colors[][4] = {
{0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */
{0.06, 0.52, 0.15, 1.0}, /* SOCK_INT */
{0.39, 0.39, 0.39, 1.0}, /* SOCK_STRING */
{0.40, 0.10, 0.10, 1.0}, /* SOCK_OBJECT */
{0.85, 0.34, 0.11, 1.0}, /* SOCK_OBJECT */
{0.10, 0.40, 0.10, 1.0}, /* SOCK_IMAGE */
{0.00, 0.83, 0.64, 1.0}, /* SOCK_GEOMETRY */
};
/* common color callbacks for standard types */

View File

@@ -138,6 +138,9 @@ void ED_node_tag_update_id(ID *id)
DEG_id_tag_update(id, 0);
WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
}
else if (ntree->type == NTREE_GEOMETRY) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
else if (id == &ntree->id) {
/* node groups */
DEG_id_tag_update(id, 0);

View File

@@ -68,8 +68,8 @@
#include "IMB_imbuf_types.h"
#include "NOD_composite.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_simulation.h"
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
@@ -391,6 +391,7 @@ void snode_dag_update(bContext *C, SpaceNode *snode)
}
DEG_id_tag_update(snode->id, 0);
DEG_id_tag_update(&snode->nodetree->id, 0);
}
void snode_notify(bContext *C, SpaceNode *snode)
@@ -416,6 +417,9 @@ void snode_notify(bContext *C, SpaceNode *snode)
else if (ED_node_is_texture(snode)) {
WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, id);
}
else if (ED_node_is_geometry(snode)) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
}
void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo)
@@ -443,9 +447,9 @@ bool ED_node_is_texture(struct SpaceNode *snode)
return STREQ(snode->tree_idname, ntreeType_Texture->idname);
}
bool ED_node_is_simulation(struct SpaceNode *snode)
bool ED_node_is_geometry(struct SpaceNode *snode)
{
return STREQ(snode->tree_idname, ntreeType_Simulation->idname);
return STREQ(snode->tree_idname, ntreeType_Geometry->idname);
}
/* assumes nothing being done in ntree yet, sets the default in/out node */
@@ -1696,7 +1700,7 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1740,7 +1744,7 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(CTX_data_main(C), snode->edittree);

View File

@@ -77,7 +77,7 @@ static bool node_group_operator_active_poll(bContext *C)
"ShaderNodeTree",
"CompositorNodeTree",
"TextureNodeTree",
"SimulationNodeTree")) {
"GeometryNodeTree")) {
return true;
}
}
@@ -94,7 +94,7 @@ static bool node_group_operator_editable(bContext *C)
* with same keymap.
*/
if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
ED_node_is_simulation(snode)) {
ED_node_is_geometry(snode)) {
return true;
}
}
@@ -120,8 +120,8 @@ static const char *group_node_idname(bContext *C)
if (ED_node_is_texture(snode)) {
return "TextureNodeGroup";
}
if (ED_node_is_simulation(snode)) {
return "SimulationNodeGroup";
if (ED_node_is_geometry(snode)) {
return "GeometryNodeGroup";
}
return "";

View File

@@ -655,7 +655,7 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
@@ -1052,7 +1052,7 @@ static int cut_links_exec(bContext *C, wmOperator *op)
}
}
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
@@ -1932,6 +1932,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */
snode_update(snode, select);
ED_node_tag_update_id((ID *)snode->edittree);
ED_node_tag_update_id(snode->id);
}
}

View File

@@ -936,7 +936,7 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
const EnumPropertyItem *item_src = RNA_enum_node_tree_types_itemf_impl(C, &free);
for (const EnumPropertyItem *item_iter = item_src; item_iter->identifier; item_iter++) {
if (!U.experimental.use_new_geometry_nodes &&
STREQ(item_iter->identifier, "SimulationNodeTree")) {
STREQ(item_iter->identifier, "GeometryNodeTree")) {
continue;
}
RNA_enum_item_add(item, totitem, item_iter);

View File

@@ -38,6 +38,8 @@ set(SRC
FN_array_spans.hh
FN_attributes_ref.hh
FN_cpp_type.hh
FN_generic_pointer.hh
FN_generic_value_map.hh
FN_generic_vector_array.hh
FN_multi_function.hh
FN_multi_function_builder.hh

View File

@@ -91,6 +91,14 @@ class CPPType : NonCopyable, NonMovable {
using CopyToUninitializedNF = void (*)(const void *src, void *dst, int64_t n);
using CopyToUninitializedIndicesF = void (*)(const void *src, void *dst, IndexMask mask);
using MoveToInitializedF = void (*)(void *src, void *dst);
using MoveToInitializedNF = void (*)(void *src, void *dst, int64_t n);
using MoveToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
using MoveToUninitializedF = void (*)(void *src, void *dst);
using MoveToUninitializedNF = void (*)(void *src, void *dst, int64_t n);
using MoveToUninitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
using RelocateToInitializedF = void (*)(void *src, void *dst);
using RelocateToInitializedNF = void (*)(void *src, void *dst, int64_t n);
using RelocateToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
@@ -131,6 +139,14 @@ class CPPType : NonCopyable, NonMovable {
CopyToUninitializedNF copy_to_uninitialized_n_;
CopyToUninitializedIndicesF copy_to_uninitialized_indices_;
MoveToInitializedF move_to_initialized_;
MoveToInitializedNF move_to_initialized_n_;
MoveToInitializedIndicesF move_to_initialized_indices_;
MoveToUninitializedF move_to_uninitialized_;
MoveToUninitializedNF move_to_uninitialized_n_;
MoveToUninitializedIndicesF move_to_uninitialized_indices_;
RelocateToInitializedF relocate_to_initialized_;
RelocateToInitializedNF relocate_to_initialized_n_;
RelocateToInitializedIndicesF relocate_to_initialized_indices_;
@@ -169,6 +185,12 @@ class CPPType : NonCopyable, NonMovable {
CopyToUninitializedF copy_to_uninitialized,
CopyToUninitializedNF copy_to_uninitialized_n,
CopyToUninitializedIndicesF copy_to_uninitialized_indices,
MoveToInitializedF move_to_initialized,
MoveToInitializedNF move_to_initialized_n,
MoveToInitializedIndicesF move_to_initialized_indices,
MoveToUninitializedF move_to_uninitialized,
MoveToUninitializedNF move_to_uninitialized_n,
MoveToUninitializedIndicesF move_to_uninitialized_indices,
RelocateToInitializedF relocate_to_initialized,
RelocateToInitializedNF relocate_to_initialized_n,
RelocateToInitializedIndicesF relocate_to_initialized_indices,
@@ -198,6 +220,12 @@ class CPPType : NonCopyable, NonMovable {
copy_to_uninitialized_(copy_to_uninitialized),
copy_to_uninitialized_n_(copy_to_uninitialized_n),
copy_to_uninitialized_indices_(copy_to_uninitialized_indices),
move_to_initialized_(move_to_initialized),
move_to_initialized_n_(move_to_initialized_n),
move_to_initialized_indices_(move_to_initialized_indices),
move_to_uninitialized_(move_to_uninitialized),
move_to_uninitialized_n_(move_to_uninitialized_n),
move_to_uninitialized_indices_(move_to_uninitialized_indices),
relocate_to_initialized_(relocate_to_initialized),
relocate_to_initialized_n_(relocate_to_initialized_n),
relocate_to_initialized_indices_(relocate_to_initialized_indices),
@@ -421,6 +449,76 @@ class CPPType : NonCopyable, NonMovable {
copy_to_uninitialized_indices_(src, dst, mask);
}
/**
* Move an instance of this type from src to dst.
*
* The memory pointed to by dst should be initialized.
*
* C++ equivalent:
* dst = std::move(src);
*/
void move_to_initialized(void *src, void *dst) const
{
BLI_assert(src != dst);
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
move_to_initialized_(src, dst);
}
void move_to_initialized_n(void *src, void *dst, int64_t n) const
{
BLI_assert(n == 0 || src != dst);
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
move_to_initialized_n_(src, dst, n);
}
void move_to_initialized_indices(void *src, void *dst, IndexMask mask) const
{
BLI_assert(mask.size() == 0 || src != dst);
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
move_to_initialized_indices_(src, dst, mask);
}
/**
* Move an instance of this type from src to dst.
*
* The memory pointed to by dst should be uninitialized.
*
* C++ equivalent:
* new (dst) T(std::move(src));
*/
void move_to_uninitialized(void *src, void *dst) const
{
BLI_assert(src != dst);
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
move_to_uninitialized_(src, dst);
}
void move_to_uninitialized_n(void *src, void *dst, int64_t n) const
{
BLI_assert(n == 0 || src != dst);
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
move_to_uninitialized_n_(src, dst, n);
}
void move_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const
{
BLI_assert(mask.size() == 0 || src != dst);
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
move_to_uninitialized_indices_(src, dst, mask);
}
/**
* Relocates an instance of this type from src to dst. src will point to uninitialized memory
* afterwards.
@@ -644,6 +742,38 @@ void copy_to_uninitialized_indices_cb(const void *src, void *dst, IndexMask mask
mask.foreach_index([&](int64_t i) { new (dst_ + i) T(src_[i]); });
}
template<typename T> void move_to_initialized_cb(void *src, void *dst)
{
blender::initialized_move_n(static_cast<T *>(src), 1, static_cast<T *>(dst));
}
template<typename T> void move_to_initialized_n_cb(void *src, void *dst, int64_t n)
{
blender::initialized_move_n(static_cast<T *>(src), n, static_cast<T *>(dst));
}
template<typename T> void move_to_initialized_indices_cb(void *src, void *dst, IndexMask mask)
{
T *src_ = static_cast<T *>(src);
T *dst_ = static_cast<T *>(dst);
mask.foreach_index([&](int64_t i) { dst_[i] = std::move(src_[i]); });
}
template<typename T> void move_to_uninitialized_cb(void *src, void *dst)
{
blender::uninitialized_move_n(static_cast<T *>(src), 1, static_cast<T *>(dst));
}
template<typename T> void move_to_uninitialized_n_cb(void *src, void *dst, int64_t n)
{
blender::uninitialized_move_n(static_cast<T *>(src), n, static_cast<T *>(dst));
}
template<typename T> void move_to_uninitialized_indices_cb(void *src, void *dst, IndexMask mask)
{
T *src_ = static_cast<T *>(src);
T *dst_ = static_cast<T *>(dst);
mask.foreach_index([&](int64_t i) { new (dst_ + i) T(std::move(src_[i])); });
}
template<typename T> void relocate_to_initialized_cb(void *src, void *dst)
{
T *src_ = static_cast<T *>(src);
@@ -767,6 +897,12 @@ inline std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &d
copy_to_uninitialized_cb<T>,
copy_to_uninitialized_n_cb<T>,
copy_to_uninitialized_indices_cb<T>,
move_to_initialized_cb<T>,
move_to_initialized_n_cb<T>,
move_to_initialized_indices_cb<T>,
move_to_uninitialized_cb<T>,
move_to_uninitialized_n_cb<T>,
move_to_uninitialized_indices_cb<T>,
relocate_to_initialized_cb<T>,
relocate_to_initialized_n_cb<T>,
relocate_to_initialized_indices_cb<T>,

View File

@@ -0,0 +1,76 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "FN_cpp_type.hh"
namespace blender::fn {
/**
* A generic pointer whose type is only known at runtime.
*/
class GMutablePointer {
private:
const CPPType *type_ = nullptr;
void *data_ = nullptr;
public:
GMutablePointer() = default;
GMutablePointer(const CPPType *type, void *data = nullptr) : type_(type), data_(data)
{
/* If there is data, there has to be a type. */
BLI_assert(data_ == nullptr || type_ != nullptr);
}
GMutablePointer(const CPPType &type, void *data = nullptr) : GMutablePointer(&type, data)
{
}
template<typename T> GMutablePointer(T *data) : GMutablePointer(&CPPType::get<T>(), data)
{
}
void *get() const
{
return data_;
}
const CPPType *type() const
{
return type_;
}
template<typename T> T *get() const
{
BLI_assert(this->is_type<T>());
return reinterpret_cast<T *>(data_);
}
template<typename T> bool is_type() const
{
return type_ != nullptr && type_->is<T>();
}
void destruct()
{
BLI_assert(data_ != nullptr);
type_->destruct(data_);
}
};
} // namespace blender::fn

View File

@@ -0,0 +1,113 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "BLI_linear_allocator.hh"
#include "BLI_map.hh"
#include "FN_generic_pointer.hh"
namespace blender::fn {
/**
* This is a map that stores key-value-pairs. What makes it special is that the type of values does
* not have to be known at compile time. There just has to be a corresponding CPPType.
*/
template<typename Key> class GValueMap {
private:
/* Used to allocate values owned by this container. */
LinearAllocator<> &allocator_;
Map<Key, GMutablePointer> values_;
public:
GValueMap(LinearAllocator<> &allocator) : allocator_(allocator)
{
}
~GValueMap()
{
/* Destruct all values that are still in the map. */
for (GMutablePointer value : values_.values()) {
value.destruct();
}
}
/* Add a value to the container. The container becomes responsible for destructing the value that
* is passed in. The caller remains responsible for freeing the value after it has been
* destructed. */
template<typename ForwardKey> void add_new_direct(ForwardKey &&key, GMutablePointer value)
{
values_.add_new_as(std::forward<ForwardKey>(key), value);
}
/* Add a value to the container that is move constructed from the given value. The caller remains
* responsible for destructing and freeing the given value. */
template<typename ForwardKey> void add_new_by_move(ForwardKey &&key, GMutablePointer value)
{
const CPPType &type = *value.type();
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.move_to_uninitialized(value.get(), buffer);
values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
}
/* Add a value to the container that is copy constructed from the given value. The caller remains
* responsible for destructing and freeing the given value. */
template<typename ForwardKey> void add_new_by_copy(ForwardKey &&key, GMutablePointer value)
{
const CPPType &type = *value.type();
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.copy_to_uninitialized(value.get(), buffer);
values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
}
/* Add a value to the container. */
template<typename ForwardKey, typename T> void add_new(ForwardKey &&key, T &&value)
{
if constexpr (std::is_rvalue_reference_v<T>) {
this->add_new_by_move(std::forward<ForwardKey>(key), &value);
}
else {
this->add_new_by_copy(std::forward<ForwardKey>(key), &value);
}
}
/* Remove the value for the given name from the container and remove it. The caller is
* responsible for freeing it. The lifetime of the referenced memory might be bound to lifetime
* of the container. */
template<typename ForwardKey> GMutablePointer extract(const ForwardKey &key)
{
return values_.pop_as(key);
}
/* Remove the value for the given name from the container and remove it. */
template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
{
GMutablePointer value = values_.pop_as(key);
const CPPType &type = *value.type();
BLI_assert(type.is<T>());
T return_value;
type.relocate_to_initialized(value.get(), &return_value);
return return_value;
}
template<typename ForwardKey> bool contains(const ForwardKey &key) const
{
return values_.contains_as(key);
}
};
} // namespace blender::fn

View File

@@ -573,7 +573,7 @@
.flag = 0, \
}
#define _DNA_DEFAULT_SimulationModifierData \
#define _DNA_DEFAULT_NodesModifierData \
{ 0 }
#define _DNA_DEFAULT_SkinModifierData \

View File

@@ -94,7 +94,7 @@ typedef enum ModifierType {
eModifierType_WeightedNormal = 54,
eModifierType_Weld = 55,
eModifierType_Fluid = 56,
eModifierType_Simulation = 57,
eModifierType_Empty = 57,
eModifierType_MeshToVolume = 58,
eModifierType_VolumeDisplace = 59,
eModifierType_VolumeToMesh = 60,
@@ -2220,9 +2220,16 @@ enum {
#define MOD_MESHSEQ_READ_ALL \
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
typedef struct SimulationModifierData {
typedef struct NodesModifierSettings {
/* This stores data that is passed into the node group. */
struct IDProperty *properties;
} NodesModifierSettings;
typedef struct NodesModifierData {
ModifierData modifier;
} SimulationModifierData;
struct bNodeTree *node_group;
struct NodesModifierSettings settings;
} NodesModifierData;
typedef struct MeshToVolumeModifierData {
ModifierData modifier;

View File

@@ -155,6 +155,7 @@ typedef enum eNodeSocketDatatype {
SOCK_STRING = 7,
SOCK_OBJECT = 8,
SOCK_IMAGE = 9,
SOCK_GEOMETRY = 10,
} eNodeSocketDatatype;
/* socket shape */
@@ -499,7 +500,7 @@ typedef struct bNodeTree {
#define NTREE_SHADER 0
#define NTREE_COMPOSIT 1
#define NTREE_TEXTURE 2
#define NTREE_SIMULATION 3
#define NTREE_GEOMETRY 3
/* ntree->init, flag */
#define NTREE_TYPE_INIT 1
@@ -1432,14 +1433,24 @@ typedef enum NodeShaderOutputTarget {
SHD_OUTPUT_CYCLES = 2,
} NodeShaderOutputTarget;
/* Particle Time Step Event node */
typedef enum NodeSimParticleTimeStepEventType {
NODE_PARTICLE_TIME_STEP_EVENT_BEGIN = 0,
NODE_PARTICLE_TIME_STEP_EVENT_END = 1,
} NodeSimParticleTimeStepEventType;
/* Geometry Nodes */
/* Simulation Time node */
typedef enum NodeSimInputTimeType {
NODE_SIM_INPUT_SIMULATION_TIME = 0,
NODE_SIM_INPUT_SCENE_TIME = 1,
} NodeSimInputTimeType;
/* Boolean Node */
typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_INTERSECT = 0,
GEO_NODE_BOOLEAN_UNION = 1,
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
} GeometryNodeBooleanOperation;
/* Triangulate Node */
typedef enum GeometryNodeTriangulateNGons {
GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0,
GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1,
} GeometryNodeTriangulateNGons;
typedef enum GeometryNodeTriangulateQuads {
GEO_NODE_TRIANGULATE_QUAD_BEAUTY = 0,
GEO_NODE_TRIANGULATE_QUAD_FIXED = 1,
GEO_NODE_TRIANGULATE_QUAD_ALTERNATE = 2,
GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE = 3,
} GeometryNodeTriangulateQuads;

View File

@@ -51,6 +51,7 @@ struct RigidBodyOb;
struct SculptSession;
struct SoftBody;
struct bGPdata;
struct GeometrySet;
/* Vertex Groups - Name Info */
typedef struct bDeformGroup {
@@ -150,6 +151,13 @@ typedef struct Object_Runtime {
* It has all modifiers applied.
*/
struct ID *data_eval;
/**
* Some objects support evaluating to a geometry set instead of a single ID. In those cases the
* evaluated geometry will be stored here instead of in #data_eval.
*/
struct GeometrySet *geometry_set_eval;
/**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.

View File

@@ -271,7 +271,7 @@ SDNA_DEFAULT_DECL_STRUCT(ScrewModifierData);
/* Shape key modifier has no items. */
SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapModifierData);
SDNA_DEFAULT_DECL_STRUCT(SimpleDeformModifierData);
SDNA_DEFAULT_DECL_STRUCT(SimulationModifierData);
SDNA_DEFAULT_DECL_STRUCT(NodesModifierData);
SDNA_DEFAULT_DECL_STRUCT(SkinModifierData);
SDNA_DEFAULT_DECL_STRUCT(SmoothModifierData);
/* Softbody modifier skipped for now. */
@@ -491,7 +491,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* Shape key modifier has no items. */
SDNA_DEFAULT_DECL(ShrinkwrapModifierData),
SDNA_DEFAULT_DECL(SimpleDeformModifierData),
SDNA_DEFAULT_DECL(SimulationModifierData),
SDNA_DEFAULT_DECL(NodesModifierData),
SDNA_DEFAULT_DECL(SkinModifierData),
SDNA_DEFAULT_DECL(SmoothModifierData),
/* Softbody modifier skipped for now. */

View File

@@ -566,10 +566,11 @@ extern StructRNA RNA_SimpleDeformModifier;
extern StructRNA RNA_SimplifyGpencilModifier;
extern StructRNA RNA_Simulation;
#ifdef WITH_GEOMETRY_NODES
extern StructRNA RNA_SimulationModifier;
extern StructRNA RNA_NodesModifier;
extern StructRNA RNA_NodesModifierSettings;
#endif
extern StructRNA RNA_SimulationNode;
extern StructRNA RNA_SimulationNodeTree;
extern StructRNA RNA_GeometryNode;
extern StructRNA RNA_GeometryNodeTree;
extern StructRNA RNA_SkinModifier;
extern StructRNA RNA_SmoothGpencilModifier;
extern StructRNA RNA_SmoothModifier;

View File

@@ -373,6 +373,7 @@ blender_include_dirs(
../../ikplugin
../../imbuf
../../makesdna
../../modifiers
../../nodes/
../../sequencer
../../simulation

View File

@@ -1160,8 +1160,8 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
IDProperty *idprop = (IDProperty *)prop;
/* Restrict to arrays only for now for performance reasons. */
if (idprop->type == IDP_ARRAY && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
if (ELEM(idprop->type, IDP_INT, IDP_FLOAT, IDP_DOUBLE) ||
((idprop->type == IDP_ARRAY) && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE))) {
const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {

View File

@@ -29,7 +29,6 @@
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
#include "MEM_guardedalloc.h"
@@ -43,6 +42,7 @@
#include "BKE_dynamicpaint.h"
#include "BKE_effect.h"
#include "BKE_fluid.h" /* For BKE_fluid_modifier_free & BKE_fluid_modifier_create_type_data */
#include "BKE_idprop.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remap.h"
#include "BKE_multires.h"
@@ -57,6 +57,8 @@
#include "WM_api.h"
#include "WM_types.h"
#include "MOD_nodes.h"
const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{0, "", 0, N_("Modify"), ""},
{eModifierType_DataTransfer,
@@ -141,6 +143,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_EDGESPLIT,
"Edge Split",
"Split away joined faces at the edges"},
{eModifierType_Empty, "EMPTY", ICON_MESH_DATA, "Empty", ""}, /* TODO: Use correct icon. */
{eModifierType_Mask,
"MASK",
ICON_MOD_MASK,
@@ -305,11 +308,6 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
"Spawn particles from the shape"},
{eModifierType_Softbody, "SOFT_BODY", ICON_MOD_SOFT, "Soft Body", ""},
{eModifierType_Surface, "SURFACE", ICON_MODIFIER, "Surface", ""},
{eModifierType_Simulation,
"SIMULATION",
ICON_PHYSICS,
"Simulation",
""}, /* TODO: Use correct icon. */
{0, NULL, 0, NULL, NULL},
};
@@ -1588,6 +1586,37 @@ static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr)
# endif
}
static bool rna_NodesModifier_node_group_poll(PointerRNA *ptr, PointerRNA value)
{
NodesModifierData *nmd = ptr->data;
bNodeTree *ntree = value.data;
UNUSED_VARS(nmd, ntree);
return true;
}
static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *object = (Object *)ptr->owner_id;
NodesModifierData *nmd = ptr->data;
rna_Modifier_dependency_update(bmain, scene, ptr);
MOD_nodes_update_interface(object, nmd);
}
static IDProperty *rna_NodesModifierSettings_properties(PointerRNA *ptr, bool create)
{
NodesModifierSettings *settings = ptr->data;
if (create && settings->properties == NULL) {
IDPropertyTemplate val = {0};
settings->properties = IDP_New(IDP_GROUP, &val, "Nodes Modifier Settings");
}
return settings->properties;
}
static char *rna_NodesModifierSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("settings");
}
#else
static void rna_def_property_subdivision_common(StructRNA *srna)
@@ -6902,18 +6931,43 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
}
# ifdef WITH_GEOMETRY_NODES
static void rna_def_modifier_simulation(BlenderRNA *brna)
static void rna_def_modifier_nodes_settings(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "SimulationModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Simulation Modifier", "");
RNA_def_struct_sdna(srna, "SimulationModifierData");
RNA_def_struct_ui_icon(srna, ICON_PHYSICS); /* TODO: Use correct icon. */
srna = RNA_def_struct(brna, "NodesModifierSettings", NULL);
RNA_def_struct_nested(brna, srna, "NodesModifier");
RNA_def_struct_path_func(srna, "rna_NodesModifierSettings_path");
RNA_def_struct_ui_text(
srna, "Nodes Modifier Settings", "Settings that are passed into the node group");
RNA_def_struct_idprops_func(srna, "rna_NodesModifierSettings_properties");
}
static void rna_def_modifier_nodes(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodesModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
RNA_def_struct_sdna(srna, "NodesModifierData");
RNA_def_struct_ui_icon(srna, ICON_MESH_DATA); /* TODO: Use correct icon. */
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "node_group", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Node Group", "Node group that controls what this modifier does");
RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_NodesModifier_node_group_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, 0, "rna_NodesModifier_node_group_update");
prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_ui_text(prop, "Settings", "Settings that are passed into the node group");
RNA_define_lib_overridable(false);
rna_def_modifier_nodes_settings(brna);
}
# endif
@@ -7272,7 +7326,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_surfacedeform(brna);
rna_def_modifier_weightednormal(brna);
# ifdef WITH_GEOMETRY_NODES
rna_def_modifier_simulation(brna);
rna_def_modifier_nodes(brna);
# endif
rna_def_modifier_mesh_to_volume(brna);
rna_def_modifier_volume_displace(brna);

View File

@@ -27,6 +27,7 @@
#include "BLT_translation.h"
#include "DNA_customdata_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
@@ -36,9 +37,9 @@
#include "DNA_texture_types.h"
#include "BKE_animsys.h"
#include "BKE_attribute.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_simulation.h"
#include "BKE_texture.h"
#include "RNA_access.h"
@@ -84,6 +85,7 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_SHADER, "SHADER", 0, "Shader", ""},
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -96,6 +98,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
{SOCK_RGBA, "RGBA", 0, "Color", ""},
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -367,6 +370,60 @@ static const EnumPropertyItem prop_shader_output_target_items[] = {
{SHD_OUTPUT_CYCLES, "CYCLES", 0, "Cycles", "Use shaders for Cycles renderer"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_boolean_method_items[] = {
{GEO_NODE_BOOLEAN_INTERSECT,
"INTERSECT",
0,
"Intersect",
"Keep the part of the mesh that is common between all operands"},
{GEO_NODE_BOOLEAN_UNION, "UNION", 0, "Union", "Combine meshes in an additive way"},
{GEO_NODE_BOOLEAN_DIFFERENCE,
"DIFFERENCE",
0,
"Difference",
"Combine meshes in a subtractive way"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_triangulate_quad_method_items[] = {
{GEO_NODE_TRIANGULATE_QUAD_BEAUTY,
"BEAUTY",
0,
"Beauty",
"Split the quads in nice triangles, slower method"},
{GEO_NODE_TRIANGULATE_QUAD_FIXED,
"FIXED",
0,
"Fixed",
"Split the quads on the first and third vertices"},
{GEO_NODE_TRIANGULATE_QUAD_ALTERNATE,
"FIXED_ALTERNATE",
0,
"Fixed Alternate",
"Split the quads on the 2nd and 4th vertices"},
{GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE,
"SHORTEST_DIAGONAL",
0,
"Shortest Diagonal",
"Split the quads based on the distance between the vertices"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_triangulate_ngon_method_items[] = {
{GEO_NODE_TRIANGULATE_NGON_BEAUTY,
"BEAUTY",
0,
"Beauty",
"Arrange the new triangles evenly (slow)"},
{GEO_NODE_TRIANGULATE_NGON_EARCLIP,
"CLIP",
0,
"Clip",
"Split the polygons with an ear clipping algorithm"},
{0, NULL, 0, NULL, NULL},
};
#endif
#ifdef RNA_RUNTIME
@@ -725,9 +782,9 @@ static const EnumPropertyItem *rna_node_static_type_itemf(bContext *UNUSED(C),
# undef DefNode
}
if (RNA_struct_is_a(ptr->type, &RNA_SimulationNode)) {
if (RNA_struct_is_a(ptr->type, &RNA_GeometryNode)) {
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
if (STREQ(#Category, "SimulationNode")) { \
if (STREQ(#Category, "GeometryNode")) { \
tmp.value = ID; \
tmp.identifier = EnumName; \
tmp.name = UIName; \
@@ -1868,16 +1925,16 @@ static StructRNA *rna_TextureNode_register(Main *bmain,
return nt->rna_ext.srna;
}
static StructRNA *rna_SimulationNode_register(Main *bmain,
ReportList *reports,
void *data,
const char *identifier,
StructValidateFunc validate,
StructCallbackFunc call,
StructFreeFunc free)
static StructRNA *rna_GeometryNode_register(Main *bmain,
ReportList *reports,
void *data,
const char *identifier,
StructValidateFunc validate,
StructCallbackFunc call,
StructFreeFunc free)
{
bNodeType *nt = rna_Node_register_base(
bmain, reports, &RNA_SimulationNode, data, identifier, validate, call, free);
bmain, reports, &RNA_GeometryNode, data, identifier, validate, call, free);
if (!nt) {
return NULL;
}
@@ -2840,6 +2897,7 @@ static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C,
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
Main *bmain = CTX_data_main(C);
ntreeUpdateTree(bmain, ntree);
DEG_relations_tag_update(bmain);
}
/* ******** Node Types ******** */
@@ -8168,6 +8226,76 @@ static void def_tex_bricks(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/* -- Geometry Nodes --------------------------------------------------------- */
static void def_geo_boolean(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_boolean_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_BOOLEAN_INTERSECT);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_create_common(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_VERTEX);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_math(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_vec_math_items);
RNA_def_property_enum_default(prop, NODE_VECTOR_MATH_ADD);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_random(StructRNA *srna)
{
def_geo_attribute_create_common(srna);
}
static void def_geo_triangulate(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "quad_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_triangulate_quad_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE);
RNA_def_property_ui_text(prop, "Quad Method", "Method for splitting the quads into triangles");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "ngon_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_node_geometry_triangulate_ngon_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_TRIANGULATE_NGON_BEAUTY);
RNA_def_property_ui_text(
prop, "Polygon Method", "Method for splitting the polygons into triangles");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -8205,14 +8333,14 @@ static void rna_def_texture_node(BlenderRNA *brna)
RNA_def_struct_register_funcs(srna, "rna_TextureNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_simulation_node(BlenderRNA *brna)
static void rna_def_geometry_node(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "SimulationNode", "NodeInternal");
RNA_def_struct_ui_text(srna, "Simulation Node", "");
srna = RNA_def_struct(brna, "GeometryNode", "NodeInternal");
RNA_def_struct_ui_text(srna, "Geometry Node", "");
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_register_funcs(srna, "rna_SimulationNode_register", "rna_Node_unregister", NULL);
RNA_def_struct_register_funcs(srna, "rna_GeometryNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_function_node(BlenderRNA *brna)
@@ -8859,6 +8987,21 @@ static void rna_def_node_socket_image(BlenderRNA *brna,
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
}
static void rna_def_node_socket_geometry(BlenderRNA *brna,
const char *identifier,
const char *interface_idname)
{
StructRNA *srna;
srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
RNA_def_struct_ui_text(srna, "Geometry Node Socket", "Geometry socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
RNA_def_struct_ui_text(srna, "Geometry Node Socket Interface", "Geometry socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
}
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
{
/* XXX Workaround: Registered functions are not exposed in python by bpy,
@@ -8997,6 +9140,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_object(brna, "NodeSocketObject", "NodeSocketInterfaceObject");
rna_def_node_socket_image(brna, "NodeSocketImage", "NodeSocketInterfaceImage");
rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry");
}
static void rna_def_internal_node(BlenderRNA *brna)
@@ -9634,7 +9779,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
{NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"},
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
{NTREE_SIMULATION, "SIMULATION", ICON_PHYSICS, "Simulation", "Simulation nodes"},
{NTREE_GEOMETRY, "GEOMETRY", ICON_MESH_DATA, "Geometry", "Geometry nodes"},
{0, NULL, 0, NULL, NULL},
};
@@ -9858,15 +10003,15 @@ static void rna_def_texture_nodetree(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
}
static void rna_def_simulation_nodetree(BlenderRNA *brna)
static void rna_def_geometry_nodetree(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "SimulationNodeTree", "NodeTree");
srna = RNA_def_struct(brna, "GeometryNodeTree", "NodeTree");
RNA_def_struct_ui_text(
srna, "Simulation Node Tree", "Node tree consisting of linked nodes used for simulations");
srna, "Geometry Node Tree", "Node tree consisting of linked nodes used for geometries");
RNA_def_struct_sdna(srna, "bNodeTree");
RNA_def_struct_ui_icon(srna, ICON_PHYSICS); /* TODO: Use correct icon. */
RNA_def_struct_ui_icon(srna, ICON_MESH_DATA); /* TODO: Use correct icon. */
}
static StructRNA *define_specific_node(BlenderRNA *brna,
@@ -9955,7 +10100,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_shader_node(brna);
rna_def_compositor_node(brna);
rna_def_texture_node(brna);
rna_def_simulation_node(brna);
rna_def_geometry_node(brna);
rna_def_function_node(brna);
rna_def_nodetree(brna);
@@ -9965,7 +10110,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_composite_nodetree(brna);
rna_def_shader_nodetree(brna);
rna_def_texture_nodetree(brna);
rna_def_simulation_nodetree(brna);
rna_def_geometry_nodetree(brna);
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
{ \
@@ -9982,13 +10127,13 @@ void RNA_def_nodetree(BlenderRNA *brna)
*/
# include "../../nodes/NOD_static_types.h"
/* Node group types need to be defined for shader, compositor, texture, simulation nodes
/* Node group types need to be defined for shader, compositor, texture, geometry nodes
* individually. Cannot use the static types header for this, since they share the same int id.
*/
define_specific_node(brna, "ShaderNodeGroup", "ShaderNode", "Group", "", def_group);
define_specific_node(brna, "CompositorNodeGroup", "CompositorNode", "Group", "", def_group);
define_specific_node(brna, "TextureNodeGroup", "TextureNode", "Group", "", def_group);
define_specific_node(brna, "SimulationNodeGroup", "SimulationNode", "Group", "", def_group);
define_specific_node(brna, "GeometryNodeGroup", "GeometryNode", "Group", "", def_group);
def_custom_group(brna,
"ShaderNodeCustomGroup",
"ShaderNode",

View File

@@ -44,7 +44,6 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_sequence_types.h"
#include "DNA_simulation_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_workspace_types.h"
@@ -2179,40 +2178,6 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *
ED_node_tree_update(C);
}
# ifdef WITH_GEOMETRY_NODES
static PointerRNA rna_SpaceNodeEditor_simulation_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
ID *id = snode->id;
if (id && GS(id->name) == ID_SIM) {
return rna_pointer_inherit_refine(ptr, &RNA_Simulation, snode->id);
}
else {
return PointerRNA_NULL;
}
}
static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr,
const PointerRNA value,
struct ReportList *UNUSED(reports))
{
SpaceNode *snode = (SpaceNode *)ptr->data;
if (!STREQ(snode->tree_idname, "SimulationNodeTree")) {
return;
}
Simulation *sim = (Simulation *)value.data;
if (sim != NULL) {
bNodeTree *ntree = sim->nodetree;
ED_node_tree_start(snode, ntree, NULL, NULL);
}
else {
ED_node_tree_start(snode, NULL, NULL, NULL);
}
snode->id = &sim->id;
}
# endif
static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
@@ -6346,19 +6311,6 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "ID From", "Data-block from which the edited data-block is linked");
# ifdef WITH_GEOMETRY_NODES
prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Simulation");
RNA_def_property_ui_text(prop, "Simulation", "Simulation that is being edited");
RNA_def_property_pointer_funcs(prop,
"rna_SpaceNodeEditor_simulation_get",
"rna_SpaceNodeEditor_simulation_set",
NULL,
NULL);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
# endif
prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "treepath", NULL);
RNA_def_property_struct_type(prop, "NodeTreePath");

View File

@@ -29,8 +29,10 @@ set(INC
../bmesh
../depsgraph
../editors/include
../functions
../makesdna
../makesrna
../nodes
../render
../windowmanager
../../../intern/eigen
@@ -86,7 +88,7 @@ set(SRC
intern/MOD_shapekey.c
intern/MOD_shrinkwrap.c
intern/MOD_simpledeform.c
intern/MOD_simulation.cc
intern/MOD_nodes.cc
intern/MOD_skin.c
intern/MOD_smooth.c
intern/MOD_softbody.c
@@ -114,6 +116,7 @@ set(SRC
intern/MOD_wireframe.c
MOD_modifiertypes.h
MOD_nodes.h
intern/MOD_meshcache_util.h
intern/MOD_solidify_util.h
intern/MOD_ui_common.h

View File

@@ -85,7 +85,7 @@ extern ModifierTypeInfo modifierType_CorrectiveSmooth;
extern ModifierTypeInfo modifierType_MeshSequenceCache;
extern ModifierTypeInfo modifierType_SurfaceDeform;
extern ModifierTypeInfo modifierType_WeightedNormal;
extern ModifierTypeInfo modifierType_Simulation;
extern ModifierTypeInfo modifierType_Nodes;
extern ModifierTypeInfo modifierType_MeshToVolume;
extern ModifierTypeInfo modifierType_VolumeDisplace;
extern ModifierTypeInfo modifierType_VolumeToMesh;

View File

@@ -16,15 +16,17 @@
#pragma once
struct Main;
struct Object;
struct NodesModifierData;
#ifdef __cplusplus
extern "C" {
#endif
extern struct bNodeTreeType *ntreeType_Simulation;
void MOD_nodes_update_interface(struct Object *object, struct NodesModifierData *nmd);
void register_node_tree_type_sim(void);
void register_node_type_sim_group(void);
void MOD_nodes_init(struct Main *bmain, struct NodesModifierData *nmd);
#ifdef __cplusplus
}

View File

@@ -53,7 +53,10 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
static Mesh *doEdgeSplit(Mesh *mesh, EdgeSplitModifierData *emd)
/* For edge split modifier node. */
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd)
{
Mesh *result;
BMesh *bm;

File diff suppressed because it is too large Load Diff

View File

@@ -1,194 +0,0 @@
/*
* 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) 2005 by the Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup modifiers
*/
#include <cstring>
#include <iostream>
#include <string>
#include "MEM_guardedalloc.h"
#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_simulation_types.h"
#include "BKE_customdata.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
#include "BLO_read_write.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "RNA_access.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
using blender::float3;
static void initData(ModifierData *md)
{
SimulationModifierData *smd = (SimulationModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(smd, modifier));
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SimulationModifierData), modifier);
}
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *UNUSED(ctx))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
}
static void foreachIDLink(ModifierData *md,
Object *UNUSED(ob),
IDWalkFunc UNUSED(walk),
void *UNUSED(userData))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
}
static bool isDisabled(const struct Scene *UNUSED(scene),
ModifierData *md,
bool UNUSED(useRenderParams))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
return false;
}
static PointCloud *modifyPointCloud(ModifierData *md,
const ModifierEvalContext *UNUSED(ctx),
PointCloud *pointcloud)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
return pointcloud;
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemL(layout, "This modifier does nothing currently", ICON_INFO);
modifier_panel_end(layout, ptr);
}
static void panelRegister(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_Simulation, panel_draw);
}
static void blendWrite(BlendWriter *writer, const ModifierData *md)
{
const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
UNUSED_VARS(smd, writer);
}
static void blendRead(BlendDataReader *reader, ModifierData *md)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd, reader);
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
SimulationModifierData *tsmd = reinterpret_cast<SimulationModifierData *>(target);
UNUSED_VARS(smd, tsmd);
BKE_modifier_copydata_generic(md, target, flag);
}
static void freeData(ModifierData *md)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
}
ModifierTypeInfo modifierType_Simulation = {
/* name */ "Simulation",
/* structName */ "SimulationModifierData",
/* structSize */ sizeof(SimulationModifierData),
#ifdef WITH_GEOMETRY_NODES
/* srna */ &RNA_SimulationModifier,
#else
/* srna */ &RNA_Modifier,
#endif
/* type */ eModifierTypeType_None,
/* flags */ (ModifierTypeFlag)0,
/* icon */ ICON_PHYSICS, /* TODO: Use correct icon. */
/* copyData */ copyData,
/* deformVerts */ nullptr,
/* deformMatrices */ nullptr,
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
/* modifyPointCloud */ modifyPointCloud,
/* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ nullptr,
/* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ nullptr,
/* dependsOnNormals */ nullptr,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ nullptr,
/* freeRuntimeData */ nullptr,
/* panelRegister */ panelRegister,
/* blendWrite */ blendWrite,
/* blendRead */ blendRead,
};

View File

@@ -48,11 +48,17 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
static Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,
const int flag)
Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,
const int flag);
Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,
const int flag)
{
Mesh *result;
BMesh *bm;

View File

@@ -342,9 +342,9 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(MeshSequenceCache);
INIT_TYPE(SurfaceDeform);
INIT_TYPE(WeightedNormal);
INIT_TYPE(Simulation);
INIT_TYPE(MeshToVolume);
INIT_TYPE(VolumeDisplace);
INIT_TYPE(VolumeToMesh);
#undef INIT_TYPE
types[eModifierType_Empty] = &modifierType_Nodes;
}

View File

@@ -24,11 +24,12 @@ set(INC
function
intern
shader
simulation
geometry
texture
../blenkernel
../blenlib
../blentranslation
../bmesh
../depsgraph
../functions
../gpu
@@ -137,6 +138,21 @@ set(SRC
function/nodes/node_fn_switch.cc
function/node_function_util.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_random.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/node_geometry_exec.cc
geometry/node_geometry_tree.cc
geometry/node_geometry_util.cc
shader/nodes/node_shader_add_shader.c
shader/nodes/node_shader_ambient_occlusion.c
shader/nodes/node_shader_attribute.c
@@ -230,10 +246,6 @@ set(SRC
shader/node_shader_tree.c
shader/node_shader_util.c
simulation/nodes/node_sim_common.cc
simulation/node_simulation_tree.cc
simulation/node_simulation_util.cc
texture/nodes/node_texture_at.c
texture/nodes/node_texture_bricks.c
texture/nodes/node_texture_checker.c
@@ -263,16 +275,18 @@ set(SRC
intern/derived_node_tree.cc
intern/node_common.c
intern/node_exec.c
intern/node_geometry_exec.cc
intern/node_socket.cc
intern/node_tree_dependencies.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
intern/type_callbacks.cc
composite/node_composite_util.h
function/node_function_util.hh
shader/node_shader_util.h
simulation/node_simulation_util.h
geometry/node_geometry_util.hh
texture/node_texture_util.h
NOD_common.h
@@ -283,10 +297,11 @@ set(SRC
NOD_node_tree_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h
NOD_simulation.h
NOD_geometry.h
NOD_socket.h
NOD_static_types.h
NOD_texture.h
NOD_type_callbacks.hh
intern/node_common.h
intern/node_exec.h
intern/node_util.h
@@ -295,6 +310,7 @@ set(SRC
set(LIB
bf_functions
bf_intern_sky
bf_bmesh
)
if(WITH_PYTHON)
@@ -326,9 +342,12 @@ if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -31,6 +31,8 @@
#include "NOD_node_tree_ref.hh"
#include "BLI_vector_set.hh"
namespace blender::nodes {
class DSocket;
@@ -65,6 +67,8 @@ class DSocket : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull identifier() const;
bNodeSocketType *typeinfo() const;
const SocketRef &socket_ref() const;
bNodeSocket *bsocket() const;
@@ -147,6 +151,8 @@ class DNode : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
bNode *bnode() const;
bNodeType *typeinfo() const;
private:
void destruct_with_sockets();
@@ -180,11 +186,15 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Vector<DOutputSocket *> output_sockets_;
MultiValueMap<const bNodeType *, DNode *> nodes_by_type_;
VectorSet<const NodeTreeRef *> used_node_tree_refs_;
bNodeTree *btree_;
public:
DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs);
~DerivedNodeTree();
bNodeTree *btree() const;
Span<const DNode *> nodes() const;
Span<const DNode *> nodes_by_type(StringRefNull idname) const;
Span<const DNode *> nodes_by_type(const bNodeType *nodetype) const;
@@ -195,6 +205,10 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Span<const DGroupInput *> group_inputs() const;
Span<const NodeTreeRef *> used_node_tree_refs() const;
bool has_link_cycles() const;
std::string to_dot() const;
private:
@@ -288,6 +302,16 @@ inline StringRefNull DSocket::name() const
return socket_ref_->name();
}
inline StringRefNull DSocket::identifier() const
{
return socket_ref_->identifier();
}
inline bNodeSocketType *DSocket::typeinfo() const
{
return socket_ref_->bsocket()->typeinfo;
}
inline const SocketRef &DSocket::socket_ref() const
{
return *socket_ref_;
@@ -445,6 +469,16 @@ inline StringRefNull DNode::name() const
return node_ref_->name();
}
inline bNode *DNode::bnode() const
{
return node_ref_->bnode();
}
inline bNodeType *DNode::typeinfo() const
{
return node_ref_->bnode()->typeinfo;
}
/* --------------------------------------------------------------------
* DParentNode inline methods.
*/
@@ -468,6 +502,11 @@ inline int DParentNode::id() const
* DerivedNodeTree inline methods.
*/
inline bNodeTree *DerivedNodeTree::btree() const
{
return btree_;
}
inline Span<const DNode *> DerivedNodeTree::nodes() const
{
return nodes_by_id_;
@@ -504,4 +543,9 @@ inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
return group_inputs_;
}
inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const
{
return used_node_tree_refs_;
}
} // namespace blender::nodes

View File

@@ -0,0 +1,42 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern struct bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_random(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
void register_node_type_geo_object_info(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,123 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "FN_generic_value_map.hh"
#include "BKE_geometry_set.hh"
#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
namespace blender::nodes {
using bke::PersistentDataHandleMap;
using bke::PersistentObjectHandle;
using fn::CPPType;
using fn::GMutablePointer;
using fn::GValueMap;
class GeoNodeExecParams {
private:
const bNode &node_;
GValueMap<StringRef> &input_values_;
GValueMap<StringRef> &output_values_;
const PersistentDataHandleMap &handle_map_;
public:
GeoNodeExecParams(const bNode &node,
GValueMap<StringRef> &input_values,
GValueMap<StringRef> &output_values,
const PersistentDataHandleMap &handle_map)
: node_(node),
input_values_(input_values),
output_values_(output_values),
handle_map_(handle_map)
{
}
/**
* Get the input value for the input socket with the given identifier.
*
* The node calling becomes responsible for destructing the value before it is done
* executing. This method can only be called once for each identifier.
*/
GMutablePointer extract_input(StringRef identifier)
{
#ifdef DEBUG
this->check_extract_input(identifier);
#endif
return input_values_.extract(identifier);
}
/**
* Get the input value for the input socket with the given identifier.
*
* This method can only be called once for each identifier.
*/
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
this->check_extract_input(identifier, &CPPType::get<T>());
#endif
return input_values_.extract<T>(identifier);
}
/**
* Move-construct a new value based on the given value and store it for the given socket
* identifier.
*/
void set_output_by_move(StringRef identifier, GMutablePointer value)
{
#ifdef DEBUG
BLI_assert(value.type() != nullptr);
BLI_assert(value.get() != nullptr);
this->check_set_output(identifier, *value.type());
#endif
output_values_.add_new_by_move(identifier, value);
}
/**
* Store the output value for the given socket identifier.
*/
template<typename T> void set_output(StringRef identifier, T &&value)
{
#ifdef DEBUG
this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
#endif
output_values_.add_new(identifier, std::forward<T>(value));
}
/**
* Get the node that is currently being executed.
*/
const bNode &node() const
{
return node_;
}
const PersistentDataHandleMap &handle_map() const
{
return handle_map_;
}
private:
void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr);
void check_set_output(StringRef identifier, const CPPType &value_type);
};
} // namespace blender::nodes

View File

@@ -26,21 +26,12 @@
#include "FN_multi_function_network.hh"
#include "NOD_derived_node_tree.hh"
#include "NOD_type_callbacks.hh"
#include "BLI_resource_collector.hh"
namespace blender::nodes {
/* Maybe this should be moved to BKE_node.h. */
inline bool is_multi_function_data_socket(const bNodeSocket *bsocket)
{
if (bsocket->typeinfo->get_mf_data_type != nullptr) {
BLI_assert(bsocket->typeinfo->expand_in_mf_network != nullptr);
return true;
}
return false;
}
/**
* A MFNetworkTreeMap maps various components of a DerivedNodeTree to components of a
* fn::MFNetwork. This is necessary for further processing of a multi-function network that has
@@ -149,7 +140,7 @@ class MFNetworkTreeMap {
if (!dsocket->is_available()) {
continue;
}
if (!is_multi_function_data_socket(dsocket->bsocket())) {
if (!socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
continue;
}
fn::MFSocket *socket = sockets[used_sockets];
@@ -299,6 +290,11 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
{
this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value));
}
void set_constant_value(const CPPType &type, const void *value)
{
/* The value has live as long as the generated mf network. */
this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value);
}
template<typename T, typename... Args> void construct_generator_fn(Args &&... args)
{
@@ -397,4 +393,37 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
ResourceCollector &resources);
using MultiFunctionByNode = Map<const DNode *, const fn::MultiFunction *>;
MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
ResourceCollector &resources);
class DataTypeConversions {
private:
Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
public:
void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
{
conversions_.add_new({from_type, to_type}, &fn);
}
const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
{
return conversions_.lookup_default({from, to}, nullptr);
}
bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
{
return conversions_.contains(
{fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
}
void convert(const CPPType &from_type,
const CPPType &to_type,
const void *from_value,
void *to_value) const;
};
const DataTypeConversions &get_implicit_type_conversions();
} // namespace blender::nodes

View File

@@ -101,6 +101,7 @@ class SocketRef : NonCopyable, NonMovable {
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull identifier() const;
bNodeSocket *bsocket() const;
bNode *bnode() const;
@@ -176,6 +177,8 @@ class NodeTreeRef : NonCopyable, NonMovable {
Span<const InputSocketRef *> input_sockets() const;
Span<const OutputSocketRef *> output_sockets() const;
bool has_link_cycles() const;
bNodeTree *btree() const;
std::string to_dot() const;
@@ -272,6 +275,11 @@ inline StringRefNull SocketRef::name() const
return bsocket_->name;
}
inline StringRefNull SocketRef::identifier() const
{
return bsocket_->identifier;
}
inline bNodeSocket *SocketRef::bsocket() const
{
return bsocket_;

View File

@@ -266,6 +266,17 @@ DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS
DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "")
DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOM, def_geo_attribute_random, "ATTRIBUTE_RANDOM", RandomAttribute, "Random Attribute", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
/* undefine macros */
#undef DefNode

View File

@@ -0,0 +1,36 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <optional>
#include "BKE_node.h"
#include "FN_multi_function_data_type.hh"
namespace blender::nodes {
using fn::CPPType;
using fn::MFDataType;
const CPPType *socket_cpp_type_get(const bNodeSocketType &stype);
std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype);
bool socket_is_mf_data_socket(const bNodeSocketType &stype);
bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value);
void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder);
} // namespace blender::nodes

View File

@@ -20,7 +20,7 @@
bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
/* Function nodes are only supported in simulation node trees so far. */
return STREQ(ntree->idname, "SimulationNodeTree");
return STREQ(ntree->idname, "GeometryNodeTree");
}
void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)

View File

@@ -21,7 +21,7 @@
static bNodeSocketTemplate fn_node_random_float_in[] = {
{SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
{SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
{SOCK_INT, N_("Seed")},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};

View File

@@ -0,0 +1,23 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "NOD_geometry_exec.hh"
MAKE_CPP_TYPE(GeometrySet, GeometrySet);
namespace blender::nodes {
}

View File

@@ -18,7 +18,7 @@
#include "MEM_guardedalloc.h"
#include "NOD_simulation.h"
#include "NOD_geometry.h"
#include "BKE_node.h"
@@ -28,18 +28,18 @@
#include "RNA_access.h"
bNodeTreeType *ntreeType_Simulation;
bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_sim(void)
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Simulation = static_cast<bNodeTreeType *>(
MEM_callocN(sizeof(bNodeTreeType), "simulation node tree type"));
tt->type = NTREE_SIMULATION;
strcpy(tt->idname, "SimulationNodeTree");
strcpy(tt->ui_name, N_("Simulation Editor"));
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
tt->ui_icon = 0; /* defined in drawnode.c */
strcpy(tt->ui_description, N_("Simulation nodes"));
tt->rna_ext.srna = &RNA_SimulationNodeTree;
strcpy(tt->ui_description, N_("Geometry nodes"));
tt->rna_ext.srna = &RNA_GeometryNodeTree;
ntreeTypeAdd(tt);
}

View File

@@ -14,16 +14,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_simulation_util.h"
#include "node_geometry_util.hh"
#include "node_util.h"
bool sim_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
return STREQ(ntree->idname, "SimulationNodeTree");
return STREQ(ntree->idname, "GeometryNodeTree");
}
void sim_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
ntype->poll = sim_node_poll_default;
ntype->poll = geo_node_poll_default;
}

View File

@@ -18,6 +18,7 @@
#include <string.h>
#include "BLI_float3.hh"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -28,10 +29,11 @@
#include "BLT_translation.h"
#include "NOD_simulation.h"
#include "NOD_geometry.h"
#include "NOD_geometry_exec.hh"
#include "node_util.h"
void sim_node_type_base(
void geo_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
bool sim_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);

View File

@@ -0,0 +1,62 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "DNA_windowmanager_types.h"
#include "BKE_attribute.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute A")},
{SOCK_STRING, N_("Attribute B")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_math_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_attribute_math_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry A");
std::string attribute_name_a = params.extract_input<std::string>("Attribute A");
std::string attribute_name_b = params.extract_input<std::string>("Attribute B");
if (geometry_set.has_mesh()) {
Mesh *mesh = geometry_set.get_mesh_for_write();
}
if (geometry_set.has_pointcloud()) {
PointCloud *point_cloud = geometry_set.get_pointcloud_for_write();
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_attribute_math()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MATH, "Attribute Math", 0, 0);
node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out);
ntype.geometry_node_execute = blender::nodes::geo_attribute_math_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,73 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_rand.hh"
#include "DNA_customdata_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_attribute.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_random_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_random_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_attribute_random_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
}
namespace blender::nodes {
static void geo_attribute_random_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
std::string attribute_name = params.extract_input<std::string>("Attribute");
RandomNumberGenerator rng(0);
CustomDataType data_type = static_cast<CustomDataType>(params.node().custom1);
AttributeDomain domain = static_cast<AttributeDomain>(params.node().custom2);
if (geometry_set.has_mesh()) {
Mesh *mesh = geometry_set.get_mesh_for_write();
}
if (geometry_set.has_pointcloud()) {
PointCloud *point_cloud = geometry_set.get_pointcloud_for_write();
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_attribute_random()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_RANDOM, "Random Attribute", 0, 0);
node_type_socket_templates(&ntype, geo_node_attribute_random_in, geo_node_attribute_random_out);
node_type_init(&ntype, geo_attribute_random_init);
ntype.geometry_node_execute = blender::nodes::geo_attribute_random_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,141 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_math_matrix.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "RNA_enum_types.h"
#include "BKE_mesh.h"
#include "bmesh.h"
#include "tools/bmesh_boolean.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_boolean_in[] = {
{SOCK_GEOMETRY, N_("Geometry A")},
{SOCK_GEOMETRY, N_("Geometry B")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_boolean_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
{
return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
}
static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
{
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
BMesh *bm;
{
struct BMeshCreateParams bmesh_create_params = {0};
bmesh_create_params.use_toolflags = false;
bm = BM_mesh_create(&allocsize, &bmesh_create_params);
}
{
struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
bmesh_from_mesh_params.calc_face_normal = true;
BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
}
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
int tottri;
BMLoop *(*looptris)[3] = (BMLoop *
(*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
const int i_faces_end = mesh_a->totpoly;
/* We need face normals because of 'BM_face_split_edgenet'
* we could calculate on the fly too (before calling split). */
int i = 0;
BMIter iter;
BMFace *bm_face;
BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
normalize_v3(bm_face->no);
/* Temp tag to test which side split faces are from. */
BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
i++;
if (i == i_faces_end) {
break;
}
}
BM_mesh_boolean(
bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, boolean_mode);
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
BM_mesh_free(bm);
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
MEM_freeN(looptris);
return result;
}
namespace blender::nodes {
static void geo_boolean_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry A");
GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry B");
GeometrySet geometry_set_out;
const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
if (operation < 0 || operation > 2) {
BLI_assert(false);
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
params.set_output("Geometry", std::move(geometry_set_out));
}
} // namespace blender::nodes
void register_node_type_geo_boolean()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", 0, 0);
node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
ntype.geometry_node_execute = blender::nodes::geo_boolean_exec;
nodeRegisterType(&ntype);
}

View File

@@ -16,23 +16,23 @@
#include "BKE_node.h"
#include "NOD_simulation.h"
#include "NOD_geometry.h"
#include "NOD_common.h"
#include "node_common.h"
#include "node_simulation_util.h"
#include "node_geometry_util.hh"
void register_node_type_sim_group(void)
void register_node_type_geo_group(void)
{
static bNodeType ntype;
node_type_base_custom(&ntype, "SimulationNodeGroup", "Group", 0, 0);
node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", 0, 0);
ntype.type = NODE_GROUP;
ntype.poll = sim_node_poll_default;
ntype.poll = geo_node_poll_default;
ntype.poll_instance = node_group_poll_instance;
ntype.insert_link = node_insert_link_default;
ntype.update_internal_links = node_update_internal_links_default;
ntype.rna_ext.srna = RNA_struct_find("SimulationNodeGroup");
ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup");
BLI_assert(ntype.rna_ext.srna != nullptr);
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);

View File

@@ -0,0 +1,87 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_math_base.h"
#include "BLI_math_rotation.h"
#include "DNA_modifier_types.h"
#include "node_geometry_util.hh"
extern "C" {
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
}
static bNodeSocketTemplate geo_node_edge_split_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT,
N_("Angle"),
DEG2RADF(30.0f),
0.0f,
0.0f,
0.0f,
0.0f,
DEG2RADF(180.0f),
PROP_ANGLE},
{SOCK_BOOLEAN, N_("Sharp Edges")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_edge_split_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_edge_split_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const float split_angle = params.extract_input<float>("Angle");
const bool use_sharp_flag = params.extract_input<bool>("Sharp Edges");
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
/* Use modifier struct to pass arguments to the modifier code. */
EdgeSplitModifierData emd;
memset(&emd, 0, sizeof(EdgeSplitModifierData));
emd.split_angle = split_angle;
emd.flags = MOD_EDGESPLIT_FROMANGLE;
if (use_sharp_flag) {
emd.flags |= MOD_EDGESPLIT_FROMFLAG;
}
Mesh *mesh_out = doEdgeSplit(mesh_in, &emd);
geometry_set.replace_mesh(mesh_out);
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_edge_split()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", 0, 0);
node_type_socket_templates(&ntype, geo_node_edge_split_in, geo_node_edge_split_out);
ntype.geometry_node_execute = blender::nodes::geo_edge_split_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,81 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_geometry_util.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
static bNodeSocketTemplate geo_node_object_info_in[] = {
{SOCK_OBJECT, N_("Object")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_object_info_out[] = {
{SOCK_VECTOR, N_("Location")},
{SOCK_VECTOR, N_("Rotation")},
{SOCK_VECTOR, N_("Scale")},
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_object_info_exec(GeoNodeExecParams params)
{
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
"Object");
Object *object = params.handle_map().lookup(object_handle);
float3 location = {0, 0, 0};
float3 rotation = {0, 0, 0};
float3 scale = {0, 0, 0};
GeometrySet geometry_set;
if (object != nullptr) {
float quaternion[4];
mat4_decompose(location, quaternion, scale, object->obmat);
quat_to_eul(rotation, quaternion);
if (object->type == OB_MESH) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object, false);
if (mesh != nullptr) {
BKE_mesh_wrapper_ensure_mdata(mesh);
/* Make a copy because the life time of the other mesh might be shorter. */
Mesh *copied_mesh = BKE_mesh_copy_for_eval(mesh, false);
geometry_set = GeometrySet::create_with_mesh(copied_mesh);
geometry_set.get_component_for_write<MeshComponent>().copy_vertex_group_names_from_object(
*object);
}
}
}
params.set_output("Location", location);
params.set_output("Rotation", rotation);
params.set_output("Scale", scale);
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_object_info()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", 0, 0);
node_type_socket_templates(&ntype, geo_node_object_info_in, geo_node_object_info_out);
ntype.geometry_node_execute = blender::nodes::geo_object_info_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,149 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_float3.hh"
#include "BLI_hash.h"
#include "BLI_math_vector.h"
#include "BLI_rand.hh"
#include "BLI_span.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
const float density,
const int density_attribute_index)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
const int looptris_len = BKE_mesh_runtime_looptri_len(mesh);
Array<float> vertex_density_factors(mesh->totvert);
if (density_attribute_index == -1) {
vertex_density_factors.fill(1.0f);
}
else {
MDeformVert *dverts = mesh->dvert;
BKE_defvert_extract_vgroup_to_vertweights(
dverts, density_attribute_index, mesh->totvert, vertex_density_factors.data(), false);
}
Vector<float3> points;
for (const int looptri_index : IndexRange(looptris_len)) {
const MLoopTri &looptri = looptris[looptri_index];
const int v0_index = mesh->mloop[looptri.tri[0]].v;
const int v1_index = mesh->mloop[looptri.tri[1]].v;
const int v2_index = mesh->mloop[looptri.tri[2]].v;
const float3 v0_pos = mesh->mvert[v0_index].co;
const float3 v1_pos = mesh->mvert[v1_index].co;
const float3 v2_pos = mesh->mvert[v2_index].co;
const float v0_density_factor = vertex_density_factors[v0_index];
const float v1_density_factor = vertex_density_factors[v1_index];
const float v2_density_factor = vertex_density_factors[v2_index];
const float looptri_density_factor = (v0_density_factor + v1_density_factor +
v2_density_factor) /
3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
const int looptri_seed = BLI_hash_int(looptri_index);
RandomNumberGenerator looptri_rng(looptri_seed);
const float points_amount_fl = area * density * looptri_density_factor;
const float add_point_probability = fractf(points_amount_fl);
const bool add_point = add_point_probability > looptri_rng.get_float();
const int point_amount = (int)points_amount_fl + (int)add_point;
for (int i = 0; i < point_amount; i++) {
const float3 bary_coords = looptri_rng.get_barycentric_coordinates();
float3 point_pos;
interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
points.append(point_pos);
}
}
return points;
}
static void geo_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const float density = params.extract_input<float>("Density");
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
if (density <= 0.0f) {
geometry_set.replace_mesh(nullptr);
geometry_set.replace_pointcloud(nullptr);
params.set_output("Geometry", std::move(geometry_set));
return;
}
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh *mesh_in = mesh_component.get_for_read();
const int density_attribute_index = mesh_component.vertex_group_index(density_attribute);
Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_attribute_index);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
for (const int i : points.index_range()) {
*(float3 *)(pointcloud->co + i) = points[i];
pointcloud->radius[i] = 0.05f;
}
geometry_set.replace_mesh(nullptr);
geometry_set.replace_pointcloud(pointcloud);
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_point_distribute()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", 0, 0);
node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out);
ntype.geometry_node_execute = blender::nodes::geo_point_distribute_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,72 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_mesh.h"
#include "BKE_persistent_data_handle.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_OBJECT, N_("Object")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_instance_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_point_instance_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<float3> positions;
if (geometry_set.has_pointcloud()) {
const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read();
positions.extend((const float3 *)pointcloud->co, pointcloud->totpoint);
}
if (geometry_set.has_mesh()) {
const Mesh *mesh = geometry_set.get_mesh_for_read();
for (const int i : IndexRange(mesh->totvert)) {
positions.append(mesh->mvert[i].co);
}
}
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
"Object");
Object *object = params.handle_map().lookup(object_handle);
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
instances.replace(std::move(positions), object);
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_point_instance()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", 0, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
ntype.geometry_node_execute = blender::nodes::geo_point_instance_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,113 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "MEM_guardedalloc.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_subdivision_surface_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
{SOCK_BOOLEAN, N_("Use Creases")},
{SOCK_BOOLEAN, N_("Boundary Smooth")},
{SOCK_BOOLEAN, N_("Smooth UVs")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_subdivision_surface_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
return;
}
#ifndef WITH_OPENSUBDIV
/* Return input geometry if Blender is built without OpenSubdiv. */
params.set_output("Geometry", std::move(geometry_set));
return;
#else
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
/* Only process subdivion if level is greater than 0. */
if (subdiv_level == 0) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const bool use_crease = params.extract_input<bool>("Use Creases");
const bool boundary_smooth = params.extract_input<bool>("Boundary Smooth");
const bool smooth_uvs = params.extract_input<bool>("Smooth UVs");
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
/* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << subdiv_level) + 1;
mesh_settings.use_optimal_display = false;
/* Initialize subdivision settings. */
SubdivSettings subdiv_settings;
subdiv_settings.is_simple = false;
subdiv_settings.is_adaptive = false;
subdiv_settings.use_creases = use_crease;
subdiv_settings.level = subdiv_level;
subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
boundary_smooth);
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
smooth_uvs);
/* Apply subdivision to mesh. */
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
geometry_set.replace_mesh(mesh_out);
// BKE_subdiv_stats_print(&subdiv->stats);
BKE_subdiv_free(subdiv);
params.set_output("Geometry", std::move(geometry_set));
#endif
}
} // namespace blender::nodes
void register_node_type_geo_subdivision_surface()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", 0, 0);
node_type_socket_templates(
&ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_subdivision_surface_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,144 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_math_matrix.h"
#include "DNA_pointcloud_types.h"
#include "BKE_mesh.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_transform_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
{SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
{-1, ""},
};
static bNodeSocketTemplate geo_node_transform_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static bool use_translate(const float3 rotation, const float3 scale)
{
if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) {
return false;
}
if (compare_ff(scale.x, 1.0f, 1e-9f) != 1 || compare_ff(scale.y, 1.0f, 1e-9f) != 1 ||
compare_ff(scale.z, 1.0f, 1e-9f) != 1) {
return false;
}
return true;
}
static void transform_mesh(Mesh *mesh,
const float3 translation,
const float3 rotation,
const float3 scale)
{
/* Use only translation if rotation and scale are zero. */
if (use_translate(rotation, scale)) {
BKE_mesh_translate(mesh, translation, true);
}
else {
float mat[4][4];
loc_eul_size_to_mat4(mat, translation, rotation, scale);
BKE_mesh_transform(mesh, mat, true);
}
}
static void transform_pointcloud(PointCloud *pointcloud,
const float3 translation,
const float3 rotation,
const float3 scale)
{
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
for (int i = 0; i < pointcloud->totpoint; i++) {
add_v3_v3(pointcloud->co[i], translation);
}
}
else {
float mat[4][4];
loc_eul_size_to_mat4(mat, translation, rotation, scale);
for (int i = 0; i < pointcloud->totpoint; i++) {
mul_m4_v3(mat, pointcloud->co[i]);
}
}
}
static void transform_instances(InstancesComponent &instances,
const float3 translation,
const float3 rotation,
const float3 scale)
{
MutableSpan<float3> positions = instances.positions();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
for (float3 &position : positions) {
add_v3_v3(position, translation);
}
}
else {
float mat[4][4];
loc_eul_size_to_mat4(mat, translation, rotation, scale);
for (float3 &position : positions) {
mul_m4_v3(mat, position);
}
}
}
static void geo_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const float3 translation = params.extract_input<float3>("Translation");
const float3 rotation = params.extract_input<float3>("Rotation");
const float3 scale = params.extract_input<float3>("Scale");
if (geometry_set.has_mesh()) {
Mesh *mesh = geometry_set.get_mesh_for_write();
transform_mesh(mesh, translation, rotation, scale);
}
if (geometry_set.has_pointcloud()) {
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
transform_pointcloud(pointcloud, translation, rotation, scale);
}
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
transform_instances(instances, translation, rotation, scale);
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_transform()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", 0, 0);
node_type_socket_templates(&ntype, geo_node_transform_in, geo_node_transform_out);
ntype.geometry_node_execute = blender::nodes::geo_transform_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,79 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "DNA_node_types.h"
#include "RNA_enum_types.h"
#include "node_geometry_util.hh"
extern "C" {
Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,
const int flag);
}
static bNodeSocketTemplate geo_node_triangulate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_INT, N_("Minimum Vertices"), 4, 0, 0, 0, 4, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_triangulate_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
{
node->custom1 = GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE;
node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY;
}
namespace blender::nodes {
static void geo_triangulate_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
params.node().custom1);
GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
params.node().custom2);
/* #triangulate_mesh might modify the input mesh currently. */
Mesh *mesh_in = geometry_set.get_mesh_for_write();
if (mesh_in != nullptr) {
Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
geometry_set.replace_mesh(mesh_out);
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_triangulate()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", 0, 0);
node_type_socket_templates(&ntype, geo_node_triangulate_in, geo_node_triangulate_out);
node_type_init(&ntype, geo_triangulate_init);
ntype.geometry_node_execute = blender::nodes::geo_triangulate_exec;
nodeRegisterType(&ntype);
}

View File

@@ -28,9 +28,12 @@ static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree
[&]() { return std::make_unique<NodeTreeRef>(btree); });
}
DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs)
DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
{
BLI_assert(btree != nullptr);
const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree);
used_node_tree_refs_.add_new(&main_tree_ref);
Vector<DNode *> all_nodes;
Vector<DGroupInput *> all_group_inputs;
@@ -137,6 +140,7 @@ BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
}
const NodeTreeRef &group_ref = get_tree_ref(node_tree_refs, btree);
used_node_tree_refs_.add(&group_ref);
DParentNode &parent = *allocator_.construct<DParentNode>();
parent.id_ = all_parent_nodes.append_and_get_index(&parent);
@@ -358,6 +362,16 @@ DerivedNodeTree::~DerivedNodeTree()
}
}
bool DerivedNodeTree::has_link_cycles() const
{
for (const NodeTreeRef *tree : used_node_tree_refs_) {
if (tree->has_link_cycles()) {
return true;
}
}
return false;
}
static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
Map<const DParentNode *, dot::Cluster *> &clusters,
const DParentNode *parent)

View File

@@ -0,0 +1,102 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
namespace blender::nodes {
void GeoNodeExecParams::check_extract_input(StringRef identifier, const CPPType *requested_type)
{
bNodeSocket *found_socket = nullptr;
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.inputs) {
if (identifier == socket->identifier) {
found_socket = socket;
break;
}
}
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.inputs) {
if ((socket->flag & SOCK_UNAVAIL) == 0) {
std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
BLI_assert(false);
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
BLI_assert(false);
}
else if (!input_values_.contains(identifier)) {
std::cout << "The identifier '" << identifier
<< "' is valid, but there is no value for it anymore.\n";
std::cout << "Most likely it has been extracted before.\n";
BLI_assert(false);
}
else if (requested_type != nullptr) {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
BLI_assert(false);
}
}
}
void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type)
{
bNodeSocket *found_socket = nullptr;
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.outputs) {
if (identifier == socket->identifier) {
found_socket = socket;
break;
}
}
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.outputs) {
if ((socket->flag & SOCK_UNAVAIL) == 0) {
std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
BLI_assert(false);
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
BLI_assert(false);
}
else if (output_values_.contains(identifier)) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
BLI_assert(false);
}
else {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
BLI_assert(false);
}
}
}
} // namespace blender::nodes

View File

@@ -32,6 +32,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_persistent_data_handle.hh"
@@ -552,10 +553,9 @@ static bNodeSocketType *make_socket_type_virtual()
static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<bool>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
return socktype;
}
@@ -563,10 +563,9 @@ static bNodeSocketType *make_socket_type_bool()
static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<float>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
float value = builder.socket_default_value<bNodeSocketValueFloat>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<float>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
return socktype;
}
@@ -574,10 +573,9 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<int>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
int value = builder.socket_default_value<bNodeSocketValueInt>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<int>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
return socktype;
}
@@ -585,12 +583,9 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
socktype->get_mf_data_type = []() {
return blender::fn::MFDataType::ForSingle<blender::float3>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
return socktype;
}
@@ -598,12 +593,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
socktype->get_mf_data_type = []() {
return blender::fn::MFDataType::ForSingle<blender::Color4f>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
return socktype;
}
@@ -611,10 +603,9 @@ static bNodeSocketType *make_socket_type_rgba()
static bNodeSocketType *make_socket_type_string()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<std::string>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
std::string value = builder.socket_default_value<bNodeSocketValueString>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value);
};
return socktype;
}
@@ -661,9 +652,9 @@ MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
socktype->get_mf_data_type = []() {
socktype->get_cpp_type = []() {
/* Objects are not passed along as raw pointers, but as handles. */
return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>();
return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
@@ -672,6 +663,16 @@ static bNodeSocketType *make_socket_type_object()
return socktype;
}
static bNodeSocketType *make_socket_type_geometry()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); };
socktype->get_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) {
new (r_value) GeometrySet();
};
return socktype;
}
void register_standard_node_socket_types(void)
{
/* draw callbacks are set in drawnode.c to avoid bad-level calls */
@@ -708,5 +709,7 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE));
nodeRegisterSocketType(make_socket_type_geometry());
nodeRegisterSocketType(make_socket_type_virtual());
}

View File

@@ -16,21 +16,13 @@
#include "NOD_node_tree_multi_function.hh"
#include "FN_multi_function_network_evaluation.hh"
#include "BLI_color.hh"
#include "BLI_float3.hh"
namespace blender::nodes {
/* Maybe this should be moved to BKE_node.h. */
static std::optional<fn::MFDataType> try_get_multi_function_data_type_of_socket(
const bNodeSocket *bsocket)
{
if (bsocket->typeinfo->get_mf_data_type == nullptr) {
return {};
}
return bsocket->typeinfo->get_mf_data_type();
}
const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
{
Vector<fn::MFDataType, 10> input_types;
@@ -38,8 +30,7 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
for (const DInputSocket *dsocket : dnode_.inputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
input_types.append(*data_type);
}
@@ -47,8 +38,7 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
}
for (const DOutputSocket *dsocket : dnode_.outputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
output_types.append(*data_type);
}
@@ -70,8 +60,7 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
input_types.append(*data_type);
input_names.append(dsocket->name());
@@ -86,8 +75,7 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
output_types.append(*data_type);
output_names.append(dsocket->name());
@@ -106,12 +94,12 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
static bool has_data_sockets(const DNode &dnode)
{
for (const DInputSocket *socket : dnode.inputs()) {
if (is_multi_function_data_socket(socket->bsocket())) {
if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
for (const DOutputSocket *socket : dnode.outputs()) {
if (is_multi_function_data_socket(socket->bsocket())) {
if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
@@ -140,7 +128,7 @@ static void insert_group_inputs(CommonMFNetworkBuilderData &common)
{
for (const DGroupInput *group_input : common.tree.group_inputs()) {
bNodeSocket *bsocket = group_input->bsocket();
if (is_multi_function_data_socket(bsocket)) {
if (socket_is_mf_data_socket(*bsocket->typeinfo)) {
bNodeSocketType *socktype = bsocket->typeinfo;
BLI_assert(socktype->expand_in_mf_network != nullptr);
@@ -171,44 +159,43 @@ static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
if (!from_dsocket.is_available()) {
return nullptr;
}
if (is_multi_function_data_socket(from_dsocket.bsocket())) {
if (socket_is_mf_data_socket(*from_dsocket.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_dsocket);
}
return nullptr;
}
const DGroupInput &from_group_input = *from_group_inputs[0];
if (is_multi_function_data_socket(from_group_input.bsocket())) {
if (socket_is_mf_data_socket(*from_group_input.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_group_input);
}
return nullptr;
}
using ImplicitConversionsMap =
Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *>;
template<typename From, typename To>
static void add_implicit_conversion(ImplicitConversionsMap &map)
static void add_implicit_conversion(DataTypeConversions &conversions)
{
static fn::CustomMF_Convert<From, To> function;
map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
}
template<typename From, typename To, typename ConversionF>
static void add_implicit_conversion(ImplicitConversionsMap &map,
static void add_implicit_conversion(DataTypeConversions &conversions,
StringRef name,
ConversionF conversion)
{
static fn::CustomMF_SI_SO<From, To> function{name, conversion};
map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
}
static ImplicitConversionsMap get_implicit_conversions()
static DataTypeConversions create_implicit_conversions()
{
ImplicitConversionsMap conversions;
DataTypeConversions conversions;
add_implicit_conversion<float, int32_t>(conversions);
add_implicit_conversion<float, float3>(conversions);
add_implicit_conversion<int32_t, float>(conversions);
add_implicit_conversion<float, bool>(conversions);
add_implicit_conversion<bool, float>(conversions);
add_implicit_conversion<float3, float>(
conversions, "Vector Length", [](float3 a) { return a.length(); });
add_implicit_conversion<int32_t, float3>(
@@ -220,11 +207,26 @@ static ImplicitConversionsMap get_implicit_conversions()
return conversions;
}
static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to)
const DataTypeConversions &get_implicit_type_conversions()
{
static const ImplicitConversionsMap conversions = get_implicit_conversions();
const fn::MultiFunction *function = conversions.lookup_default({from, to}, nullptr);
return function;
static const DataTypeConversions conversions = create_implicit_conversions();
return conversions;
}
void DataTypeConversions::convert(const CPPType &from_type,
const CPPType &to_type,
const void *from_value,
void *to_value) const
{
const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
MFDataType::ForSingle(to_type));
BLI_assert(fn != nullptr);
fn::MFContextBuilder context;
fn::MFParamsBuilder params{*fn, 1};
params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
fn->call({0}, params, context);
}
static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
@@ -253,7 +255,7 @@ static void insert_links(CommonMFNetworkBuilderData &common)
if (!to_dsocket->is_linked()) {
continue;
}
if (!is_multi_function_data_socket(to_dsocket->bsocket())) {
if (!socket_is_mf_data_socket(*to_dsocket->bsocket()->typeinfo)) {
continue;
}
@@ -269,7 +271,8 @@ static void insert_links(CommonMFNetworkBuilderData &common)
fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type);
const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
@@ -308,7 +311,7 @@ static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common)
Vector<const DInputSocket *> unlinked_data_inputs;
for (const DInputSocket *dsocket : common.tree.input_sockets()) {
if (dsocket->is_available()) {
if (is_multi_function_data_socket(dsocket->bsocket())) {
if (socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
if (!dsocket->is_linked()) {
insert_unlinked_input(common, *dsocket);
}
@@ -340,4 +343,150 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
return network_map;
}
/**
* A single node is allowed to expand into multiple nodes before evaluation. Depending on what
* nodes it expands to, it belongs a different type of the ones below.
*/
enum class NodeExpandType {
SingleFunctionNode,
MultipleFunctionNodes,
HasDummyNodes,
};
/**
* Checks how the given node expanded in the multi-function network. If it is only a single
* function node, the corresponding function is returned as well.
*/
static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map,
const DNode &dnode,
const fn::MultiFunction **r_single_function)
{
const fn::MFFunctionNode *single_function_node = nullptr;
bool has_multiple_nodes = false;
bool has_dummy_nodes = false;
auto check_mf_node = [&](fn::MFNode &mf_node) {
if (mf_node.is_function()) {
if (single_function_node == nullptr) {
single_function_node = &mf_node.as_function();
}
if (&mf_node != single_function_node) {
has_multiple_nodes = true;
}
}
else {
BLI_assert(mf_node.is_dummy());
has_dummy_nodes = true;
}
};
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
check_mf_node(mf_input->node());
}
}
}
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
check_mf_node(mf_output.node());
}
}
if (has_dummy_nodes) {
return NodeExpandType::HasDummyNodes;
}
if (has_multiple_nodes) {
return NodeExpandType::MultipleFunctionNodes;
}
*r_single_function = &single_function_node->function();
return NodeExpandType::SingleFunctionNode;
}
static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple(
const DNode &dnode,
fn::MFNetwork &network,
MFNetworkTreeMap &network_map,
ResourceCollector &resources)
{
Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo());
fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type);
for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
network.add_link(fn_input, *mf_input);
dummy_fn_inputs.append(&fn_input);
}
}
}
Vector<const fn::MFInputSocket *> dummy_fn_outputs;
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
MFDataType data_type = mf_output.data_type();
fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type);
network.add_link(mf_output, fn_output);
dummy_fn_outputs.append(&fn_output);
}
}
fn::MFNetworkEvaluator &fn_evaluator = resources.construct<fn::MFNetworkEvaluator>(
__func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
return fn_evaluator;
}
/**
* Returns a single multi-function for every node that supports it. This makes it easier to reuse
* the multi-function implementation of nodes in different contexts.
*/
MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
ResourceCollector &resources)
{
/* Build a network that nodes can insert themselves into. However, the individual nodes are not
* connected. */
fn::MFNetwork &network = resources.construct<fn::MFNetwork>(__func__);
MFNetworkTreeMap network_map{tree, network};
MultiFunctionByNode functions_by_node;
CommonMFNetworkBuilderData common{resources, network, network_map, tree};
for (const DNode *dnode : tree.nodes()) {
const bNodeType *node_type = dnode->typeinfo();
if (node_type->expand_in_mf_network == nullptr) {
/* This node does not have a multi-function implementation. */
continue;
}
NodeMFNetworkBuilder builder{common, *dnode};
node_type->expand_in_mf_network(builder);
const fn::MultiFunction *single_function = nullptr;
const NodeExpandType expand_type = get_node_expand_type(network_map, *dnode, &single_function);
switch (expand_type) {
case NodeExpandType::HasDummyNodes: {
/* Dummy nodes cannot be executed, so skip them. */
break;
}
case NodeExpandType::SingleFunctionNode: {
/* This is the common case. Most nodes just expand to a single function. */
functions_by_node.add_new(dnode, single_function);
break;
}
case NodeExpandType::MultipleFunctionNodes: {
/* If a node expanded into multiple functions, a new function has to be created that
* combines those. */
const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
*dnode, network, network_map, resources);
functions_by_node.add_new(dnode, &fn);
break;
}
}
}
return functions_by_node;
}
} // namespace blender::nodes

View File

@@ -137,6 +137,48 @@ void NodeTreeRef::find_targets_skipping_reroutes(OutputSocketRef &socket,
}
}
static bool has_link_cycles_recursive(const NodeRef &node,
MutableSpan<bool> visited,
MutableSpan<bool> is_in_stack)
{
const int node_id = node.id();
if (is_in_stack[node_id]) {
return true;
}
if (visited[node_id]) {
return false;
}
visited[node_id] = true;
is_in_stack[node_id] = true;
for (const OutputSocketRef *from_socket : node.outputs()) {
for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
const NodeRef &to_node = to_socket->node();
if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
return true;
}
}
}
is_in_stack[node_id] = false;
return false;
}
bool NodeTreeRef::has_link_cycles() const
{
const int node_amount = nodes_by_id_.size();
Array<bool> visited(node_amount, false);
Array<bool> is_in_stack(node_amount, false);
for (const NodeRef *node : nodes_by_id_) {
if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
return true;
}
}
return false;
}
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;

View File

@@ -0,0 +1,76 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "NOD_node_tree_multi_function.hh"
#include "NOD_type_callbacks.hh"
namespace blender::nodes {
const CPPType *socket_cpp_type_get(const bNodeSocketType &stype)
{
if (stype.get_cpp_type != nullptr) {
return stype.get_cpp_type();
}
return nullptr;
}
std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype)
{
const CPPType *cpp_type = socket_cpp_type_get(stype);
if (cpp_type != nullptr) {
return MFDataType::ForSingle(*cpp_type);
}
return {};
}
bool socket_is_mf_data_socket(const bNodeSocketType &stype)
{
if (!socket_mf_type_get(stype).has_value()) {
return false;
}
if (stype.expand_in_mf_network == nullptr && stype.get_cpp_value == nullptr) {
return false;
}
return true;
}
bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value)
{
if (socket.typeinfo->get_cpp_value != nullptr) {
socket.typeinfo->get_cpp_value(socket, r_value);
return true;
}
return false;
}
void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
{
bNodeSocket &socket = builder.bsocket();
if (socket.typeinfo->expand_in_mf_network != nullptr) {
socket.typeinfo->expand_in_mf_network(builder);
}
else if (socket.typeinfo->get_cpp_value != nullptr) {
const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
void *buffer = builder.resources().linear_allocator().allocate(type.size(), type.alignment());
socket.typeinfo->get_cpp_value(socket, buffer);
builder.set_constant_value(type, buffer);
}
else {
BLI_assert(false);
}
}
} // namespace blender::nodes

View File

@@ -34,7 +34,7 @@ bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "SimulationNodeTree");
return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "GeometryNodeTree");
}
void sh_node_type_base(