Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
|
|
2025-04-01 12:35:01 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup pythonintern
|
2025-04-08 12:28:57 +02:00
|
|
|
*
|
|
|
|
|
* This file contains the `bpy.types.GeometrySet` Python API which is a wrapper for the internal
|
|
|
|
|
* `GeometrySet` type.
|
|
|
|
|
*
|
|
|
|
|
* It's not implemented as RNA type because a `GeometrySet` is standalone (i.e. is not necessarily
|
|
|
|
|
* owned by anything else in Blender like an ID), is wrapping a DNA type and is itself a
|
|
|
|
|
* non-trivial owner of other data (like sub-geometries).
|
2025-04-01 12:35:01 +11:00
|
|
|
*/
|
|
|
|
|
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
|
|
#include "BKE_duplilist.hh"
|
|
|
|
|
#include "BKE_geometry_set.hh"
|
|
|
|
|
#include "BKE_geometry_set_instances.hh"
|
|
|
|
|
#include "BKE_idtype.hh"
|
|
|
|
|
#include "BKE_instances.hh"
|
|
|
|
|
#include "BKE_lib_id.hh"
|
|
|
|
|
#include "BKE_mesh_wrapper.hh"
|
|
|
|
|
#include "BKE_pointcloud.hh"
|
|
|
|
|
|
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
|
|
|
|
|
|
#include "DNA_ID.h"
|
|
|
|
|
#include "DNA_collection_types.h"
|
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
|
#include "DNA_pointcloud_types.h"
|
|
|
|
|
|
|
|
|
|
#include "RNA_enum_types.hh"
|
|
|
|
|
#include "RNA_prototypes.hh"
|
|
|
|
|
|
|
|
|
|
#include "bpy_geometry_set.hh"
|
|
|
|
|
#include "bpy_rna.hh"
|
|
|
|
|
|
2025-04-01 12:33:56 +11:00
|
|
|
#include "../generic/py_capi_utils.hh"
|
|
|
|
|
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
using blender::bke::GeometrySet;
|
|
|
|
|
|
|
|
|
|
extern PyTypeObject bpy_geometry_set_Type;
|
|
|
|
|
|
|
|
|
|
struct BPy_GeometrySet {
|
|
|
|
|
PyObject_HEAD
|
|
|
|
|
GeometrySet geometry;
|
|
|
|
|
PointCloud *instances_pointcloud;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static BPy_GeometrySet *python_object_from_geometry_set(GeometrySet geometry = {})
|
|
|
|
|
{
|
|
|
|
|
BPy_GeometrySet *self = reinterpret_cast<BPy_GeometrySet *>(
|
|
|
|
|
bpy_geometry_set_Type.tp_alloc(&bpy_geometry_set_Type, 0));
|
|
|
|
|
if (self == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
new (&self->geometry) GeometrySet(std::move(geometry));
|
|
|
|
|
self->instances_pointcloud = nullptr;
|
|
|
|
|
/* We can't safely give access to shared geometries via the Python API currently, because
|
|
|
|
|
* constness can't be enforced. Therefore, ensure that this Python object has its own copy of
|
|
|
|
|
* each data-block. Note that attributes may still be shared with other data in Blender. */
|
|
|
|
|
self->geometry.ensure_no_shared_components();
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 12:12:48 +02:00
|
|
|
static PyObject *BPy_GeometrySet_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
{
|
2025-04-08 12:12:48 +02:00
|
|
|
static const char *kwlist[] = {nullptr};
|
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", const_cast<char **>(kwlist))) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2025-04-08 09:54:39 +02:00
|
|
|
return reinterpret_cast<PyObject *>(python_object_from_geometry_set());
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void BPy_GeometrySet_dealloc(BPy_GeometrySet *self)
|
|
|
|
|
{
|
|
|
|
|
std::destroy_at(&self->geometry);
|
|
|
|
|
if (self->instances_pointcloud) {
|
|
|
|
|
BKE_id_free(nullptr, self->instances_pointcloud);
|
|
|
|
|
}
|
|
|
|
|
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_from_evaluated_object_doc,
|
|
|
|
|
".. staticmethod:: from_evaluated_object(evaluated_object)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" Create a geometry set from the evaluated geometry of an evaluated object.\n"
|
|
|
|
|
" Typically, it's more convenient to use :func:`bpy.types.Object.evaluated_geometry`.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" :arg evaluated_object: The evaluated object to create a geometry set from.\n"
|
|
|
|
|
" :type evaluated_object: bpy.types.Object\n");
|
|
|
|
|
static BPy_GeometrySet *BPy_GeometrySet_static_from_evaluated_object(PyObject * /*self*/,
|
|
|
|
|
PyObject *args,
|
|
|
|
|
PyObject *kwds)
|
|
|
|
|
{
|
|
|
|
|
using namespace blender;
|
|
|
|
|
static const char *kwlist[] = {"evaluated_object", nullptr};
|
|
|
|
|
PyObject *py_evaluated_object;
|
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(
|
|
|
|
|
args, kwds, "O", const_cast<char **>(kwlist), &py_evaluated_object))
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
ID *evaluated_object_id = nullptr;
|
|
|
|
|
if (!pyrna_id_FromPyObject(py_evaluated_object, &evaluated_object_id)) {
|
|
|
|
|
PyErr_Format(
|
|
|
|
|
PyExc_TypeError, "Expected an Object, not %.200s", Py_TYPE(py_evaluated_object)->tp_name);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GS(evaluated_object_id->name) != ID_OB) {
|
|
|
|
|
PyErr_Format(PyExc_TypeError,
|
|
|
|
|
"Expected an Object, not %.200s",
|
|
|
|
|
BKE_idtype_idcode_to_name(GS(evaluated_object_id->name)));
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
Object *evaluated_object = reinterpret_cast<Object *>(evaluated_object_id);
|
2025-05-02 15:08:29 +02:00
|
|
|
if (!DEG_is_evaluated(evaluated_object)) {
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
PyErr_SetString(PyExc_TypeError, "Expected an evaluated object");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
const bool is_instance_collection = evaluated_object->type == OB_EMPTY &&
|
|
|
|
|
evaluated_object->instance_collection;
|
|
|
|
|
const bool valid_object_type = OB_TYPE_IS_GEOMETRY(evaluated_object->type) ||
|
|
|
|
|
is_instance_collection;
|
|
|
|
|
if (!valid_object_type) {
|
|
|
|
|
const char *ob_type_name = "<unknown>";
|
|
|
|
|
RNA_enum_name_from_value(rna_enum_object_type_items, evaluated_object->type, &ob_type_name);
|
|
|
|
|
PyErr_Format(PyExc_TypeError, "Expected a geometry object, not %.200s", ob_type_name);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (!DEG_object_geometry_is_evaluated(*evaluated_object)) {
|
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
|
"Object geometry is not yet evaluated, is the depsgraph evaluated?");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
Depsgraph *depsgraph = DEG_get_depsgraph_by_id(*evaluated_object_id);
|
|
|
|
|
if (!depsgraph) {
|
|
|
|
|
PyErr_SetString(PyExc_TypeError, "Object is not owned by a depsgraph");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
Scene *scene = DEG_get_input_scene(depsgraph);
|
|
|
|
|
|
|
|
|
|
GeometrySet geometry;
|
|
|
|
|
if (is_instance_collection) {
|
|
|
|
|
bke::Instances *instances = new bke::Instances();
|
|
|
|
|
instances->add_new_reference(bke::InstanceReference{*evaluated_object->instance_collection});
|
|
|
|
|
instances->add_instance(0, float4x4::identity());
|
|
|
|
|
geometry.replace_instances(instances);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bke::Instances instances = object_duplilist_legacy_instances(
|
|
|
|
|
*depsgraph, *scene, *evaluated_object);
|
|
|
|
|
geometry = bke::object_get_evaluated_geometry_set(*evaluated_object, false);
|
|
|
|
|
if (instances.instances_num() > 0) {
|
|
|
|
|
geometry.replace_instances(new bke::Instances(std::move(instances)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BPy_GeometrySet *self = python_object_from_geometry_set(std::move(geometry));
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyObject *BPy_GeometrySet_repr(BPy_GeometrySet *self)
|
|
|
|
|
{
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << self->geometry;
|
|
|
|
|
std::string str = ss.str();
|
2025-04-01 12:33:56 +11:00
|
|
|
return PyC_UnicodeFromStdStr(str);
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_get_instances_pointcloud_doc,
|
|
|
|
|
".. method:: instances_pointcloud()\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" Get a pointcloud that encodes information about the instances of the geometry.\n"
|
|
|
|
|
" The returned pointcloud should not be modified.\n"
|
|
|
|
|
" There is a point per instance and per-instance data is stored in point attributes.\n"
|
|
|
|
|
" The local transforms are stored in the ``instance_transform`` attribute.\n"
|
|
|
|
|
" The data instanced by each point is referenced by the ``.reference_index`` attribute,\n"
|
|
|
|
|
" indexing into the list returned by :func:`bpy.types.GeometrySet.instance_references`.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" :rtype: bpy.types.PointCloud\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_instances_pointcloud(BPy_GeometrySet *self)
|
|
|
|
|
{
|
|
|
|
|
using namespace blender;
|
|
|
|
|
const bke::Instances *instances = self->geometry.get_instances();
|
|
|
|
|
if (!instances) {
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
|
|
|
|
if (self->instances_pointcloud == nullptr) {
|
|
|
|
|
const int instances_num = instances->instances_num();
|
|
|
|
|
PointCloud *pointcloud = BKE_pointcloud_new_nomain(instances_num);
|
|
|
|
|
bke::gather_attributes(instances->attributes(),
|
|
|
|
|
bke::AttrDomain::Instance,
|
|
|
|
|
bke::AttrDomain::Point,
|
|
|
|
|
{},
|
|
|
|
|
IndexMask(instances_num),
|
|
|
|
|
pointcloud->attributes_for_write());
|
|
|
|
|
self->instances_pointcloud = pointcloud;
|
|
|
|
|
}
|
|
|
|
|
return pyrna_id_CreatePyObject(&self->instances_pointcloud->id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_get_instance_references_doc,
|
|
|
|
|
".. method:: instance_references()\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" This returns a list of geometries that is indexed by the ``.reference_index``\n"
|
|
|
|
|
" attribute of the pointcloud returned by \n"
|
|
|
|
|
" :func:`bpy.types.GeometrySet.instances_pointcloud`.\n"
|
|
|
|
|
" It may contain other geometry sets, objects, collections and None values.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" :rtype: list[None | bpy.types.Object | bpy.types.Collection | bpy.types.GeometrySet]\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_instance_references(BPy_GeometrySet *self)
|
|
|
|
|
{
|
|
|
|
|
using namespace blender;
|
|
|
|
|
const bke::Instances *instances = self->geometry.get_instances();
|
|
|
|
|
if (!instances) {
|
|
|
|
|
return PyList_New(0);
|
|
|
|
|
}
|
|
|
|
|
const Span<bke::InstanceReference> references = instances->references();
|
|
|
|
|
PyObject *py_references = PyList_New(references.size());
|
|
|
|
|
for (const int i : references.index_range()) {
|
|
|
|
|
const bke::InstanceReference &reference = references[i];
|
|
|
|
|
switch (reference.type()) {
|
|
|
|
|
case bke::InstanceReference::Type::None: {
|
|
|
|
|
PyList_SET_ITEM(py_references, i, Py_NewRef(Py_None));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case bke::InstanceReference::Type::Object: {
|
|
|
|
|
Object &object = reference.object();
|
|
|
|
|
PyList_SET_ITEM(py_references, i, pyrna_id_CreatePyObject(&object.id));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case bke::InstanceReference::Type::Collection: {
|
|
|
|
|
Collection &collection = reference.collection();
|
|
|
|
|
PyList_SET_ITEM(py_references, i, pyrna_id_CreatePyObject(&collection.id));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case bke::InstanceReference::Type::GeometrySet: {
|
|
|
|
|
const bke::GeometrySet &geometry_set = reference.geometry_set();
|
|
|
|
|
PyList_SET_ITEM(py_references, i, python_object_from_geometry_set(geometry_set));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return py_references;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_name_doc,
|
2025-04-08 12:14:10 +02:00
|
|
|
"The name of the geometry set. It can be used for debugging purposes and is not unique.\n"
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
"\n"
|
|
|
|
|
":type: str\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_name(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
2025-04-01 12:33:56 +11:00
|
|
|
return PyC_UnicodeFromStdStr(self->geometry.name);
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int BPy_GeometrySet_set_name(BPy_GeometrySet *self, PyObject *value, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
if (!PyUnicode_Check(value)) {
|
|
|
|
|
PyErr_SetString(PyExc_TypeError, "expected a string");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
const char *name = PyUnicode_AsUTF8(value);
|
2025-04-08 12:18:12 +02:00
|
|
|
self->geometry.name = name;
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_mesh_doc,
|
|
|
|
|
"The mesh data-block in the geometry set.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
":type: :class:`bpy.types.Mesh`\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_mesh(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
Mesh *base_mesh = self->geometry.get_mesh_for_write();
|
2025-04-08 09:50:10 +02:00
|
|
|
if (!base_mesh) {
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
Mesh *mesh = BKE_mesh_wrapper_ensure_subdivision(base_mesh);
|
|
|
|
|
return pyrna_id_CreatePyObject(reinterpret_cast<ID *>(mesh));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_mesh_base_doc,
|
|
|
|
|
"The mesh data-block in the geometry set without final subdivision.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
":type: :class:`bpy.types.Mesh`\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_mesh_base(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
Mesh *base_mesh = self->geometry.get_mesh_for_write();
|
|
|
|
|
return pyrna_id_CreatePyObject(reinterpret_cast<ID *>(base_mesh));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_pointcloud_doc,
|
|
|
|
|
"The point cloud data-block in the geometry set.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
":type: :class:`bpy.types.PointCloud`\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_pointcloud(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
return pyrna_id_CreatePyObject(
|
|
|
|
|
reinterpret_cast<ID *>(self->geometry.get_pointcloud_for_write()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_curves_doc,
|
|
|
|
|
"The curves data-block in the geometry set.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
":type: :class:`bpy.types.Curves`\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_curves(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
return pyrna_id_CreatePyObject(reinterpret_cast<ID *>(self->geometry.get_curves_for_write()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_volume_doc,
|
|
|
|
|
"The volume data-block in the geometry set.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
":type: :class:`bpy.types.Volume`\n");
|
|
|
|
|
static PyObject *BPy_GeometrySet_get_volume(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
return pyrna_id_CreatePyObject(reinterpret_cast<ID *>(self->geometry.get_volume_for_write()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_grease_pencil_doc,
|
|
|
|
|
"The Grease Pencil data-block in the geometry set.\n"
|
|
|
|
|
"\n"
|
2025-09-29 12:25:23 +02:00
|
|
|
":type: :class:`bpy.types.GreasePencil`\n");
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
static PyObject *BPy_GeometrySet_get_grease_pencil(BPy_GeometrySet *self, void * /*closure*/)
|
|
|
|
|
{
|
|
|
|
|
return pyrna_id_CreatePyObject(
|
|
|
|
|
reinterpret_cast<ID *>(self->geometry.get_grease_pencil_for_write()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyGetSetDef BPy_GeometrySet_getseters[] = {
|
|
|
|
|
{
|
|
|
|
|
"name",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_name),
|
|
|
|
|
reinterpret_cast<setter>(BPy_GeometrySet_set_name),
|
|
|
|
|
bpy_geometry_set_name_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"mesh",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_mesh),
|
|
|
|
|
nullptr,
|
|
|
|
|
bpy_geometry_set_mesh_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"mesh_base",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_mesh_base),
|
|
|
|
|
nullptr,
|
|
|
|
|
bpy_geometry_set_mesh_base_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"pointcloud",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_pointcloud),
|
|
|
|
|
nullptr,
|
|
|
|
|
bpy_geometry_set_pointcloud_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"curves",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_curves),
|
|
|
|
|
nullptr,
|
|
|
|
|
bpy_geometry_set_curves_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"volume",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_volume),
|
|
|
|
|
nullptr,
|
|
|
|
|
bpy_geometry_set_volume_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"grease_pencil",
|
|
|
|
|
reinterpret_cast<getter>(BPy_GeometrySet_get_grease_pencil),
|
|
|
|
|
nullptr,
|
|
|
|
|
bpy_geometry_set_grease_pencil_doc,
|
|
|
|
|
nullptr,
|
|
|
|
|
},
|
|
|
|
|
{nullptr},
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-01 12:06:03 +11:00
|
|
|
#ifdef __GNUC__
|
|
|
|
|
# ifdef __clang__
|
|
|
|
|
# pragma clang diagnostic push
|
|
|
|
|
# pragma clang diagnostic ignored "-Wcast-function-type"
|
|
|
|
|
# else
|
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
static PyMethodDef BPy_GeometrySet_methods[] = {
|
|
|
|
|
{"from_evaluated_object",
|
|
|
|
|
reinterpret_cast<PyCFunction>(BPy_GeometrySet_static_from_evaluated_object),
|
|
|
|
|
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
|
|
|
|
|
bpy_geometry_set_from_evaluated_object_doc},
|
|
|
|
|
{"instances_pointcloud",
|
|
|
|
|
reinterpret_cast<PyCFunction>(BPy_GeometrySet_get_instances_pointcloud),
|
|
|
|
|
METH_NOARGS,
|
|
|
|
|
bpy_geometry_set_get_instances_pointcloud_doc},
|
|
|
|
|
{"instance_references",
|
|
|
|
|
reinterpret_cast<PyCFunction>(BPy_GeometrySet_get_instance_references),
|
|
|
|
|
METH_NOARGS,
|
|
|
|
|
bpy_geometry_set_get_instance_references_doc},
|
|
|
|
|
{nullptr, nullptr, 0, nullptr},
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-01 12:06:03 +11:00
|
|
|
#ifdef __GNUC__
|
|
|
|
|
# ifdef __clang__
|
|
|
|
|
# pragma clang diagnostic pop
|
|
|
|
|
# else
|
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_geometry_set_doc,
|
|
|
|
|
"Stores potentially multiple geometry components of different types.\n"
|
|
|
|
|
"For example, it might contain a mesh, curves and nested instances.\n");
|
|
|
|
|
PyTypeObject bpy_geometry_set_Type = {
|
|
|
|
|
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
|
|
|
|
/*tp_name*/ "GeometrySet",
|
|
|
|
|
/*tp_basicsize*/ sizeof(BPy_GeometrySet),
|
|
|
|
|
/*tp_itemsize*/ 0,
|
|
|
|
|
/*tp_dealloc*/ reinterpret_cast<destructor>(BPy_GeometrySet_dealloc),
|
|
|
|
|
/*tp_vectorcall_offset*/ 0,
|
|
|
|
|
/*tp_getattr*/ nullptr,
|
|
|
|
|
/*tp_setattr*/ nullptr,
|
|
|
|
|
/*tp_as_async*/ nullptr,
|
|
|
|
|
/*tp_repr*/ reinterpret_cast<reprfunc>(BPy_GeometrySet_repr),
|
|
|
|
|
/*tp_as_number*/ nullptr,
|
|
|
|
|
/*tp_as_sequence*/ nullptr,
|
|
|
|
|
/*tp_as_mapping*/ nullptr,
|
|
|
|
|
/*tp_hash*/ nullptr,
|
|
|
|
|
/*tp_call*/ nullptr,
|
|
|
|
|
/*tp_str*/ nullptr,
|
|
|
|
|
/*tp_getattro*/ nullptr,
|
|
|
|
|
/*tp_setattro*/ nullptr,
|
|
|
|
|
/*tp_as_buffer*/ nullptr,
|
|
|
|
|
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
|
|
|
/*tp_doc*/ bpy_geometry_set_doc,
|
|
|
|
|
/*tp_traverse*/ nullptr,
|
|
|
|
|
/*tp_clear*/ nullptr,
|
|
|
|
|
/*tp_richcompare*/ nullptr,
|
|
|
|
|
/*tp_weaklistoffset*/ 0,
|
|
|
|
|
/*tp_iter*/ nullptr,
|
|
|
|
|
/*tp_iternext*/ nullptr,
|
|
|
|
|
/*tp_methods*/ BPy_GeometrySet_methods,
|
|
|
|
|
/*tp_members*/ nullptr,
|
|
|
|
|
/*tp_getset*/ BPy_GeometrySet_getseters,
|
|
|
|
|
/*tp_base*/ nullptr,
|
|
|
|
|
/*tp_dict*/ nullptr,
|
|
|
|
|
/*tp_descr_get*/ nullptr,
|
|
|
|
|
/*tp_descr_set*/ nullptr,
|
|
|
|
|
/*tp_dictoffset*/ 0,
|
|
|
|
|
/*tp_init*/ nullptr,
|
|
|
|
|
/*tp_alloc*/ nullptr,
|
2025-04-08 09:54:39 +02:00
|
|
|
/*tp_new*/ BPy_GeometrySet_new,
|
Python: Geometry: create GeometrySet wrapper for Python
In Geometry Nodes a geometry is represented by a `GeometrySet`. This is a
container that can contain one geometry of each of the supported types (mesh,
curves, volume, grease pencil, pointcloud, instances). It's possible for a
`GeometrySet` to contain e.g. a mesh and a point cloud.
This patch creates a Python wrapper for the built-in `GeometrySet`. For now,
it's main purpose is to consume the complete evaluated geometry of an object
without having to go through complex hoops via `depsgraph.object_instances`. It
also also allows retrieving instances that have been created with legacy
instancing systems such as dupli-verts or particles.
In the future, the `GeometrySet` API could also be used for more kinds of
geometry processing from Python, similar to how we use `GeometrySet` internally
as generic geometry storage.
Since we can't really have constness guarantees in Python currently, it's
enforced that the `GeometrySet` wrapper always has its own copy of each geometry
type (so e.g. it does not share a `Mesh` data-block pointer with any other place
in Blender). Without the copy, changes to the mesh in the geometry set would
also affect the evaluated geometry that Blender sees. The copy has a small cost,
but typically the overhead should be low, because attributes and other run-time
data can still be shared. This should be entirely thread-safe, assuming that no
code modifies implicitly shared data, which is forbidden. For historic reasons
there are still cases like #132423 where this assumption does not hold in all
cases. Those cases should be fixed. To my knowledge, this patch does not
introduce any new such issues or makes existing issues worse.
Pull Request: https://projects.blender.org/blender/blender/pulls/135318
2025-03-28 22:40:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PyObject *BPyInit_geometry_set_type()
|
|
|
|
|
{
|
|
|
|
|
if (PyType_Ready(&bpy_geometry_set_Type) < 0) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return reinterpret_cast<PyObject *>(&bpy_geometry_set_Type);
|
|
|
|
|
}
|