Geometry Nodes: Object info node optional instance output
The object info node output an instance as a performance optimization. Before that optimization was (almost) invisible to the user, but now that we aren't automatically realizing instances, it isn't intuitive for a single object to become an instance. I refactored the transform node so its ability to translate/transform an entire geometry set was more usable from elsewhere and exposed the function to get a geometry set from an object. Differential Revision: https://developer.blender.org/D12833
This commit is contained in:
parent
76f386a37a
commit
19bab2a536
Notes:
blender-bot
2023-02-13 22:38:46 +01:00
Referenced by issue #91890, Make outputing an instance in the object info node optional
|
@ -20,6 +20,8 @@
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
GeometrySet object_get_evaluated_geometry_set(const Object &object);
|
||||
|
||||
/**
|
||||
* Used to keep track of a group of instances using the same geometry data.
|
||||
*/
|
||||
|
|
|
@ -56,7 +56,7 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS
|
|||
/**
|
||||
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
|
||||
*/
|
||||
static GeometrySet object_get_geometry_set_for_read(const Object &object)
|
||||
GeometrySet object_get_evaluated_geometry_set(const Object &object)
|
||||
{
|
||||
if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) {
|
||||
GeometrySet geometry_set;
|
||||
|
@ -100,7 +100,7 @@ static void geometry_set_collect_recursive_object(const Object &object,
|
|||
const float4x4 &transform,
|
||||
Vector<GeometryInstanceGroup> &r_sets)
|
||||
{
|
||||
GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
|
||||
GeometrySet instance_geometry_set = object_get_evaluated_geometry_set(object);
|
||||
geometry_set_collect_recursive(instance_geometry_set, transform, r_sets);
|
||||
|
||||
if (object.type == OB_EMPTY) {
|
||||
|
@ -628,14 +628,14 @@ void InstancesComponent::foreach_referenced_geometry(
|
|||
switch (reference.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
const Object &object = reference.object();
|
||||
const GeometrySet object_geometry_set = object_get_geometry_set_for_read(object);
|
||||
const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object);
|
||||
callback(object_geometry_set);
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::Collection: {
|
||||
Collection &collection = reference.collection();
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) {
|
||||
const GeometrySet object_geometry_set = object_get_geometry_set_for_read(*object);
|
||||
const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(*object);
|
||||
callback(object_geometry_set);
|
||||
}
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
|
@ -676,7 +676,7 @@ void InstancesComponent::ensure_geometry_instances()
|
|||
/* Create a new reference that contains the geometry set of the object. We may want to
|
||||
* treat e.g. lamps and similar object types separately here. */
|
||||
const Object &object = reference.object();
|
||||
GeometrySet object_geometry_set = object_get_geometry_set_for_read(object);
|
||||
GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object);
|
||||
if (object_geometry_set.has_instances()) {
|
||||
InstancesComponent &component =
|
||||
object_geometry_set.get_component_for_write<InstancesComponent>();
|
||||
|
|
|
@ -50,11 +50,15 @@ void update_attribute_input_socket_availabilities(bNode &node,
|
|||
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
|
||||
const AttributeDomain domain);
|
||||
|
||||
void transform_mesh(Mesh *mesh,
|
||||
void transform_mesh(Mesh &mesh,
|
||||
const float3 translation,
|
||||
const float3 rotation,
|
||||
const float3 scale);
|
||||
|
||||
void transform_geometry_set(GeometrySet &geometry,
|
||||
const float4x4 &transform,
|
||||
const Depsgraph &depsgraph);
|
||||
|
||||
Mesh *create_line_mesh(const float3 start, const float3 delta, const int count);
|
||||
|
||||
Mesh *create_grid_mesh(const int verts_x,
|
||||
|
|
|
@ -153,7 +153,7 @@ static void geo_node_bounding_box_exec(GeoNodeExecParams params)
|
|||
const float3 scale = max - min;
|
||||
const float3 center = min + scale / 2.0f;
|
||||
Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2);
|
||||
transform_mesh(mesh, center, float3(0), float3(1));
|
||||
transform_mesh(*mesh, center, float3(0), float3(1));
|
||||
params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh));
|
||||
params.set_output("Min", min);
|
||||
params.set_output("Max", max);
|
||||
|
|
|
@ -456,12 +456,12 @@ static Mesh *create_cube_mesh(const float3 size,
|
|||
}
|
||||
if (verts_y == 1) { /* XZ plane. */
|
||||
Mesh *mesh = create_grid_mesh(verts_x, verts_z, size.x, size.z);
|
||||
transform_mesh(mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1));
|
||||
transform_mesh(*mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1));
|
||||
return mesh;
|
||||
}
|
||||
/* YZ plane. */
|
||||
Mesh *mesh = create_grid_mesh(verts_z, verts_y, size.z, size.y);
|
||||
transform_mesh(mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1));
|
||||
transform_mesh(*mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1));
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ namespace blender::nodes {
|
|||
static void geo_node_object_info_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Object>("Object").hide_label();
|
||||
b.add_input<decl::Bool>("As Instance")
|
||||
.description(
|
||||
"Output the entire object as single instance. "
|
||||
"This allows instancing non-geometry object types");
|
||||
b.add_output<decl::Vector>("Location");
|
||||
b.add_output<decl::Vector>("Rotation");
|
||||
b.add_output<decl::Vector>("Scale");
|
||||
|
@ -54,12 +58,11 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
|
|||
const Object *self_object = params.self_object();
|
||||
|
||||
if (object != nullptr) {
|
||||
float transform[4][4];
|
||||
mul_m4_m4m4(transform, self_object->imat, object->obmat);
|
||||
const float4x4 transform = float4x4(self_object->imat) * float4x4(object->obmat);
|
||||
|
||||
float quaternion[4];
|
||||
if (transform_space_relative) {
|
||||
mat4_decompose(location, quaternion, scale, transform);
|
||||
mat4_decompose(location, quaternion, scale, transform.values);
|
||||
}
|
||||
else {
|
||||
mat4_decompose(location, quaternion, scale, object->obmat);
|
||||
|
@ -67,16 +70,23 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
|
|||
quat_to_eul(rotation, quaternion);
|
||||
|
||||
if (object != self_object) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
const int handle = instances.add_reference(*object);
|
||||
|
||||
if (transform_space_relative) {
|
||||
instances.add_instance(handle, transform);
|
||||
if (params.get_input<bool>("As Instance")) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
const int handle = instances.add_reference(*object);
|
||||
if (transform_space_relative) {
|
||||
instances.add_instance(handle, transform);
|
||||
}
|
||||
else {
|
||||
float unit_transform[4][4];
|
||||
unit_m4(unit_transform);
|
||||
instances.add_instance(handle, unit_transform);
|
||||
}
|
||||
}
|
||||
else {
|
||||
float unit_transform[4][4];
|
||||
unit_m4(unit_transform);
|
||||
instances.add_instance(handle, unit_transform);
|
||||
geometry_set = bke::object_get_evaluated_geometry_set(*object);
|
||||
if (transform_space_relative) {
|
||||
transform_geometry_set(geometry_set, transform, *params.depsgraph());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_pointcloud.h"
|
||||
#include "BKE_spline.hh"
|
||||
#include "BKE_volume.h"
|
||||
|
||||
|
@ -55,112 +56,142 @@ static bool use_translate(const float3 rotation, const float3 scale)
|
|||
return true;
|
||||
}
|
||||
|
||||
void transform_mesh(Mesh *mesh,
|
||||
static void translate_mesh(Mesh &mesh, const float3 translation)
|
||||
{
|
||||
if (!translation.is_zero()) {
|
||||
BKE_mesh_translate(&mesh, translation, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_mesh(Mesh &mesh, const float4x4 &transform)
|
||||
{
|
||||
BKE_mesh_transform(&mesh, transform.values, false);
|
||||
BKE_mesh_normals_tag_dirty(&mesh);
|
||||
}
|
||||
|
||||
void transform_mesh(Mesh &mesh,
|
||||
const float3 translation,
|
||||
const float3 rotation,
|
||||
const float3 scale)
|
||||
{
|
||||
/* Use only translation if rotation and scale are zero. */
|
||||
if (use_translate(rotation, scale)) {
|
||||
if (!translation.is_zero()) {
|
||||
BKE_mesh_translate(mesh, translation, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
|
||||
BKE_mesh_transform(mesh, matrix.values, false);
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
}
|
||||
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
|
||||
transform_mesh(mesh, matrix);
|
||||
}
|
||||
|
||||
static void transform_pointcloud(PointCloud *pointcloud,
|
||||
const float3 translation,
|
||||
const float3 rotation,
|
||||
const float3 scale)
|
||||
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
|
||||
{
|
||||
/* Use only translation if rotation and scale don't apply. */
|
||||
if (use_translate(rotation, scale)) {
|
||||
for (const int i : IndexRange(pointcloud->totpoint)) {
|
||||
add_v3_v3(pointcloud->co[i], translation);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
|
||||
for (const int i : IndexRange(pointcloud->totpoint)) {
|
||||
float3 &co = *(float3 *)pointcloud->co[i];
|
||||
co = matrix * co;
|
||||
}
|
||||
CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
|
||||
BKE_pointcloud_update_customdata_pointers(&pointcloud);
|
||||
for (const int i : IndexRange(pointcloud.totpoint)) {
|
||||
add_v3_v3(pointcloud.co[i], translation);
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_instances(InstancesComponent &instances,
|
||||
const float3 translation,
|
||||
const float3 rotation,
|
||||
const float3 scale)
|
||||
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
|
||||
{
|
||||
CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
|
||||
BKE_pointcloud_update_customdata_pointers(&pointcloud);
|
||||
for (const int i : IndexRange(pointcloud.totpoint)) {
|
||||
float3 &co = *(float3 *)pointcloud.co[i];
|
||||
co = transform * co;
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_instances(InstancesComponent &instances, const float3 translation)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.instance_transforms();
|
||||
|
||||
/* Use only translation if rotation and scale don't apply. */
|
||||
if (use_translate(rotation, scale)) {
|
||||
for (float4x4 &transform : transforms) {
|
||||
add_v3_v3(transform.ptr()[3], translation);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
|
||||
for (float4x4 &transform : transforms) {
|
||||
transform = matrix * transform;
|
||||
}
|
||||
for (float4x4 &transform : transforms) {
|
||||
add_v3_v3(transform.ptr()[3], translation);
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_volume(Volume *volume,
|
||||
const float3 translation,
|
||||
const float3 rotation,
|
||||
const float3 scale,
|
||||
GeoNodeExecParams ¶ms)
|
||||
static void transform_instances(InstancesComponent &instances, const float4x4 &transform)
|
||||
{
|
||||
MutableSpan<float4x4> instance_transforms = instances.instance_transforms();
|
||||
for (float4x4 &instance_transform : instance_transforms) {
|
||||
instance_transform = transform * instance_transform;
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph)
|
||||
{
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Scaling an axis to zero is not supported for volumes. */
|
||||
const float3 translation = transform.translation();
|
||||
const float3 rotation = transform.to_euler();
|
||||
const float3 scale = transform.scale();
|
||||
const float3 limited_scale = {
|
||||
(scale.x == 0.0f) ? FLT_EPSILON : scale.x,
|
||||
(scale.y == 0.0f) ? FLT_EPSILON : scale.y,
|
||||
(scale.z == 0.0f) ? FLT_EPSILON : scale.z,
|
||||
};
|
||||
const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale(
|
||||
translation, rotation, limited_scale);
|
||||
|
||||
const Main *bmain = DEG_get_bmain(params.depsgraph());
|
||||
BKE_volume_load(volume, bmain);
|
||||
|
||||
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale);
|
||||
const Main *bmain = DEG_get_bmain(&depsgraph);
|
||||
BKE_volume_load(&volume, bmain);
|
||||
|
||||
openvdb::Mat4s vdb_matrix;
|
||||
memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
|
||||
memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4]));
|
||||
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
|
||||
|
||||
const int num_grids = BKE_volume_num_grids(volume);
|
||||
const int num_grids = BKE_volume_num_grids(&volume);
|
||||
for (const int i : IndexRange(num_grids)) {
|
||||
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i);
|
||||
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
|
||||
|
||||
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
|
||||
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false);
|
||||
openvdb::math::Transform &grid_transform = grid->transform();
|
||||
grid_transform.postMult(vdb_matrix_d);
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(volume, translation, rotation, scale, params);
|
||||
UNUSED_VARS(volume, transform, depsgraph);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void transform_curve(CurveEval &curve,
|
||||
const float3 translation,
|
||||
const float3 rotation,
|
||||
const float3 scale)
|
||||
static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph)
|
||||
{
|
||||
if (use_translate(rotation, scale)) {
|
||||
curve.translate(translation);
|
||||
transform_volume(volume, float4x4::from_location(translation), depsgraph);
|
||||
}
|
||||
|
||||
void transform_geometry_set(GeometrySet &geometry,
|
||||
const float4x4 &transform,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
if (CurveEval *curve = geometry.get_curve_for_write()) {
|
||||
curve->transform(transform);
|
||||
}
|
||||
else {
|
||||
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
|
||||
curve.transform(matrix);
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
transform_mesh(*mesh, transform);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
transform_pointcloud(*pointcloud, transform);
|
||||
}
|
||||
if (Volume *volume = geometry.get_volume_for_write()) {
|
||||
transform_volume(*volume, transform, depsgraph);
|
||||
}
|
||||
if (geometry.has_instances()) {
|
||||
transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform);
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_geometry_set(GeometrySet &geometry,
|
||||
const float3 translation,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
if (CurveEval *curve = geometry.get_curve_for_write()) {
|
||||
curve->translate(translation);
|
||||
}
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
translate_mesh(*mesh, translation);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
translate_pointcloud(*pointcloud, translation);
|
||||
}
|
||||
if (Volume *volume = geometry.get_volume_for_write()) {
|
||||
translate_volume(*volume, translation, depsgraph);
|
||||
}
|
||||
if (geometry.has_instances()) {
|
||||
translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,25 +202,14 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
|
|||
const float3 rotation = params.extract_input<float3>("Rotation");
|
||||
const float3 scale = params.extract_input<float3>("Scale");
|
||||
|
||||
if (geometry_set.has_mesh()) {
|
||||
Mesh *mesh = geometry_set.get_mesh_for_write();
|
||||
transform_mesh(mesh, translation, rotation, scale);
|
||||
/* Use only translation if rotation and scale don't apply. */
|
||||
if (use_translate(rotation, scale)) {
|
||||
translate_geometry_set(geometry_set, translation, *params.depsgraph());
|
||||
}
|
||||
if (geometry_set.has_pointcloud()) {
|
||||
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
|
||||
transform_pointcloud(pointcloud, translation, rotation, scale);
|
||||
}
|
||||
if (geometry_set.has_instances()) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
transform_instances(instances, translation, rotation, scale);
|
||||
}
|
||||
if (geometry_set.has_volume()) {
|
||||
Volume *volume = geometry_set.get_volume_for_write();
|
||||
transform_volume(volume, translation, rotation, scale, params);
|
||||
}
|
||||
if (geometry_set.has_curve()) {
|
||||
CurveEval *curve = geometry_set.get_curve_for_write();
|
||||
transform_curve(*curve, translation, rotation, scale);
|
||||
else {
|
||||
transform_geometry_set(geometry_set,
|
||||
float4x4::from_loc_eul_scale(translation, rotation, scale),
|
||||
*params.depsgraph());
|
||||
}
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
|
|
Loading…
Reference in New Issue