Geometry Nodes: extract geometry transform functions to separate file
This commit is contained in:
@@ -41,6 +41,7 @@ set(SRC
|
||||
intern/set_curve_type.cc
|
||||
intern/smooth_curves.cc
|
||||
intern/subdivide_curves.cc
|
||||
intern/transform.cc
|
||||
intern/trim_curves.cc
|
||||
intern/uv_pack.cc
|
||||
intern/uv_parametrizer.cc
|
||||
@@ -71,6 +72,7 @@ set(SRC
|
||||
GEO_set_curve_type.hh
|
||||
GEO_smooth_curves.hh
|
||||
GEO_subdivide_curves.hh
|
||||
GEO_transform.hh
|
||||
GEO_trim_curves.hh
|
||||
GEO_uv_pack.hh
|
||||
GEO_uv_parametrizer.hh
|
||||
|
||||
30
source/blender/geometry/GEO_transform.hh
Normal file
30
source/blender/geometry/GEO_transform.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_math_quaternion_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
struct Mesh;
|
||||
namespace blender::bke {
|
||||
struct GeometrySet;
|
||||
}
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
void transform_mesh(Mesh &mesh, float3 translation, math::Quaternion rotation, float3 scale);
|
||||
|
||||
struct TransformGeometryErrors {
|
||||
bool volume_too_small = false;
|
||||
};
|
||||
|
||||
std::optional<TransformGeometryErrors> transform_geometry(bke::GeometrySet &geometry,
|
||||
const float4x4 &transform);
|
||||
|
||||
void translate_geometry(bke::GeometrySet &geometry, const float3 translation);
|
||||
|
||||
} // namespace blender::geometry
|
||||
273
source/blender/geometry/intern/transform.cc
Normal file
273
source/blender/geometry/intern/transform.cc
Normal file
@@ -0,0 +1,273 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/openvdb.h>
|
||||
#endif
|
||||
|
||||
#include "GEO_transform.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_instances.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
#include "BKE_volume.hh"
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
|
||||
{
|
||||
threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (float3 &position : positions.slice(range)) {
|
||||
position += translation;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
|
||||
{
|
||||
threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float3 &position : positions.slice(range)) {
|
||||
position = math::transform_point(matrix, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_mesh(Mesh &mesh, const float4x4 &transform)
|
||||
{
|
||||
transform_positions(mesh.vert_positions_for_write(), transform);
|
||||
mesh.tag_positions_changed();
|
||||
}
|
||||
|
||||
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
|
||||
{
|
||||
if (math::is_zero(translation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<Bounds<float3>> bounds;
|
||||
if (pointcloud.runtime->bounds_cache.is_cached()) {
|
||||
bounds = pointcloud.runtime->bounds_cache.data();
|
||||
}
|
||||
|
||||
bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
|
||||
bke::SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
|
||||
"position", bke::AttrDomain::Point);
|
||||
translate_positions(position.span, translation);
|
||||
position.finish();
|
||||
|
||||
if (bounds) {
|
||||
bounds->min += translation;
|
||||
bounds->max += translation;
|
||||
pointcloud.runtime->bounds_cache.ensure([&](Bounds<float3> &r_data) { r_data = *bounds; });
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
|
||||
{
|
||||
bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
|
||||
bke::SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
|
||||
"position", bke::AttrDomain::Point);
|
||||
transform_positions(position.span, transform);
|
||||
position.finish();
|
||||
}
|
||||
|
||||
static void translate_greasepencil(GreasePencil &grease_pencil, const float3 translation)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
for (const int layer_index : grease_pencil.layers().index_range()) {
|
||||
if (Drawing *drawing = get_eval_grease_pencil_layer_drawing_for_write(grease_pencil,
|
||||
layer_index))
|
||||
{
|
||||
drawing->strokes_for_write().translate(translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &transform)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
for (const int layer_index : grease_pencil.layers().index_range()) {
|
||||
if (Drawing *drawing = get_eval_grease_pencil_layer_drawing_for_write(grease_pencil,
|
||||
layer_index))
|
||||
{
|
||||
drawing->strokes_for_write().transform(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_instances(bke::Instances &instances, const float3 translation)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float4x4 &instance_transform : transforms.slice(range)) {
|
||||
add_v3_v3(instance_transform.ptr()[3], translation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_instances(bke::Instances &instances, const float4x4 &transform)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float4x4 &instance_transform : transforms.slice(range)) {
|
||||
instance_transform = transform * instance_transform;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static bool transform_volume(Volume &volume, const float4x4 &transform)
|
||||
{
|
||||
bool found_too_small_scale = false;
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::Mat4s vdb_matrix;
|
||||
memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
|
||||
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
|
||||
|
||||
const int grids_num = BKE_volume_num_grids(&volume);
|
||||
for (const int i : IndexRange(grids_num)) {
|
||||
bke::VolumeGridData *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
|
||||
|
||||
float4x4 grid_matrix = bke::volume_grid::get_transform_matrix(*volume_grid);
|
||||
grid_matrix = transform * grid_matrix;
|
||||
const float determinant = math::determinant(grid_matrix);
|
||||
if (!BKE_volume_grid_determinant_valid(determinant)) {
|
||||
found_too_small_scale = true;
|
||||
/* Clear the tree because it is too small. */
|
||||
bke::volume_grid::clear_tree(*volume_grid);
|
||||
if (determinant == 0) {
|
||||
/* Reset rotation and scale. */
|
||||
grid_matrix.x_axis() = float3(1, 0, 0);
|
||||
grid_matrix.y_axis() = float3(0, 1, 0);
|
||||
grid_matrix.z_axis() = float3(0, 0, 1);
|
||||
}
|
||||
else {
|
||||
/* Keep rotation but reset scale. */
|
||||
grid_matrix.x_axis() = math::normalize(grid_matrix.x_axis());
|
||||
grid_matrix.y_axis() = math::normalize(grid_matrix.y_axis());
|
||||
grid_matrix.z_axis() = math::normalize(grid_matrix.z_axis());
|
||||
}
|
||||
}
|
||||
bke::volume_grid::set_transform_matrix(*volume_grid, grid_matrix);
|
||||
}
|
||||
|
||||
#else
|
||||
UNUSED_VARS(volume, transform);
|
||||
#endif
|
||||
return found_too_small_scale;
|
||||
}
|
||||
|
||||
static void translate_volume(Volume &volume, const float3 translation)
|
||||
{
|
||||
transform_volume(volume, math::from_location<float4x4>(translation));
|
||||
}
|
||||
|
||||
static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
|
||||
{
|
||||
if (edit_hints.positions.has_value()) {
|
||||
transform_positions(*edit_hints.positions, transform);
|
||||
}
|
||||
float3x3 deform_mat;
|
||||
copy_m3_m4(deform_mat.ptr(), transform.ptr());
|
||||
if (edit_hints.deform_mats.has_value()) {
|
||||
MutableSpan<float3x3> deform_mats = *edit_hints.deform_mats;
|
||||
threading::parallel_for(deform_mats.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
deform_mats[i] = deform_mat * deform_mats[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
|
||||
{
|
||||
if (edit_hints.positions.has_value()) {
|
||||
translate_positions(*edit_hints.positions, translation);
|
||||
}
|
||||
}
|
||||
|
||||
void translate_geometry(bke::GeometrySet &geometry, const float3 translation)
|
||||
{
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
curves->geometry.wrap().translate(translation);
|
||||
}
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
BKE_mesh_translate(mesh, translation, false);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
translate_pointcloud(*pointcloud, translation);
|
||||
}
|
||||
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
|
||||
translate_greasepencil(*grease_pencil, translation);
|
||||
}
|
||||
if (Volume *volume = geometry.get_volume_for_write()) {
|
||||
translate_volume(*volume, translation);
|
||||
}
|
||||
if (bke::Instances *instances = geometry.get_instances_for_write()) {
|
||||
translate_instances(*instances, translation);
|
||||
}
|
||||
if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
|
||||
translate_curve_edit_hints(*curve_edit_hints, translation);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TransformGeometryErrors> transform_geometry(bke::GeometrySet &geometry,
|
||||
const float4x4 &transform)
|
||||
{
|
||||
TransformGeometryErrors errors;
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
curves->geometry.wrap().transform(transform);
|
||||
}
|
||||
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 (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
|
||||
transform_greasepencil(*grease_pencil, transform);
|
||||
}
|
||||
if (Volume *volume = geometry.get_volume_for_write()) {
|
||||
errors.volume_too_small = transform_volume(*volume, transform);
|
||||
}
|
||||
if (bke::Instances *instances = geometry.get_instances_for_write()) {
|
||||
transform_instances(*instances, transform);
|
||||
}
|
||||
if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
|
||||
transform_curve_edit_hints(*curve_edit_hints, transform);
|
||||
}
|
||||
|
||||
if (errors.volume_too_small) {
|
||||
return errors;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void transform_mesh(Mesh &mesh,
|
||||
const float3 translation,
|
||||
const math::Quaternion rotation,
|
||||
const float3 scale)
|
||||
{
|
||||
const float4x4 matrix = math::from_loc_rot_scale<float4x4>(translation, rotation, scale);
|
||||
transform_mesh(mesh, matrix);
|
||||
}
|
||||
|
||||
} // namespace blender::geometry
|
||||
@@ -33,13 +33,6 @@ namespace blender::nodes {
|
||||
bool check_tool_context_and_error(GeoNodeExecParams ¶ms);
|
||||
void search_link_ops_for_tool_node(GatherLinkSearchOpParams ¶ms);
|
||||
|
||||
void transform_mesh(Mesh &mesh, float3 translation, math::Quaternion rotation, float3 scale);
|
||||
|
||||
void transform_geometry_set(GeoNodeExecParams ¶ms,
|
||||
GeometrySet &geometry,
|
||||
const float4x4 &transform,
|
||||
const Depsgraph &depsgraph);
|
||||
|
||||
/**
|
||||
* Returns the parts of the geometry that are on the selection for the given domain. If the domain
|
||||
* is not applicable for the component, e.g. face domain for point cloud, nothing happens to that
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GEO_mesh_primitive_cuboid.hh"
|
||||
#include "GEO_transform.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
@@ -54,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const float3 scale = sub_bounds->max - sub_bounds->min;
|
||||
const float3 center = sub_bounds->min + scale / 2.0f;
|
||||
Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
|
||||
transform_mesh(*mesh, center, math::Quaternion::identity(), float3(1));
|
||||
geometry::transform_mesh(*mesh, center, math::Quaternion::identity(), float3(1));
|
||||
sub_geometry.replace_mesh(mesh);
|
||||
sub_geometry.keep_only_during_modify({GeometryComponent::Type::Mesh});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "GEO_mesh_primitive_cuboid.hh"
|
||||
#include "GEO_mesh_primitive_grid.hh"
|
||||
#include "GEO_mesh_primitive_line.hh"
|
||||
#include "GEO_transform.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
@@ -75,13 +76,13 @@ static Mesh *create_cube_mesh(const float3 size,
|
||||
}
|
||||
if (verts_y == 1) { /* XZ plane. */
|
||||
Mesh *mesh = geometry::create_grid_mesh(verts_x, verts_z, size.x, size.z, uv_map_id);
|
||||
transform_mesh(
|
||||
geometry::transform_mesh(
|
||||
*mesh, float3(0), math::to_quaternion(math::EulerXYZ(M_PI_2, 0.0f, 0.0f)), float3(1));
|
||||
return mesh;
|
||||
}
|
||||
/* YZ plane. */
|
||||
Mesh *mesh = geometry::create_grid_mesh(verts_z, verts_y, size.z, size.y, uv_map_id);
|
||||
transform_mesh(
|
||||
geometry::transform_mesh(
|
||||
*mesh, float3(0), math::to_quaternion(math::EulerXYZ(0.0f, M_PI_2, 0.0f)), float3(1));
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "GEO_transform.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_object_info_cc {
|
||||
@@ -90,7 +92,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
else {
|
||||
geometry_set = bke::object_get_evaluated_geometry_set(*object);
|
||||
if (transform_space_relative) {
|
||||
transform_geometry_set(params, geometry_set, transform, *params.depsgraph());
|
||||
geometry::transform_geometry(geometry_set, transform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/openvdb.h>
|
||||
#endif
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_rotation.hh"
|
||||
@@ -24,9 +20,20 @@
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "GEO_transform.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
namespace blender::nodes::node_geo_transform_geometry_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>("Geometry");
|
||||
b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION);
|
||||
b.add_input<decl::Rotation>("Rotation");
|
||||
b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ);
|
||||
b.add_output<decl::Geometry>("Geometry").propagate_all();
|
||||
}
|
||||
|
||||
static bool use_translate(const math::Quaternion &rotation, const float3 scale)
|
||||
{
|
||||
@@ -41,269 +48,6 @@ static bool use_translate(const math::Quaternion &rotation, const float3 scale)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
|
||||
{
|
||||
threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (float3 &position : positions.slice(range)) {
|
||||
position += translation;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
|
||||
{
|
||||
threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float3 &position : positions.slice(range)) {
|
||||
position = math::transform_point(matrix, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_mesh(Mesh &mesh, const float4x4 &transform)
|
||||
{
|
||||
transform_positions(mesh.vert_positions_for_write(), transform);
|
||||
mesh.tag_positions_changed();
|
||||
}
|
||||
|
||||
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
|
||||
{
|
||||
if (math::is_zero(translation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<Bounds<float3>> bounds;
|
||||
if (pointcloud.runtime->bounds_cache.is_cached()) {
|
||||
bounds = pointcloud.runtime->bounds_cache.data();
|
||||
}
|
||||
|
||||
MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
|
||||
SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
|
||||
"position", AttrDomain::Point);
|
||||
translate_positions(position.span, translation);
|
||||
position.finish();
|
||||
|
||||
if (bounds) {
|
||||
bounds->min += translation;
|
||||
bounds->max += translation;
|
||||
pointcloud.runtime->bounds_cache.ensure([&](Bounds<float3> &r_data) { r_data = *bounds; });
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
|
||||
{
|
||||
MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
|
||||
SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
|
||||
"position", AttrDomain::Point);
|
||||
transform_positions(position.span, transform);
|
||||
position.finish();
|
||||
}
|
||||
|
||||
static void translate_greasepencil(GreasePencil &grease_pencil, const float3 translation)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
for (const int layer_index : grease_pencil.layers().index_range()) {
|
||||
if (Drawing *drawing = get_eval_grease_pencil_layer_drawing_for_write(grease_pencil,
|
||||
layer_index))
|
||||
{
|
||||
drawing->strokes_for_write().translate(translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &transform)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
for (const int layer_index : grease_pencil.layers().index_range()) {
|
||||
if (Drawing *drawing = get_eval_grease_pencil_layer_drawing_for_write(grease_pencil,
|
||||
layer_index))
|
||||
{
|
||||
drawing->strokes_for_write().transform(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_instances(bke::Instances &instances, const float3 translation)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float4x4 &instance_transform : transforms.slice(range)) {
|
||||
add_v3_v3(instance_transform.ptr()[3], translation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_instances(bke::Instances &instances, const float4x4 &transform)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float4x4 &instance_transform : transforms.slice(range)) {
|
||||
instance_transform = transform * instance_transform;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void transform_volume(GeoNodeExecParams ¶ms,
|
||||
Volume &volume,
|
||||
const float4x4 &transform,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
#ifdef WITH_OPENVDB
|
||||
const Main *bmain = DEG_get_bmain(&depsgraph);
|
||||
BKE_volume_load(&volume, bmain);
|
||||
|
||||
openvdb::Mat4s vdb_matrix;
|
||||
memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
|
||||
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
|
||||
|
||||
bool found_too_small_scale = false;
|
||||
const int grids_num = BKE_volume_num_grids(&volume);
|
||||
for (const int i : IndexRange(grids_num)) {
|
||||
bke::VolumeGridData *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
|
||||
|
||||
float4x4 grid_matrix = bke::volume_grid::get_transform_matrix(*volume_grid);
|
||||
grid_matrix = transform * grid_matrix;
|
||||
const float determinant = math::determinant(grid_matrix);
|
||||
if (!BKE_volume_grid_determinant_valid(determinant)) {
|
||||
found_too_small_scale = true;
|
||||
/* Clear the tree because it is too small. */
|
||||
bke::volume_grid::clear_tree(*volume_grid);
|
||||
if (determinant == 0) {
|
||||
/* Reset rotation and scale. */
|
||||
grid_matrix.x_axis() = float3(1, 0, 0);
|
||||
grid_matrix.y_axis() = float3(0, 1, 0);
|
||||
grid_matrix.z_axis() = float3(0, 0, 1);
|
||||
}
|
||||
else {
|
||||
/* Keep rotation but reset scale. */
|
||||
grid_matrix.x_axis() = math::normalize(grid_matrix.x_axis());
|
||||
grid_matrix.y_axis() = math::normalize(grid_matrix.y_axis());
|
||||
grid_matrix.z_axis() = math::normalize(grid_matrix.z_axis());
|
||||
}
|
||||
}
|
||||
bke::volume_grid::set_transform_matrix(*volume_grid, grid_matrix);
|
||||
}
|
||||
if (found_too_small_scale) {
|
||||
params.error_message_add(NodeWarningType::Warning,
|
||||
TIP_("Volume scale is lower than permitted by OpenVDB"));
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(params, volume, transform, depsgraph);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void translate_volume(GeoNodeExecParams ¶ms,
|
||||
Volume &volume,
|
||||
const float3 translation,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
transform_volume(params, volume, math::from_location<float4x4>(translation), depsgraph);
|
||||
}
|
||||
|
||||
static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
|
||||
{
|
||||
if (edit_hints.positions.has_value()) {
|
||||
transform_positions(*edit_hints.positions, transform);
|
||||
}
|
||||
float3x3 deform_mat;
|
||||
copy_m3_m4(deform_mat.ptr(), transform.ptr());
|
||||
if (edit_hints.deform_mats.has_value()) {
|
||||
MutableSpan<float3x3> deform_mats = *edit_hints.deform_mats;
|
||||
threading::parallel_for(deform_mats.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
deform_mats[i] = deform_mat * deform_mats[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
|
||||
{
|
||||
if (edit_hints.positions.has_value()) {
|
||||
translate_positions(*edit_hints.positions, translation);
|
||||
}
|
||||
}
|
||||
|
||||
static void translate_geometry_set(GeoNodeExecParams ¶ms,
|
||||
GeometrySet &geometry,
|
||||
const float3 translation,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
curves->geometry.wrap().translate(translation);
|
||||
}
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
BKE_mesh_translate(mesh, translation, false);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
translate_pointcloud(*pointcloud, translation);
|
||||
}
|
||||
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
|
||||
translate_greasepencil(*grease_pencil, translation);
|
||||
}
|
||||
if (Volume *volume = geometry.get_volume_for_write()) {
|
||||
translate_volume(params, *volume, translation, depsgraph);
|
||||
}
|
||||
if (bke::Instances *instances = geometry.get_instances_for_write()) {
|
||||
translate_instances(*instances, translation);
|
||||
}
|
||||
if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
|
||||
translate_curve_edit_hints(*curve_edit_hints, translation);
|
||||
}
|
||||
}
|
||||
|
||||
void transform_geometry_set(GeoNodeExecParams ¶ms,
|
||||
GeometrySet &geometry,
|
||||
const float4x4 &transform,
|
||||
const Depsgraph &depsgraph)
|
||||
{
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
curves->geometry.wrap().transform(transform);
|
||||
}
|
||||
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 (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
|
||||
transform_greasepencil(*grease_pencil, transform);
|
||||
}
|
||||
if (Volume *volume = geometry.get_volume_for_write()) {
|
||||
transform_volume(params, *volume, transform, depsgraph);
|
||||
}
|
||||
if (bke::Instances *instances = geometry.get_instances_for_write()) {
|
||||
transform_instances(*instances, transform);
|
||||
}
|
||||
if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
|
||||
transform_curve_edit_hints(*curve_edit_hints, transform);
|
||||
}
|
||||
}
|
||||
|
||||
void transform_mesh(Mesh &mesh,
|
||||
const float3 translation,
|
||||
const math::Quaternion rotation,
|
||||
const float3 scale)
|
||||
{
|
||||
const float4x4 matrix = math::from_loc_rot_scale<float4x4>(translation, rotation, scale);
|
||||
transform_mesh(mesh, matrix);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
namespace blender::nodes::node_geo_transform_geometry_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>("Geometry");
|
||||
b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION);
|
||||
b.add_input<decl::Rotation>("Rotation");
|
||||
b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ);
|
||||
b.add_output<decl::Geometry>("Geometry").propagate_all();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
@@ -313,13 +57,17 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
/* Use only translation if rotation and scale don't apply. */
|
||||
if (use_translate(rotation, scale)) {
|
||||
translate_geometry_set(params, geometry_set, translation, *params.depsgraph());
|
||||
geometry::translate_geometry(geometry_set, translation);
|
||||
}
|
||||
else {
|
||||
transform_geometry_set(params,
|
||||
geometry_set,
|
||||
math::from_loc_rot_scale<float4x4>(translation, rotation, scale),
|
||||
*params.depsgraph());
|
||||
if (auto errors = geometry::transform_geometry(
|
||||
geometry_set, math::from_loc_rot_scale<float4x4>(translation, rotation, scale)))
|
||||
{
|
||||
if (errors->volume_too_small) {
|
||||
params.error_message_add(NodeWarningType::Warning,
|
||||
TIP_("Volume scale is lower than permitted by OpenVDB"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
|
||||
Reference in New Issue
Block a user