2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2022-02-15 12:32:15 +01:00
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
|
* \ingroup edcurves
|
|
|
|
|
*/
|
|
|
|
|
|
2022-04-07 12:49:13 +02:00
|
|
|
#include <atomic>
|
|
|
|
|
|
2023-01-03 22:59:25 -05:00
|
|
|
#include "BLI_array_utils.hh"
|
2023-01-20 16:40:51 +01:00
|
|
|
#include "BLI_devirtualize_parameters.hh"
|
|
|
|
|
#include "BLI_kdtree.h"
|
Cleanup: reduce amount of math-related includes
Using ClangBuildAnalyzer on the whole Blender build, it was pointing
out that BLI_math.h is the heaviest "header hub" (i.e. non tiny file
that is included a lot).
However, there's very little (actually zero) source files in Blender
that need "all the math" (base, colors, vectors, matrices,
quaternions, intersection, interpolation, statistics, solvers and
time). A common use case is source files needing just vectors, or
just vectors & matrices, or just colors etc. Actually, 181 files
were including the whole math thing without needing it at all.
This change removes BLI_math.h completely, and instead in all the
places that need it, includes BLI_math_vector.h or BLI_math_color.h
and so on.
Change from that:
- BLI_math_color.h was included 1399 times -> now 408 (took 114.0sec
to parse -> now 36.3sec)
- BLI_simd.h 1403 -> 418 (109.7sec -> 34.9sec).
Full rebuild of Blender (Apple M1, Xcode, RelWithDebInfo) is not
affected much (342sec -> 334sec). Most of benefit would be when
someone's changing BLI_simd.h or BLI_math_color.h or similar files,
that now there's 3x fewer files result in a recompile.
Pull Request #110944
2023-08-09 11:39:20 +03:00
|
|
|
#include "BLI_math_geom.h"
|
2023-02-06 21:25:45 +01:00
|
|
|
#include "BLI_math_matrix.hh"
|
2023-01-20 16:40:51 +01:00
|
|
|
#include "BLI_rand.hh"
|
2023-09-01 21:37:11 +02:00
|
|
|
#include "BLI_string.h"
|
2022-03-30 13:29:31 +02:00
|
|
|
#include "BLI_utildefines.h"
|
2022-05-31 19:00:24 +02:00
|
|
|
#include "BLI_vector_set.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2023-06-09 16:12:27 +02:00
|
|
|
#include "BLT_translation.h"
|
|
|
|
|
|
2023-07-15 03:44:58 +02:00
|
|
|
#include "ED_curves.hh"
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_object.hh"
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "ED_screen.hh"
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_select_utils.hh"
|
|
|
|
|
#include "ED_view3d.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_api.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-06-05 12:14:13 +02:00
|
|
|
#include "BKE_attribute_math.hh"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_bvhutils.hh"
|
|
|
|
|
#include "BKE_context.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
#include "BKE_curves.hh"
|
2022-05-31 19:00:24 +02:00
|
|
|
#include "BKE_geometry_set.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
#include "BKE_layer.h"
|
2022-05-31 19:00:24 +02:00
|
|
|
#include "BKE_lib_id.h"
|
2023-03-12 22:29:15 +01:00
|
|
|
#include "BKE_mesh.hh"
|
2023-08-02 22:14:18 +02:00
|
|
|
#include "BKE_mesh_legacy_convert.hh"
|
|
|
|
|
#include "BKE_mesh_runtime.hh"
|
2023-04-28 14:44:08 -04:00
|
|
|
#include "BKE_mesh_sample.hh"
|
2023-10-09 23:41:53 +02:00
|
|
|
#include "BKE_object.hh"
|
2023-08-02 22:14:18 +02:00
|
|
|
#include "BKE_paint.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
#include "BKE_particle.h"
|
2022-04-07 12:49:13 +02:00
|
|
|
#include "BKE_report.h"
|
2022-03-30 13:29:31 +02:00
|
|
|
|
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
|
#include "DNA_modifier_types.h"
|
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
|
#include "DNA_particle_types.h"
|
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph.hh"
|
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
|
|
|
|
#include "RNA_define.hh"
|
|
|
|
|
#include "RNA_enum_types.hh"
|
2022-05-12 11:50:08 +02:00
|
|
|
#include "RNA_prototypes.h"
|
2022-04-07 12:49:13 +02:00
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "UI_interface.hh"
|
|
|
|
|
#include "UI_resources.hh"
|
2023-01-20 16:40:51 +01:00
|
|
|
|
2022-06-05 12:14:13 +02:00
|
|
|
#include "GEO_reverse_uv_sampler.hh"
|
|
|
|
|
|
2022-03-30 13:29:31 +02:00
|
|
|
/**
|
|
|
|
|
* The code below uses a suffix naming convention to indicate the coordinate space:
|
2022-03-31 13:00:10 +11:00
|
|
|
* `cu`: Local space of the curves object that is being edited.
|
|
|
|
|
* `su`: Local space of the surface object.
|
|
|
|
|
* `wo`: World space.
|
|
|
|
|
* `ha`: Local space of an individual hair in the legacy hair system.
|
2022-03-30 13:29:31 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
namespace blender::ed::curves {
|
|
|
|
|
|
2023-01-20 16:32:25 +01:00
|
|
|
bool object_has_editable_curves(const Main &bmain, const Object &object)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
|
|
|
|
if (object.type != OB_CURVES) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-30 15:09:13 +02:00
|
|
|
VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
|
|
|
|
VectorSet<Curves *> unique_curves;
|
|
|
|
|
|
|
|
|
|
const Main &bmain = *CTX_data_main(&C);
|
|
|
|
|
|
|
|
|
|
Object *object = CTX_data_active_object(&C);
|
|
|
|
|
if (object && object_has_editable_curves(bmain, *object)) {
|
|
|
|
|
unique_curves.add_new(static_cast<Curves *>(object->data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
|
|
|
|
|
if (object_has_editable_curves(bmain, *object)) {
|
|
|
|
|
unique_curves.add(static_cast<Curves *>(object->data));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
|
|
return unique_curves;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 16:32:25 +01:00
|
|
|
static bool curves_poll_impl(bContext *C,
|
|
|
|
|
const bool check_editable,
|
|
|
|
|
const bool check_surface,
|
|
|
|
|
const bool check_edit_mode)
|
2022-07-25 11:59:33 -05:00
|
|
|
{
|
|
|
|
|
Object *object = CTX_data_active_object(C);
|
|
|
|
|
if (object == nullptr || object->type != OB_CURVES) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (check_editable) {
|
|
|
|
|
if (!ED_operator_object_active_editable_ex(C, object)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (check_surface) {
|
|
|
|
|
Curves &curves = *static_cast<Curves *>(object->data);
|
|
|
|
|
if (curves.surface == nullptr || curves.surface->type != OB_MESH) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-20 16:32:25 +01:00
|
|
|
if (check_edit_mode) {
|
|
|
|
|
if ((object->mode & OB_MODE_EDIT) == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-25 11:59:33 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 16:32:25 +01:00
|
|
|
bool editable_curves_in_edit_mode_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return curves_poll_impl(C, true, false, true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-25 11:59:33 -05:00
|
|
|
bool editable_curves_with_surface_poll(bContext *C)
|
|
|
|
|
{
|
2023-01-20 16:32:25 +01:00
|
|
|
return curves_poll_impl(C, true, true, false);
|
2022-07-25 11:59:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool curves_with_surface_poll(bContext *C)
|
|
|
|
|
{
|
2023-01-20 16:32:25 +01:00
|
|
|
return curves_poll_impl(C, false, true, false);
|
2022-07-25 11:59:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool editable_curves_poll(bContext *C)
|
|
|
|
|
{
|
2023-01-20 16:32:25 +01:00
|
|
|
return curves_poll_impl(C, false, false, false);
|
2022-07-25 11:59:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool curves_poll(bContext *C)
|
|
|
|
|
{
|
2023-01-20 16:32:25 +01:00
|
|
|
return curves_poll_impl(C, false, false, false);
|
2022-07-25 11:59:33 -05:00
|
|
|
}
|
|
|
|
|
|
2023-02-10 18:58:08 +01:00
|
|
|
static bool editable_curves_point_domain_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
if (!curves::editable_curves_poll(C)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data);
|
|
|
|
|
if (curves_id->selection_domain != ATTR_DOMAIN_POINT) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 13:29:31 +02:00
|
|
|
using bke::CurvesGeometry;
|
|
|
|
|
|
|
|
|
|
namespace convert_to_particle_system {
|
|
|
|
|
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
static int find_mface_for_root_position(const Span<float3> positions,
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
const MFace *mface,
|
2022-03-30 13:29:31 +02:00
|
|
|
const Span<int> possible_mface_indices,
|
|
|
|
|
const float3 &root_pos)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(possible_mface_indices.size() >= 1);
|
|
|
|
|
if (possible_mface_indices.size() == 1) {
|
|
|
|
|
return possible_mface_indices.first();
|
|
|
|
|
}
|
|
|
|
|
/* Find the closest #MFace to #root_pos. */
|
|
|
|
|
int mface_i;
|
|
|
|
|
float best_distance_sq = FLT_MAX;
|
|
|
|
|
for (const int possible_mface_i : possible_mface_indices) {
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
const MFace &possible_mface = mface[possible_mface_i];
|
2022-03-30 13:29:31 +02:00
|
|
|
{
|
|
|
|
|
float3 point_in_triangle;
|
|
|
|
|
closest_on_tri_to_point_v3(point_in_triangle,
|
|
|
|
|
root_pos,
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
positions[possible_mface.v1],
|
|
|
|
|
positions[possible_mface.v2],
|
|
|
|
|
positions[possible_mface.v3]);
|
2022-03-30 13:29:31 +02:00
|
|
|
const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
|
|
|
|
|
if (distance_sq < best_distance_sq) {
|
|
|
|
|
best_distance_sq = distance_sq;
|
|
|
|
|
mface_i = possible_mface_i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Optionally check the second triangle if the #MFace is a quad. */
|
|
|
|
|
if (possible_mface.v4) {
|
|
|
|
|
float3 point_in_triangle;
|
|
|
|
|
closest_on_tri_to_point_v3(point_in_triangle,
|
|
|
|
|
root_pos,
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
positions[possible_mface.v1],
|
|
|
|
|
positions[possible_mface.v3],
|
|
|
|
|
positions[possible_mface.v4]);
|
2022-03-30 13:29:31 +02:00
|
|
|
const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
|
|
|
|
|
if (distance_sq < best_distance_sq) {
|
|
|
|
|
best_distance_sq = distance_sq;
|
|
|
|
|
mface_i = possible_mface_i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mface_i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \return Barycentric coordinates in the #MFace.
|
|
|
|
|
*/
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
static float4 compute_mface_weights_for_position(const Span<float3> positions,
|
2022-03-30 13:29:31 +02:00
|
|
|
const MFace &mface,
|
|
|
|
|
const float3 &position)
|
|
|
|
|
{
|
|
|
|
|
float4 mface_weights;
|
|
|
|
|
if (mface.v4) {
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
float mface_positions_su[4][3];
|
|
|
|
|
copy_v3_v3(mface_positions_su[0], positions[mface.v1]);
|
|
|
|
|
copy_v3_v3(mface_positions_su[1], positions[mface.v2]);
|
|
|
|
|
copy_v3_v3(mface_positions_su[2], positions[mface.v3]);
|
|
|
|
|
copy_v3_v3(mface_positions_su[3], positions[mface.v4]);
|
|
|
|
|
interp_weights_poly_v3(mface_weights, mface_positions_su, 4, position);
|
2022-03-30 13:29:31 +02:00
|
|
|
}
|
|
|
|
|
else {
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
interp_weights_tri_v3(
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
mface_weights, positions[mface.v1], positions[mface.v2], positions[mface.v3], position);
|
2022-03-30 13:29:31 +02:00
|
|
|
mface_weights[3] = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
return mface_weights;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
static void try_convert_single_object(Object &curves_ob,
|
|
|
|
|
Main &bmain,
|
|
|
|
|
Scene &scene,
|
|
|
|
|
bool *r_could_not_convert_some_curves)
|
2022-03-30 13:29:31 +02:00
|
|
|
{
|
2022-04-14 12:25:54 +02:00
|
|
|
if (curves_ob.type != OB_CURVES) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
|
2023-01-31 18:45:34 +01:00
|
|
|
CurvesGeometry &curves = curves_id.geometry.wrap();
|
2022-04-14 12:25:54 +02:00
|
|
|
if (curves_id.surface == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Object &surface_ob = *curves_id.surface;
|
|
|
|
|
if (surface_ob.type != OB_MESH) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
|
|
|
|
|
|
2022-06-05 12:14:13 +02:00
|
|
|
BVHTreeFromMesh surface_bvh;
|
2023-12-14 12:08:21 +11:00
|
|
|
BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRIS, 2);
|
2022-06-05 12:14:13 +02:00
|
|
|
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
|
|
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
const Span<float3> positions_cu = curves.positions();
|
2023-07-24 22:06:55 +02:00
|
|
|
const Span<int> looptri_faces = surface_me.looptri_faces();
|
2022-04-14 12:25:54 +02:00
|
|
|
|
2023-07-24 22:06:55 +02:00
|
|
|
if (looptri_faces.is_empty()) {
|
2022-06-05 12:14:13 +02:00
|
|
|
*r_could_not_convert_some_curves = true;
|
2022-04-14 12:25:54 +02:00
|
|
|
}
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-06-05 12:14:13 +02:00
|
|
|
const int hair_num = curves.curves_num();
|
|
|
|
|
if (hair_num == 0) {
|
2022-04-14 12:25:54 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
ParticleSystem *particle_system = nullptr;
|
|
|
|
|
LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
|
|
|
|
|
if (STREQ(psys->name, curves_ob.id.name + 2)) {
|
|
|
|
|
particle_system = psys;
|
|
|
|
|
break;
|
2022-03-30 13:29:31 +02:00
|
|
|
}
|
2022-04-14 12:25:54 +02:00
|
|
|
}
|
|
|
|
|
if (particle_system == nullptr) {
|
|
|
|
|
ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
|
|
|
|
|
object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2));
|
|
|
|
|
particle_system = psmd.psys;
|
|
|
|
|
particle_system->part->draw_step = 3;
|
|
|
|
|
}
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
ParticleSettings &settings = *particle_system->part;
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
psys_free_particles(particle_system);
|
|
|
|
|
settings.type = PART_HAIR;
|
|
|
|
|
settings.totpart = 0;
|
|
|
|
|
psys_changed_type(&surface_ob, particle_system);
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
MutableSpan<ParticleData> particles{
|
2022-06-05 12:14:13 +02:00
|
|
|
static_cast<ParticleData *>(MEM_calloc_arrayN(hair_num, sizeof(ParticleData), __func__)),
|
|
|
|
|
hair_num};
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
/* The old hair system still uses #MFace, so make sure those are available on the mesh. */
|
|
|
|
|
BKE_mesh_tessface_calc(&surface_me);
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-06-01 15:11:56 +10:00
|
|
|
/* Prepare utility data structure to map hair roots to #MFace's. */
|
2022-04-14 12:25:54 +02:00
|
|
|
const Span<int> mface_to_poly_map{
|
2023-07-24 22:06:55 +02:00
|
|
|
static_cast<const int *>(CustomData_get_layer(&surface_me.fdata_legacy, CD_ORIGINDEX)),
|
|
|
|
|
surface_me.totface_legacy};
|
|
|
|
|
Array<Vector<int>> poly_to_mface_map(surface_me.faces_num);
|
2022-04-14 12:25:54 +02:00
|
|
|
for (const int mface_i : mface_to_poly_map.index_range()) {
|
2023-07-24 22:06:55 +02:00
|
|
|
const int face_i = mface_to_poly_map[mface_i];
|
|
|
|
|
poly_to_mface_map[face_i].append(mface_i);
|
2022-04-14 12:25:54 +02:00
|
|
|
}
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
/* Prepare transformation matrices. */
|
2022-07-05 15:06:14 +02:00
|
|
|
const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
|
2022-04-14 12:25:54 +02:00
|
|
|
|
2023-07-24 22:06:55 +02:00
|
|
|
const MFace *mfaces = (const MFace *)CustomData_get_layer(&surface_me.fdata_legacy, CD_MFACE);
|
2023-01-18 11:52:27 +01:00
|
|
|
const OffsetIndices points_by_curve = curves.points_by_curve();
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
const Span<float3> positions = surface_me.vert_positions();
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
|
2022-06-05 12:14:13 +02:00
|
|
|
for (const int new_hair_i : IndexRange(hair_num)) {
|
|
|
|
|
const int curve_i = new_hair_i;
|
2023-01-18 11:52:27 +01:00
|
|
|
const IndexRange points = points_by_curve[curve_i];
|
2022-04-14 12:25:54 +02:00
|
|
|
|
|
|
|
|
const float3 &root_pos_cu = positions_cu[points.first()];
|
2023-02-06 21:25:45 +01:00
|
|
|
const float3 root_pos_su = math::transform_point(transforms.curves_to_surface, root_pos_cu);
|
2022-04-14 12:25:54 +02:00
|
|
|
|
2022-06-05 12:14:13 +02:00
|
|
|
BVHTreeNearest nearest;
|
|
|
|
|
nearest.dist_sq = FLT_MAX;
|
|
|
|
|
BLI_bvhtree_find_nearest(
|
|
|
|
|
surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh);
|
|
|
|
|
BLI_assert(nearest.index >= 0);
|
|
|
|
|
|
|
|
|
|
const int looptri_i = nearest.index;
|
2023-07-24 22:06:55 +02:00
|
|
|
const int face_i = looptri_faces[looptri_i];
|
2022-06-05 12:14:13 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
const int mface_i = find_mface_for_root_position(
|
2023-07-24 22:06:55 +02:00
|
|
|
positions, mfaces, poly_to_mface_map[face_i], root_pos_su);
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
const MFace &mface = mfaces[mface_i];
|
2022-04-14 12:25:54 +02:00
|
|
|
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
const float4 mface_weights = compute_mface_weights_for_position(positions, mface, root_pos_su);
|
2022-04-14 12:25:54 +02:00
|
|
|
|
|
|
|
|
ParticleData &particle = particles[new_hair_i];
|
|
|
|
|
const int num_keys = points.size();
|
|
|
|
|
MutableSpan<HairKey> hair_keys{
|
|
|
|
|
static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys};
|
|
|
|
|
|
|
|
|
|
particle.hair = hair_keys.data();
|
|
|
|
|
particle.totkey = hair_keys.size();
|
|
|
|
|
copy_v4_v4(particle.fuv, mface_weights);
|
|
|
|
|
particle.num = mface_i;
|
|
|
|
|
/* Not sure if there is a better way to initialize this. */
|
|
|
|
|
particle.num_dmcache = DMCACHE_NOTFOUND;
|
|
|
|
|
|
|
|
|
|
float4x4 hair_to_surface_mat;
|
|
|
|
|
psys_mat_hair_to_object(
|
2023-02-06 21:25:45 +01:00
|
|
|
&surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.ptr());
|
2022-04-14 12:25:54 +02:00
|
|
|
/* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
|
2023-02-06 21:25:45 +01:00
|
|
|
hair_to_surface_mat.location() = root_pos_su;
|
|
|
|
|
const float4x4 surface_to_hair_mat = math::invert(hair_to_surface_mat);
|
2022-04-14 12:25:54 +02:00
|
|
|
|
|
|
|
|
for (const int key_i : hair_keys.index_range()) {
|
|
|
|
|
const float3 &key_pos_cu = positions_cu[points[key_i]];
|
2023-02-06 21:25:45 +01:00
|
|
|
const float3 key_pos_su = math::transform_point(transforms.curves_to_surface, key_pos_cu);
|
|
|
|
|
const float3 key_pos_ha = math::transform_point(surface_to_hair_mat, key_pos_su);
|
2022-04-14 12:25:54 +02:00
|
|
|
|
|
|
|
|
HairKey &key = hair_keys[key_i];
|
|
|
|
|
copy_v3_v3(key.co, key_pos_ha);
|
2023-06-07 14:49:20 +02:00
|
|
|
const float key_fac = key_i / float(hair_keys.size() - 1);
|
|
|
|
|
key.time = 100.0f * key_fac;
|
|
|
|
|
key.weight = 1.0f - key_fac;
|
2022-03-30 13:29:31 +02:00
|
|
|
}
|
2022-04-14 12:25:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
particle_system->particles = particles.data();
|
|
|
|
|
particle_system->totpart = particles.size();
|
|
|
|
|
particle_system->flag |= PSYS_EDITED;
|
|
|
|
|
particle_system->recalc |= ID_RECALC_PSYS_RESET;
|
|
|
|
|
|
|
|
|
|
DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY);
|
|
|
|
|
DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE);
|
|
|
|
|
}
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main &bmain = *CTX_data_main(C);
|
|
|
|
|
Scene &scene = *CTX_data_scene(C);
|
|
|
|
|
|
|
|
|
|
bool could_not_convert_some_curves = false;
|
2022-03-30 13:29:31 +02:00
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
Object &active_object = *CTX_data_active_object(C);
|
|
|
|
|
try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves);
|
|
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
|
|
|
|
|
if (curves_ob != &active_object) {
|
|
|
|
|
try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
|
|
|
|
|
}
|
2022-03-30 13:29:31 +02:00
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
2022-04-14 12:25:54 +02:00
|
|
|
if (could_not_convert_some_curves) {
|
|
|
|
|
BKE_report(op->reports,
|
|
|
|
|
RPT_INFO,
|
|
|
|
|
"Some curves could not be converted because they were not attached to the surface");
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-04 15:42:37 +10:00
|
|
|
WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, nullptr);
|
2022-03-30 13:29:31 +02:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace convert_to_particle_system
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Convert Curves to Particle System";
|
|
|
|
|
ot->idname = "CURVES_OT_convert_to_particle_system";
|
|
|
|
|
ot->description = "Add a new or update an existing hair particle system on the surface object";
|
|
|
|
|
|
2022-07-25 11:59:33 -05:00
|
|
|
ot->poll = curves_with_surface_poll;
|
2022-03-30 13:29:31 +02:00
|
|
|
ot->exec = convert_to_particle_system::curves_convert_to_particle_system_exec;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-12 11:50:08 +02:00
|
|
|
namespace convert_from_particle_system {
|
|
|
|
|
|
|
|
|
|
static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys)
|
|
|
|
|
{
|
|
|
|
|
ParticleSettings &settings = *psys.part;
|
|
|
|
|
if (psys.part->type != PART_HAIR) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0;
|
|
|
|
|
|
|
|
|
|
const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached};
|
|
|
|
|
const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache};
|
|
|
|
|
|
|
|
|
|
int points_num = 0;
|
|
|
|
|
Vector<int> curve_offsets;
|
|
|
|
|
Vector<int> parents_to_transfer;
|
|
|
|
|
Vector<int> children_to_transfer;
|
|
|
|
|
if (transfer_parents) {
|
|
|
|
|
for (const int parent_i : parents_cache.index_range()) {
|
|
|
|
|
const int segments = parents_cache[parent_i]->segments;
|
|
|
|
|
if (segments <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
parents_to_transfer.append(parent_i);
|
|
|
|
|
curve_offsets.append(points_num);
|
|
|
|
|
points_num += segments + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const int child_i : children_cache.index_range()) {
|
|
|
|
|
const int segments = children_cache[child_i]->segments;
|
|
|
|
|
if (segments <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
children_to_transfer.append(child_i);
|
|
|
|
|
curve_offsets.append(points_num);
|
|
|
|
|
points_num += segments + 1;
|
|
|
|
|
}
|
|
|
|
|
const int curves_num = parents_to_transfer.size() + children_to_transfer.size();
|
|
|
|
|
curve_offsets.append(points_num);
|
|
|
|
|
BLI_assert(curve_offsets.size() == curves_num + 1);
|
|
|
|
|
bke::CurvesGeometry curves(points_num, curves_num);
|
|
|
|
|
curves.offsets_for_write().copy_from(curve_offsets);
|
|
|
|
|
|
2023-02-06 21:25:45 +01:00
|
|
|
const float4x4 object_to_world_mat(object.object_to_world);
|
|
|
|
|
const float4x4 world_to_object_mat = math::invert(object_to_world_mat);
|
2022-05-12 11:50:08 +02:00
|
|
|
|
|
|
|
|
MutableSpan<float3> positions = curves.positions_for_write();
|
2023-01-18 11:52:27 +01:00
|
|
|
const OffsetIndices points_by_curve = curves.points_by_curve();
|
2022-05-12 11:50:08 +02:00
|
|
|
|
|
|
|
|
const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache,
|
|
|
|
|
const Span<int> indices_to_transfer,
|
|
|
|
|
const int curve_index_offset) {
|
|
|
|
|
threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) {
|
|
|
|
|
for (const int i : range) {
|
|
|
|
|
const int hair_i = indices_to_transfer[i];
|
|
|
|
|
const int curve_i = i + curve_index_offset;
|
2023-01-18 11:52:27 +01:00
|
|
|
const IndexRange points = points_by_curve[curve_i];
|
2022-05-12 11:50:08 +02:00
|
|
|
const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()};
|
|
|
|
|
for (const int key_i : keys.index_range()) {
|
|
|
|
|
const float3 key_pos_wo = keys[key_i].co;
|
2023-02-06 21:25:45 +01:00
|
|
|
positions[points[key_i]] = math::transform_point(world_to_object_mat, key_pos_wo);
|
2022-05-12 11:50:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (transfer_parents) {
|
|
|
|
|
copy_hair_to_curves(parents_cache, parents_to_transfer, 0);
|
|
|
|
|
}
|
|
|
|
|
copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size());
|
|
|
|
|
|
|
|
|
|
curves.update_curve_types();
|
|
|
|
|
curves.tag_topology_changed();
|
|
|
|
|
return curves;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int curves_convert_from_particle_system_exec(bContext *C, wmOperator * /*op*/)
|
2022-05-12 11:50:08 +02:00
|
|
|
{
|
|
|
|
|
Main &bmain = *CTX_data_main(C);
|
2022-09-14 21:30:20 +02:00
|
|
|
Scene &scene = *CTX_data_scene(C);
|
2022-05-12 11:50:08 +02:00
|
|
|
ViewLayer &view_layer = *CTX_data_view_layer(C);
|
|
|
|
|
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
|
|
|
|
|
Object *ob_from_orig = ED_object_active_context(C);
|
|
|
|
|
ParticleSystem *psys_orig = static_cast<ParticleSystem *>(
|
|
|
|
|
CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
|
|
|
|
|
if (psys_orig == nullptr) {
|
|
|
|
|
psys_orig = psys_get_current(ob_from_orig);
|
|
|
|
|
}
|
|
|
|
|
if (psys_orig == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig);
|
|
|
|
|
ParticleSystem *psys_eval = nullptr;
|
|
|
|
|
LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) {
|
|
|
|
|
if (md->type != eModifierType_ParticleSystem) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md);
|
|
|
|
|
if (!STREQ(psmd->psys->name, psys_orig->name)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
psys_eval = psmd->psys;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-14 21:30:20 +02:00
|
|
|
Object *ob_new = BKE_object_add(&bmain, &scene, &view_layer, OB_CURVES, psys_eval->name);
|
2022-05-12 11:50:08 +02:00
|
|
|
Curves *curves_id = static_cast<Curves *>(ob_new->data);
|
2022-10-24 14:16:37 +02:00
|
|
|
BKE_object_apply_mat4(ob_new, ob_from_orig->object_to_world, true, false);
|
2023-01-31 18:45:34 +01:00
|
|
|
curves_id->geometry.wrap() = particles_to_curves(*ob_from_eval, *psys_eval);
|
2022-05-12 11:50:08 +02:00
|
|
|
|
|
|
|
|
DEG_relations_tag_update(&bmain);
|
|
|
|
|
WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool curves_convert_from_particle_system_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return ED_object_active_context(C) != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace convert_from_particle_system
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Convert Particle System to Curves";
|
|
|
|
|
ot->idname = "CURVES_OT_convert_from_particle_system";
|
|
|
|
|
ot->description = "Add a new curves object based on the current state of the particle system";
|
|
|
|
|
|
|
|
|
|
ot->poll = convert_from_particle_system::curves_convert_from_particle_system_poll;
|
|
|
|
|
ot->exec = convert_from_particle_system::curves_convert_from_particle_system_exec;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 12:49:13 +02:00
|
|
|
namespace snap_curves_to_surface {
|
|
|
|
|
|
|
|
|
|
enum class AttachMode {
|
2023-11-29 17:26:06 +11:00
|
|
|
Nearest = 0,
|
|
|
|
|
Deform = 1,
|
2022-04-07 12:49:13 +02:00
|
|
|
};
|
|
|
|
|
|
2022-07-05 15:37:34 +02:00
|
|
|
static void snap_curves_to_surface_exec_object(Object &curves_ob,
|
|
|
|
|
const Object &surface_ob,
|
|
|
|
|
const AttachMode attach_mode,
|
|
|
|
|
bool *r_invalid_uvs,
|
|
|
|
|
bool *r_missing_uvs)
|
|
|
|
|
{
|
|
|
|
|
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
|
2023-01-31 18:45:34 +01:00
|
|
|
CurvesGeometry &curves = curves_id.geometry.wrap();
|
2022-07-05 15:37:34 +02:00
|
|
|
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
const Mesh &surface_mesh = *static_cast<const Mesh *>(surface_ob.data);
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
const Span<float3> surface_positions = surface_mesh.vert_positions();
|
Mesh: Replace MLoop struct with generic attributes
Implements #102359.
Split the `MLoop` struct into two separate integer arrays called
`corner_verts` and `corner_edges`, referring to the vertex each corner
is attached to and the next edge around the face at each corner. These
arrays can be sliced to give access to the edges or vertices in a face.
Then they are often referred to as "poly_verts" or "poly_edges".
The main benefits are halving the necessary memory bandwidth when only
one array is used and simplifications from using regular integer indices
instead of a special-purpose struct.
The commit also starts a renaming from "loop" to "corner" in mesh code.
Like the other mesh struct of array refactors, forward compatibility is
kept by writing files with the older format. This will be done until 4.0
to ease the transition process.
Looking at a small portion of the patch should give a good impression
for the rest of the changes. I tried to make the changes as small as
possible so it's easy to tell the correctness from the diff. Though I
found Blender developers have been very inventive over the last decade
when finding different ways to loop over the corners in a face.
For performance, nearly every piece of code that deals with `Mesh` is
slightly impacted. Any algorithm that is memory bottle-necked should
see an improvement. For example, here is a comparison of interpolating
a vertex float attribute to face corners (Ryzen 3700x):
**Before** (Average: 3.7 ms, Min: 3.4 ms)
```
threading::parallel_for(loops.index_range(), 4096, [&](IndexRange range) {
for (const int64_t i : range) {
dst[i] = src[loops[i].v];
}
});
```
**After** (Average: 2.9 ms, Min: 2.6 ms)
```
array_utils::gather(src, corner_verts, dst);
```
That's an improvement of 28% to the average timings, and it's also a
simplification, since an index-based routine can be used instead.
For more examples using the new arrays, see the design task.
Pull Request: https://projects.blender.org/blender/blender/pulls/104424
2023-03-20 15:55:13 +01:00
|
|
|
const Span<int> corner_verts = surface_mesh.corner_verts();
|
2022-09-24 11:41:08 +02:00
|
|
|
const Span<MLoopTri> surface_looptris = surface_mesh.looptris();
|
2022-07-05 15:37:34 +02:00
|
|
|
VArraySpan<float2> surface_uv_map;
|
|
|
|
|
if (curves_id.surface_uv_map != nullptr) {
|
2022-09-07 21:41:39 -05:00
|
|
|
const bke::AttributeAccessor surface_attributes = surface_mesh.attributes();
|
2023-04-19 11:21:06 +02:00
|
|
|
surface_uv_map = *surface_attributes.lookup<float2>(curves_id.surface_uv_map,
|
|
|
|
|
ATTR_DOMAIN_CORNER);
|
2022-07-05 15:37:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-18 11:52:27 +01:00
|
|
|
const OffsetIndices points_by_curve = curves.points_by_curve();
|
2022-07-05 15:37:34 +02:00
|
|
|
MutableSpan<float3> positions_cu = curves.positions_for_write();
|
|
|
|
|
MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
|
|
|
|
|
|
|
|
|
|
const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
|
|
|
|
|
|
|
|
|
|
switch (attach_mode) {
|
|
|
|
|
case AttachMode::Nearest: {
|
|
|
|
|
BVHTreeFromMesh surface_bvh;
|
2023-12-14 12:08:21 +11:00
|
|
|
BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRIS, 2);
|
2022-07-05 15:37:34 +02:00
|
|
|
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
|
|
|
|
|
|
|
|
|
|
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
|
|
|
|
|
for (const int curve_i : curves_range) {
|
2023-01-18 11:52:27 +01:00
|
|
|
const IndexRange points = points_by_curve[curve_i];
|
2022-07-05 15:37:34 +02:00
|
|
|
const int first_point_i = points.first();
|
|
|
|
|
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
|
2023-02-06 21:25:45 +01:00
|
|
|
const float3 old_first_point_pos_su = math::transform_point(transforms.curves_to_surface,
|
|
|
|
|
old_first_point_pos_cu);
|
2022-07-05 15:37:34 +02:00
|
|
|
|
|
|
|
|
BVHTreeNearest nearest;
|
|
|
|
|
nearest.index = -1;
|
|
|
|
|
nearest.dist_sq = FLT_MAX;
|
|
|
|
|
BLI_bvhtree_find_nearest(surface_bvh.tree,
|
|
|
|
|
old_first_point_pos_su,
|
|
|
|
|
&nearest,
|
|
|
|
|
surface_bvh.nearest_callback,
|
|
|
|
|
&surface_bvh);
|
|
|
|
|
const int looptri_index = nearest.index;
|
|
|
|
|
if (looptri_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float3 new_first_point_pos_su = nearest.co;
|
2023-02-06 21:25:45 +01:00
|
|
|
const float3 new_first_point_pos_cu = math::transform_point(transforms.surface_to_curves,
|
|
|
|
|
new_first_point_pos_su);
|
2022-07-05 15:37:34 +02:00
|
|
|
const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
|
|
|
|
|
|
|
|
|
|
for (float3 &pos_cu : positions_cu.slice(points)) {
|
|
|
|
|
pos_cu += pos_diff_cu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!surface_uv_map.is_empty()) {
|
2023-12-14 12:08:21 +11:00
|
|
|
const MLoopTri < = surface_looptris[looptri_index];
|
2023-04-28 14:44:08 -04:00
|
|
|
const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
|
2023-12-14 12:08:21 +11:00
|
|
|
surface_positions, corner_verts, lt, new_first_point_pos_su);
|
2023-04-28 14:44:45 -04:00
|
|
|
const float2 uv = bke::mesh_surface_sample::sample_corner_attribute_with_bary_coords(
|
2023-12-14 12:08:21 +11:00
|
|
|
bary_coords, lt, surface_uv_map);
|
2022-07-05 15:37:34 +02:00
|
|
|
surface_uv_coords[curve_i] = uv;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case AttachMode::Deform: {
|
|
|
|
|
if (surface_uv_map.is_empty()) {
|
|
|
|
|
*r_missing_uvs = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
using geometry::ReverseUVSampler;
|
|
|
|
|
ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris};
|
|
|
|
|
|
|
|
|
|
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
|
|
|
|
|
for (const int curve_i : curves_range) {
|
2023-01-18 11:52:27 +01:00
|
|
|
const IndexRange points = points_by_curve[curve_i];
|
2022-07-05 15:37:34 +02:00
|
|
|
const int first_point_i = points.first();
|
|
|
|
|
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
|
|
|
|
|
|
|
|
|
|
const float2 uv = surface_uv_coords[curve_i];
|
|
|
|
|
ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
|
|
|
|
|
if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
|
|
|
|
|
*r_invalid_uvs = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-14 12:08:21 +11:00
|
|
|
const MLoopTri < = surface_looptris[lookup_result.looptri_index];
|
2022-07-05 15:37:34 +02:00
|
|
|
const float3 &bary_coords = lookup_result.bary_weights;
|
|
|
|
|
|
2023-12-14 12:08:21 +11:00
|
|
|
const float3 &p0_su = surface_positions[corner_verts[lt.tri[0]]];
|
|
|
|
|
const float3 &p1_su = surface_positions[corner_verts[lt.tri[1]]];
|
|
|
|
|
const float3 &p2_su = surface_positions[corner_verts[lt.tri[2]]];
|
2022-07-05 15:37:34 +02:00
|
|
|
|
|
|
|
|
float3 new_first_point_pos_su;
|
|
|
|
|
interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
|
2023-02-06 21:25:45 +01:00
|
|
|
const float3 new_first_point_pos_cu = math::transform_point(transforms.surface_to_curves,
|
|
|
|
|
new_first_point_pos_su);
|
2022-07-05 15:37:34 +02:00
|
|
|
|
|
|
|
|
const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
|
|
|
|
|
for (float3 &pos_cu : positions_cu.slice(points)) {
|
|
|
|
|
pos_cu += pos_diff_cu;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 09:37:57 +02:00
|
|
|
curves.tag_positions_changed();
|
2022-07-05 15:37:34 +02:00
|
|
|
DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 12:49:13 +02:00
|
|
|
static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
|
|
|
|
|
|
2022-07-05 15:37:34 +02:00
|
|
|
bool found_invalid_uvs = false;
|
|
|
|
|
bool found_missing_uvs = false;
|
2022-04-07 12:49:13 +02:00
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
|
|
|
|
|
if (curves_ob->type != OB_CURVES) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
|
|
|
|
|
if (curves_id.surface == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-07-05 15:37:34 +02:00
|
|
|
if (curves_id.surface->type != OB_MESH) {
|
2022-04-07 12:49:13 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-07-05 15:37:34 +02:00
|
|
|
snap_curves_to_surface_exec_object(
|
|
|
|
|
*curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
|
2022-04-07 12:49:13 +02:00
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
2022-07-05 15:37:34 +02:00
|
|
|
if (found_missing_uvs) {
|
|
|
|
|
BKE_report(op->reports,
|
|
|
|
|
RPT_ERROR,
|
|
|
|
|
"Curves do not have attachment information that can be used for deformation");
|
|
|
|
|
}
|
|
|
|
|
if (found_invalid_uvs) {
|
2022-04-07 12:49:13 +02:00
|
|
|
BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 16:25:20 +10:00
|
|
|
/* Refresh the entire window to also clear eventual modifier and nodes editor warnings. */
|
2022-07-08 14:45:48 +02:00
|
|
|
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
2022-04-07 12:49:13 +02:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace snap_curves_to_surface
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
using namespace snap_curves_to_surface;
|
|
|
|
|
|
|
|
|
|
ot->name = "Snap Curves to Surface";
|
|
|
|
|
ot->idname = "CURVES_OT_snap_curves_to_surface";
|
|
|
|
|
ot->description = "Move curves so that the first point is exactly on the surface mesh";
|
|
|
|
|
|
2022-07-25 11:59:33 -05:00
|
|
|
ot->poll = editable_curves_with_surface_poll;
|
2022-04-07 12:49:13 +02:00
|
|
|
ot->exec = snap_curves_to_surface_exec;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
|
|
|
|
|
|
|
|
|
static const EnumPropertyItem attach_mode_items[] = {
|
2022-09-25 18:30:50 +10:00
|
|
|
{int(AttachMode::Nearest),
|
2022-04-07 12:49:13 +02:00
|
|
|
"NEAREST",
|
|
|
|
|
0,
|
|
|
|
|
"Nearest",
|
|
|
|
|
"Find the closest point on the surface for the root point of every curve and move the root "
|
|
|
|
|
"there"},
|
2022-09-25 18:30:50 +10:00
|
|
|
{int(AttachMode::Deform),
|
2022-04-07 12:49:13 +02:00
|
|
|
"DEFORM",
|
|
|
|
|
0,
|
|
|
|
|
"Deform",
|
|
|
|
|
"Re-attach curves to a deformed surface using the existing attachment information. This "
|
|
|
|
|
"only works when the topology of the surface mesh has not changed"},
|
2022-04-13 17:51:20 -05:00
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
2022-04-07 12:49:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
RNA_def_enum(ot->srna,
|
|
|
|
|
"attach_mode",
|
|
|
|
|
attach_mode_items,
|
2022-09-25 18:30:50 +10:00
|
|
|
int(AttachMode::Nearest),
|
2022-04-07 12:49:13 +02:00
|
|
|
"Attach Mode",
|
|
|
|
|
"How to find the point on the surface to attach to");
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-31 19:00:24 +02:00
|
|
|
namespace set_selection_domain {
|
|
|
|
|
|
|
|
|
|
static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2022-06-01 14:38:06 +10:00
|
|
|
const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain"));
|
2022-05-31 19:00:24 +02:00
|
|
|
|
|
|
|
|
for (Curves *curves_id : get_unique_editable_curves(*C)) {
|
2022-12-24 16:09:16 -05:00
|
|
|
if (curves_id->selection_domain == domain) {
|
2022-05-31 19:00:24 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curves_id->selection_domain = domain;
|
|
|
|
|
|
2023-01-31 18:45:34 +01:00
|
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
2022-07-28 17:39:10 -05:00
|
|
|
if (curves.points_num() == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-01-03 22:59:25 -05:00
|
|
|
|
2023-10-18 11:31:34 +02:00
|
|
|
/* Adding and removing attributes with the C++ API doesn't affect the active attribute index.
|
|
|
|
|
* In order to make the active attribute consistent before and after the change, save the name
|
|
|
|
|
* and reset the active item afterwards.
|
|
|
|
|
*
|
|
|
|
|
* This would be unnecessary if the active attribute were stored as a string on the ID. */
|
|
|
|
|
std::string active_attribute;
|
|
|
|
|
if (const CustomDataLayer *layer = BKE_id_attributes_active_get(&curves_id->id)) {
|
|
|
|
|
active_attribute = layer->name;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:21:06 +02:00
|
|
|
if (const GVArray src = *attributes.lookup(".selection", domain)) {
|
2023-02-13 11:22:24 -05:00
|
|
|
const CPPType &type = src.type();
|
|
|
|
|
void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
|
|
|
|
|
src.materialize(dst);
|
|
|
|
|
|
|
|
|
|
attributes.remove(".selection");
|
|
|
|
|
if (!attributes.add(".selection",
|
|
|
|
|
domain,
|
|
|
|
|
bke::cpp_type_to_custom_data_type(type),
|
|
|
|
|
bke::AttributeInitMoveArray(dst)))
|
|
|
|
|
{
|
|
|
|
|
MEM_freeN(dst);
|
|
|
|
|
}
|
2022-05-31 19:00:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-18 11:31:34 +02:00
|
|
|
BKE_id_attributes_active_set(&curves_id->id, active_attribute.c_str());
|
|
|
|
|
|
2022-05-31 19:00:24 +02:00
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace set_selection_domain
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
|
|
|
|
|
ot->name = "Set Select Mode";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Change the mode used for selection masking in curves sculpt mode";
|
|
|
|
|
|
|
|
|
|
ot->exec = set_selection_domain::curves_set_selection_domain_exec;
|
2022-07-25 11:59:33 -05:00
|
|
|
ot->poll = editable_curves_poll;
|
2022-05-31 19:00:24 +02:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
ot->prop = prop = RNA_def_enum(
|
|
|
|
|
ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", "");
|
|
|
|
|
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 16:40:51 +01:00
|
|
|
static bool has_anything_selected(const Span<Curves *> curves_ids)
|
|
|
|
|
{
|
|
|
|
|
return std::any_of(curves_ids.begin(), curves_ids.end(), [](const Curves *curves_id) {
|
2023-01-31 18:45:34 +01:00
|
|
|
return has_anything_selected(curves_id->geometry.wrap());
|
2023-01-20 16:40:51 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int select_all_exec(bContext *C, wmOperator *op)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
2023-01-20 16:40:51 +01:00
|
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
|
|
|
|
|
|
|
|
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
|
|
|
|
|
|
|
|
|
|
if (action == SEL_TOGGLE) {
|
|
|
|
|
action = has_anything_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
|
2023-01-03 22:59:25 -05:00
|
|
|
}
|
2023-01-20 16:40:51 +01:00
|
|
|
|
|
|
|
|
for (Curves *curves_id : unique_curves) {
|
|
|
|
|
/* (De)select all the curves. */
|
2023-01-31 18:45:34 +01:00
|
|
|
select_all(curves_id->geometry.wrap(), eAttrDomain(curves_id->selection_domain), action);
|
2023-01-20 16:40:51 +01:00
|
|
|
|
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
2023-01-03 22:59:25 -05:00
|
|
|
}
|
2023-01-20 16:40:51 +01:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
2022-05-31 19:00:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 16:40:51 +01:00
|
|
|
static void CURVES_OT_select_all(wmOperatorType *ot)
|
2022-06-30 15:09:13 +02:00
|
|
|
{
|
2023-01-20 16:40:51 +01:00
|
|
|
ot->name = "(De)select All";
|
|
|
|
|
ot->idname = "CURVES_OT_select_all";
|
|
|
|
|
ot->description = "(De)select all control points";
|
|
|
|
|
|
|
|
|
|
ot->exec = select_all_exec;
|
|
|
|
|
ot->poll = editable_curves_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
WM_operator_properties_select_all(ot);
|
2022-05-31 19:00:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 16:40:51 +01:00
|
|
|
static int select_random_exec(bContext *C, wmOperator *op)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
2023-01-20 16:40:51 +01:00
|
|
|
VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
|
|
|
|
|
|
|
|
|
|
const int seed = RNA_int_get(op->ptr, "seed");
|
|
|
|
|
const float probability = RNA_float_get(op->ptr, "probability");
|
|
|
|
|
|
|
|
|
|
for (Curves *curves_id : unique_curves) {
|
2023-01-31 18:45:34 +01:00
|
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
2023-07-05 13:48:22 +02:00
|
|
|
const eAttrDomain selection_domain = eAttrDomain(curves_id->selection_domain);
|
2023-11-13 12:45:28 +01:00
|
|
|
const int domain_size = curves.attributes().domain_size(selection_domain);
|
2023-07-05 13:48:22 +02:00
|
|
|
|
|
|
|
|
IndexMaskMemory memory;
|
2023-08-09 16:14:33 +02:00
|
|
|
const IndexMask inv_random_elements = random_mask(
|
|
|
|
|
curves, selection_domain, seed, probability, memory)
|
2023-11-13 12:45:28 +01:00
|
|
|
.complement(IndexRange(domain_size), memory);
|
2023-07-05 13:48:22 +02:00
|
|
|
|
|
|
|
|
const bool was_anything_selected = has_anything_selected(curves);
|
|
|
|
|
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
|
|
|
|
curves, selection_domain, CD_PROP_BOOL);
|
|
|
|
|
if (!was_anything_selected) {
|
|
|
|
|
curves::fill_selection_true(selection.span);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 16:14:33 +02:00
|
|
|
curves::fill_selection_false(selection.span, inv_random_elements);
|
2023-07-05 13:48:22 +02:00
|
|
|
selection.finish();
|
2023-01-20 16:40:51 +01:00
|
|
|
|
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_FINISHED;
|
2022-05-31 19:00:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 16:40:51 +01:00
|
|
|
static void select_random_ui(bContext * /*C*/, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
uiLayout *layout = op->layout;
|
2022-06-30 15:09:13 +02:00
|
|
|
|
2023-07-29 15:06:33 +10:00
|
|
|
uiItemR(layout, op->ptr, "seed", UI_ITEM_NONE, nullptr, ICON_NONE);
|
2023-01-20 16:40:51 +01:00
|
|
|
uiItemR(layout, op->ptr, "probability", UI_ITEM_R_SLIDER, "Probability", ICON_NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_select_random(wmOperatorType *ot)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
2023-01-20 16:40:51 +01:00
|
|
|
ot->name = "Select Random";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Randomizes existing selection or create new random selection";
|
|
|
|
|
|
|
|
|
|
ot->exec = select_random_exec;
|
|
|
|
|
ot->poll = curves::editable_curves_poll;
|
|
|
|
|
ot->ui = select_random_ui;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
RNA_def_int(ot->srna,
|
|
|
|
|
"seed",
|
|
|
|
|
0,
|
|
|
|
|
INT32_MIN,
|
|
|
|
|
INT32_MAX,
|
|
|
|
|
"Seed",
|
|
|
|
|
"Source of randomness",
|
|
|
|
|
INT32_MIN,
|
|
|
|
|
INT32_MAX);
|
|
|
|
|
RNA_def_float(ot->srna,
|
|
|
|
|
"probability",
|
|
|
|
|
0.5f,
|
|
|
|
|
0.0f,
|
|
|
|
|
1.0f,
|
|
|
|
|
"Probability",
|
|
|
|
|
"Chance of every point or curve being included in the selection",
|
|
|
|
|
0.0f,
|
|
|
|
|
1.0f);
|
2022-05-31 19:00:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-09 16:12:27 +02:00
|
|
|
static int select_ends_exec(bContext *C, wmOperator *op)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
2023-01-20 16:40:51 +01:00
|
|
|
VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
|
2023-06-09 16:12:27 +02:00
|
|
|
const int amount_start = RNA_int_get(op->ptr, "amount_start");
|
|
|
|
|
const int amount_end = RNA_int_get(op->ptr, "amount_end");
|
2022-05-31 19:00:24 +02:00
|
|
|
|
|
|
|
|
for (Curves *curves_id : unique_curves) {
|
2023-01-31 18:45:34 +01:00
|
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
2023-06-26 11:21:16 +02:00
|
|
|
|
|
|
|
|
IndexMaskMemory memory;
|
|
|
|
|
const IndexMask inverted_end_points_mask = end_points(
|
|
|
|
|
curves, amount_start, amount_end, true, memory);
|
|
|
|
|
|
|
|
|
|
const bool was_anything_selected = has_anything_selected(curves);
|
|
|
|
|
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
|
|
|
|
curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL);
|
|
|
|
|
if (!was_anything_selected) {
|
|
|
|
|
fill_selection_true(selection.span);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selection.span.type().is<bool>()) {
|
|
|
|
|
index_mask::masked_fill(selection.span.typed<bool>(), false, inverted_end_points_mask);
|
|
|
|
|
}
|
|
|
|
|
if (selection.span.type().is<float>()) {
|
|
|
|
|
index_mask::masked_fill(selection.span.typed<float>(), 0.0f, inverted_end_points_mask);
|
|
|
|
|
}
|
|
|
|
|
selection.finish();
|
2022-05-31 19:00:24 +02:00
|
|
|
|
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-09 16:12:27 +02:00
|
|
|
static void select_ends_ui(bContext * /*C*/, wmOperator *op)
|
2022-05-31 19:00:24 +02:00
|
|
|
{
|
2023-06-09 16:12:27 +02:00
|
|
|
uiLayout *layout = op->layout;
|
|
|
|
|
|
|
|
|
|
uiLayoutSetPropSep(layout, true);
|
|
|
|
|
|
|
|
|
|
uiLayout *col = uiLayoutColumn(layout, true);
|
|
|
|
|
uiLayoutSetPropDecorate(col, false);
|
2023-07-29 15:06:33 +10:00
|
|
|
uiItemR(col, op->ptr, "amount_start", UI_ITEM_NONE, IFACE_("Amount Start"), ICON_NONE);
|
|
|
|
|
uiItemR(col, op->ptr, "amount_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
|
2023-06-09 16:12:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_select_ends(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Select Ends";
|
2023-01-20 16:40:51 +01:00
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Select end points of curves";
|
2022-05-31 19:00:24 +02:00
|
|
|
|
2023-06-09 16:12:27 +02:00
|
|
|
ot->exec = select_ends_exec;
|
|
|
|
|
ot->ui = select_ends_ui;
|
2023-02-10 18:58:08 +01:00
|
|
|
ot->poll = editable_curves_point_domain_poll;
|
2022-05-31 19:00:24 +02:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
2023-06-09 16:12:27 +02:00
|
|
|
RNA_def_int(ot->srna,
|
|
|
|
|
"amount_start",
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
INT32_MAX,
|
|
|
|
|
"Amount Front",
|
|
|
|
|
"Number of points to select from the front",
|
|
|
|
|
0,
|
|
|
|
|
INT32_MAX);
|
|
|
|
|
RNA_def_int(ot->srna,
|
|
|
|
|
"amount_end",
|
|
|
|
|
1,
|
|
|
|
|
0,
|
|
|
|
|
INT32_MAX,
|
|
|
|
|
"Amount Back",
|
|
|
|
|
"Number of points to select from the back",
|
|
|
|
|
0,
|
|
|
|
|
INT32_MAX);
|
2022-05-31 19:00:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-10 18:58:08 +01:00
|
|
|
static int select_linked_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
|
|
|
|
|
for (Curves *curves_id : unique_curves) {
|
|
|
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
|
|
|
select_linked(curves);
|
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_select_linked(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Select Linked";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Select all points in curves with any point selection";
|
|
|
|
|
|
|
|
|
|
ot->exec = select_linked_exec;
|
|
|
|
|
ot->poll = editable_curves_point_domain_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-20 11:51:16 +01:00
|
|
|
static int select_more_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
|
|
|
|
|
for (Curves *curves_id : unique_curves) {
|
|
|
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
|
|
|
select_adjacent(curves, false);
|
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_select_more(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Select More";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Grow the selection by one point";
|
|
|
|
|
|
|
|
|
|
ot->exec = select_more_exec;
|
|
|
|
|
ot->poll = editable_curves_point_domain_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int select_less_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
|
|
|
|
|
for (Curves *curves_id : unique_curves) {
|
|
|
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
|
|
|
select_adjacent(curves, true);
|
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
|
|
|
* attribute for now. */
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_select_less(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Select Less";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Shrink the selection by one point";
|
|
|
|
|
|
|
|
|
|
ot->exec = select_less_exec;
|
|
|
|
|
ot->poll = editable_curves_point_domain_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 14:45:48 +02:00
|
|
|
namespace surface_set {
|
|
|
|
|
|
|
|
|
|
static bool surface_set_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
const Object *object = CTX_data_active_object(C);
|
|
|
|
|
if (object == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (object->type != OB_MESH) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int surface_set_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
|
|
|
|
|
Object &new_surface_ob = *CTX_data_active_object(C);
|
|
|
|
|
|
|
|
|
|
Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data);
|
2023-07-25 21:15:52 +02:00
|
|
|
const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.loop_data,
|
Mesh: Move UV layers to generic attributes
Currently the `MLoopUV` struct stores UV coordinates and flags related
to editing UV maps in the UV editor. This patch changes the coordinates
to use the generic 2D vector type, and moves the flags into three
separate boolean attributes. This follows the design in T95965, with
the ultimate intention of simplifying code and improving performance.
Importantly, the change allows exporters and renderers to use UVs
"touched" by geometry nodes, which only creates generic attributes.
It also allows geometry nodes to create "proper" UV maps from scratch,
though only with the Store Named Attribute node for now.
The new design considers any 2D vector attribute on the corner domain
to be a UV map. In the future, they might be distinguished from regular
2D vectors with attribute metadata, which may be helpful because they
are often interpolated differently.
Most of the code changes deal with passing around UV BMesh custom data
offsets and tracking the boolean "sublayers". The boolean layers are
use the following prefixes for attribute names: vert selection: `.vs.`,
edge selection: `.es.`, pinning: `.pn.`. Currently these are short to
avoid using up the maximum length of attribute names. To accommodate
for these 4 extra characters, the name length limit is enlarged to 68
bytes, while the maximum user settable name length is still 64 bytes.
Unfortunately Python/RNA API access to the UV flag data becomes slower.
Accessing the boolean layers directly is be better for performance in
general.
Like the other mesh SoA refactors, backward and forward compatibility
aren't affected, and won't be changed until 4.0. We pay for that by
making mesh reading and writing more expensive with conversions.
Resolves T85962
Differential Revision: https://developer.blender.org/D14365
2023-01-10 00:47:04 -05:00
|
|
|
CD_PROP_FLOAT2);
|
2022-07-08 14:45:48 +02:00
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) {
|
|
|
|
|
if (selected_ob->type != OB_CURVES) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Object &curves_ob = *selected_ob;
|
|
|
|
|
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
|
|
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(curves_id.surface_uv_map);
|
|
|
|
|
if (new_uv_map_name != nullptr) {
|
|
|
|
|
curves_id.surface_uv_map = BLI_strdup(new_uv_map_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool missing_uvs;
|
|
|
|
|
bool invalid_uvs;
|
|
|
|
|
snap_curves_to_surface::snap_curves_to_surface_exec_object(
|
|
|
|
|
curves_ob,
|
|
|
|
|
new_surface_ob,
|
|
|
|
|
snap_curves_to_surface::AttachMode::Nearest,
|
|
|
|
|
&invalid_uvs,
|
|
|
|
|
&missing_uvs);
|
|
|
|
|
|
|
|
|
|
/* Add deformation modifier if necessary. */
|
|
|
|
|
blender::ed::curves::ensure_surface_deformation_node_exists(*C, curves_ob);
|
|
|
|
|
|
|
|
|
|
curves_id.surface = &new_surface_ob;
|
|
|
|
|
ED_object_parent_set(
|
|
|
|
|
op->reports, C, scene, &curves_ob, &new_surface_ob, PAR_OBJECT, false, true, nullptr);
|
|
|
|
|
|
|
|
|
|
DEG_id_tag_update(&curves_ob.id, ID_RECALC_TRANSFORM);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
|
2022-11-19 11:51:42 +01:00
|
|
|
WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
|
2022-07-08 14:45:48 +02:00
|
|
|
|
|
|
|
|
/* Required for deformation. */
|
|
|
|
|
new_surface_ob.modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION;
|
|
|
|
|
DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY);
|
|
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace surface_set
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_surface_set(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Set Curves Surface Object";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description =
|
|
|
|
|
"Use the active object as surface for selected curves objects and set it as the parent";
|
|
|
|
|
|
|
|
|
|
ot->exec = surface_set::surface_set_exec;
|
|
|
|
|
ot->poll = surface_set::surface_set_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-15 10:50:01 +01:00
|
|
|
namespace curves_delete {
|
|
|
|
|
|
|
|
|
|
static int delete_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
for (Curves *curves_id : get_unique_editable_curves(*C)) {
|
|
|
|
|
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
2023-02-16 15:26:13 +01:00
|
|
|
if (remove_selection(curves, eAttrDomain(curves_id->selection_domain))) {
|
2023-02-15 10:50:01 +01:00
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace curves_delete
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_delete(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Delete";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Remove selected control points or curves";
|
|
|
|
|
|
|
|
|
|
ot->exec = curves_delete::delete_exec;
|
|
|
|
|
ot->poll = editable_curves_in_edit_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 15:44:59 -05:00
|
|
|
namespace curves_duplicate {
|
|
|
|
|
|
|
|
|
|
static int delete_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
for (Curves *curves_id : get_unique_editable_curves(*C)) {
|
|
|
|
|
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
|
|
|
IndexMaskMemory memory;
|
|
|
|
|
switch (eAttrDomain(curves_id->selection_domain)) {
|
|
|
|
|
case ATTR_DOMAIN_POINT:
|
|
|
|
|
duplicate_points(curves, retrieve_selected_points(*curves_id, memory));
|
|
|
|
|
break;
|
|
|
|
|
case ATTR_DOMAIN_CURVE:
|
|
|
|
|
duplicate_curves(curves, retrieve_selected_curves(*curves_id, memory));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace curves_duplicate
|
|
|
|
|
|
|
|
|
|
static void CURVES_OT_duplicate(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Duplicate";
|
|
|
|
|
ot->idname = __func__;
|
|
|
|
|
ot->description = "Copy selected points or curves";
|
|
|
|
|
|
|
|
|
|
ot->exec = curves_duplicate::delete_exec;
|
|
|
|
|
ot->poll = editable_curves_in_edit_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 13:29:31 +02:00
|
|
|
} // namespace blender::ed::curves
|
2022-02-15 12:32:15 +01:00
|
|
|
|
|
|
|
|
void ED_operatortypes_curves()
|
|
|
|
|
{
|
2022-03-30 13:29:31 +02:00
|
|
|
using namespace blender::ed::curves;
|
2023-10-17 12:35:40 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_attribute_set);
|
2022-03-30 13:29:31 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_convert_to_particle_system);
|
2022-05-12 11:50:08 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_convert_from_particle_system);
|
2023-12-11 19:44:19 +01:00
|
|
|
WM_operatortype_append(CURVES_OT_draw);
|
2022-04-07 12:49:13 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_snap_curves_to_surface);
|
2022-05-31 19:00:24 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_set_selection_domain);
|
2023-01-13 10:47:24 -06:00
|
|
|
WM_operatortype_append(CURVES_OT_select_all);
|
2023-01-20 16:40:51 +01:00
|
|
|
WM_operatortype_append(CURVES_OT_select_random);
|
2023-06-09 16:12:27 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_select_ends);
|
2023-02-10 18:58:08 +01:00
|
|
|
WM_operatortype_append(CURVES_OT_select_linked);
|
2023-02-20 11:51:16 +01:00
|
|
|
WM_operatortype_append(CURVES_OT_select_more);
|
|
|
|
|
WM_operatortype_append(CURVES_OT_select_less);
|
2022-07-08 14:45:48 +02:00
|
|
|
WM_operatortype_append(CURVES_OT_surface_set);
|
2023-02-15 10:50:01 +01:00
|
|
|
WM_operatortype_append(CURVES_OT_delete);
|
2023-12-11 15:44:59 -05:00
|
|
|
WM_operatortype_append(CURVES_OT_duplicate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ED_operatormacros_curves()
|
|
|
|
|
{
|
|
|
|
|
wmOperatorType *ot;
|
|
|
|
|
wmOperatorTypeMacro *otmacro;
|
|
|
|
|
|
|
|
|
|
/* Duplicate + Move = Interactively place newly duplicated strokes */
|
|
|
|
|
ot = WM_operatortype_append_macro("CURVES_OT_duplicate_move",
|
|
|
|
|
"Duplicate",
|
|
|
|
|
"Make copies of selected elements and move them",
|
|
|
|
|
OPTYPE_UNDO | OPTYPE_REGISTER);
|
|
|
|
|
WM_operatortype_macro_define(ot, "CURVES_OT_duplicate");
|
|
|
|
|
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
|
|
|
|
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
|
|
|
|
RNA_boolean_set(otmacro->ptr, "mirror", false);
|
2022-02-15 12:32:15 +01:00
|
|
|
}
|
2023-01-20 16:40:51 +01:00
|
|
|
|
|
|
|
|
void ED_keymap_curves(wmKeyConfig *keyconf)
|
|
|
|
|
{
|
|
|
|
|
using namespace blender::ed::curves;
|
|
|
|
|
/* Only set in editmode curves, by space_view3d listener. */
|
2023-09-14 13:32:42 +10:00
|
|
|
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
2023-10-31 21:48:27 +11:00
|
|
|
keymap->poll = editable_curves_in_edit_mode_poll;
|
2023-01-20 16:40:51 +01:00
|
|
|
}
|