Files
test/source/blender/blenkernel/intern/lattice_deform.cc
Hans Goudey 1c0f374ec3 Object: Move transform matrices to runtime struct
The `object_to_world` and `world_to_object` matrices are set during
depsgraph evaluation, calculated from the object's animated location,
rotation, scale, parenting, and constraints. It's confusing and
unnecessary to store them with the original data in DNA.

This commit moves them to `ObjectRuntime` and moves the matrices to
use the C++ `float4x4` type, giving the potential for simplified code
using the C++ abstractions. The matrices are accessible with functions
on `Object` directly since they are used so commonly. Though for write
access, directly using the runtime struct is necessary.

The inverse `world_to_object` matrix is often calculated before it's
used, even though it's calculated as part of depsgraph evaluation.
Long term we might not want to store this in `ObjectRuntime` at all,
and just calculate it on demand. Or at least we should remove the
redundant calculations. That should be done separately though.

Pull Request: https://projects.blender.org/blender/blender/pulls/118210
2024-02-14 16:14:49 +01:00

476 lines
15 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* Deform coordinates by a lattice object (used by modifier).
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BLI_simd.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "DNA_curve_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BKE_curve.hh"
#include "BKE_displist.h"
#include "BKE_editmesh.hh"
#include "BKE_key.hh"
#include "BKE_lattice.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_object_types.hh"
#include "BKE_deform.hh"
/* -------------------------------------------------------------------- */
/** \name Lattice Deform API
* \{ */
struct LatticeDeformData {
/* Convert from object space to deform space */
float latmat[4][4];
/* Cached reference to the lattice to use for evaluation. When in edit mode this attribute
* is set to the edit mode lattice. */
const Lattice *lt;
/* Preprocessed lattice points (converted to deform space). */
float *latticedata;
/* Prefetched DeformWeights of the lattice. */
float *lattice_weights;
};
LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Object *ob)
{
/* we make an array with all differences */
Lattice *lt = BKE_object_get_lattice(oblatt);
DispList *dl = oblatt->runtime->curve_cache ?
BKE_displist_find(&oblatt->runtime->curve_cache->disp, DL_VERTS) :
nullptr;
const float *co = dl ? dl->verts : nullptr;
float *fp, imat[4][4];
float fu, fv, fw;
int u, v, w;
float *latticedata;
float *lattice_weights = nullptr;
float latmat[4][4];
LatticeDeformData *lattice_deform_data;
/* May be null. */
BPoint *bp = lt->def;
const int32_t num_points = lt->pntsu * lt->pntsv * lt->pntsw;
/* We allocate one additional float for SSE2 optimizations. Without this
* the SSE2 instructions for the last item would read in unallocated memory. */
fp = latticedata = static_cast<float *>(
MEM_mallocN(sizeof(float[3]) * num_points + sizeof(float), "latticedata"));
/* for example with a particle system: (ob == nullptr) */
if (ob == nullptr) {
/* In deform-space, calc matrix. */
invert_m4_m4(latmat, oblatt->object_to_world().ptr());
/* back: put in deform array */
invert_m4_m4(imat, latmat);
}
else {
/* In deform-space, calc matrix. */
invert_m4_m4(imat, oblatt->object_to_world().ptr());
mul_m4_m4m4(latmat, imat, ob->object_to_world().ptr());
/* back: put in deform array. */
invert_m4_m4(imat, latmat);
}
/* Prefetch lattice deform group weights. */
int defgrp_index = -1;
const MDeformVert *dvert = BKE_lattice_deform_verts_get(oblatt);
if (lt->vgroup[0] && dvert) {
defgrp_index = BKE_id_defgroup_name_index(&lt->id, lt->vgroup);
if (defgrp_index != -1) {
lattice_weights = static_cast<float *>(
MEM_malloc_arrayN(num_points, sizeof(float), "lattice_weights"));
for (int index = 0; index < num_points; index++) {
lattice_weights[index] = BKE_defvert_find_weight(dvert + index, defgrp_index);
}
}
}
for (w = 0, fw = lt->fw; w < lt->pntsw; w++, fw += lt->dw) {
for (v = 0, fv = lt->fv; v < lt->pntsv; v++, fv += lt->dv) {
for (u = 0, fu = lt->fu; u < lt->pntsu; u++, co += 3, fp += 3, fu += lt->du) {
if (dl) {
fp[0] = co[0] - fu;
fp[1] = co[1] - fv;
fp[2] = co[2] - fw;
}
else {
fp[0] = bp->vec[0] - fu;
fp[1] = bp->vec[1] - fv;
fp[2] = bp->vec[2] - fw;
bp++;
}
mul_mat3_m4_v3(imat, fp);
}
}
}
lattice_deform_data = static_cast<LatticeDeformData *>(
MEM_mallocN(sizeof(LatticeDeformData), "Lattice Deform Data"));
lattice_deform_data->latticedata = latticedata;
lattice_deform_data->lattice_weights = lattice_weights;
lattice_deform_data->lt = lt;
copy_m4_m4(lattice_deform_data->latmat, latmat);
return lattice_deform_data;
}
void BKE_lattice_deform_data_eval_co(LatticeDeformData *lattice_deform_data,
float co[3],
float weight)
{
float *latticedata = lattice_deform_data->latticedata;
float *lattice_weights = lattice_deform_data->lattice_weights;
BLI_assert(latticedata);
const Lattice *lt = lattice_deform_data->lt;
float u, v, w, tu[4], tv[4], tw[4];
float vec[3];
int idx_w, idx_v, idx_u;
int ui, vi, wi, uu, vv, ww;
/* vgroup influence */
float co_prev[4] = {0}, weight_blend = 0.0f;
copy_v3_v3(co_prev, co);
#if BLI_HAVE_SSE2
__m128 co_vec = _mm_loadu_ps(co_prev);
#endif
/* co is in local coords, treat with latmat */
mul_v3_m4v3(vec, lattice_deform_data->latmat, co);
/* u v w coords */
if (lt->pntsu > 1) {
u = (vec[0] - lt->fu) / lt->du;
ui = int(floor(u));
u -= ui;
key_curve_position_weights(u, tu, lt->typeu);
}
else {
tu[0] = tu[2] = tu[3] = 0.0;
tu[1] = 1.0;
ui = 0;
}
if (lt->pntsv > 1) {
v = (vec[1] - lt->fv) / lt->dv;
vi = int(floor(v));
v -= vi;
key_curve_position_weights(v, tv, lt->typev);
}
else {
tv[0] = tv[2] = tv[3] = 0.0;
tv[1] = 1.0;
vi = 0;
}
if (lt->pntsw > 1) {
w = (vec[2] - lt->fw) / lt->dw;
wi = int(floor(w));
w -= wi;
key_curve_position_weights(w, tw, lt->typew);
}
else {
tw[0] = tw[2] = tw[3] = 0.0;
tw[1] = 1.0;
wi = 0;
}
const int w_stride = lt->pntsu * lt->pntsv;
const int idx_w_max = (lt->pntsw - 1) * lt->pntsu * lt->pntsv;
const int v_stride = lt->pntsu;
const int idx_v_max = (lt->pntsv - 1) * lt->pntsu;
const int idx_u_max = (lt->pntsu - 1);
for (ww = wi - 1; ww <= wi + 2; ww++) {
w = weight * tw[ww - wi + 1];
idx_w = std::clamp(ww * w_stride, 0, idx_w_max);
for (vv = vi - 1; vv <= vi + 2; vv++) {
v = w * tv[vv - vi + 1];
idx_v = std::clamp(vv * v_stride, 0, idx_v_max);
for (uu = ui - 1; uu <= ui + 2; uu++) {
u = v * tu[uu - ui + 1];
idx_u = std::clamp(uu, 0, idx_u_max);
const int idx = idx_w + idx_v + idx_u;
#if BLI_HAVE_SSE2
{
__m128 weight_vec = _mm_set1_ps(u);
/* We need to address special case for last item to avoid accessing invalid memory. */
__m128 lattice_vec;
if (idx * 3 == idx_w_max) {
copy_v3_v3((float *)&lattice_vec, &latticedata[idx * 3]);
}
else {
/* When not on last item, we can safely access one extra float, it will be ignored
* anyway. */
lattice_vec = _mm_loadu_ps(&latticedata[idx * 3]);
}
co_vec = _mm_add_ps(co_vec, _mm_mul_ps(lattice_vec, weight_vec));
}
#else
madd_v3_v3fl(co, &latticedata[idx * 3], u);
#endif
if (lattice_weights) {
weight_blend += (u * lattice_weights[idx]);
}
}
}
}
#if BLI_HAVE_SSE2
{
copy_v3_v3(co, (float *)&co_vec);
}
#endif
if (lattice_weights) {
interp_v3_v3v3(co, co_prev, co, weight_blend);
}
}
void BKE_lattice_deform_data_destroy(LatticeDeformData *lattice_deform_data)
{
if (lattice_deform_data->latticedata) {
MEM_freeN(lattice_deform_data->latticedata);
}
MEM_freeN(lattice_deform_data);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lattice Deform #BKE_lattice_deform_coords API
*
* #BKE_lattice_deform_coords and related functions.
* \{ */
struct LatticeDeformUserdata {
LatticeDeformData *lattice_deform_data;
float (*vert_coords)[3];
const MDeformVert *dvert;
int defgrp_index;
float fac;
bool invert_vgroup;
/** Specific data types. */
struct {
int cd_dvert_offset;
} bmesh;
};
static void lattice_deform_vert_with_dvert(const LatticeDeformUserdata *data,
const int index,
const MDeformVert *dvert)
{
if (dvert != nullptr) {
const float weight = data->invert_vgroup ?
1.0f - BKE_defvert_find_weight(dvert, data->defgrp_index) :
BKE_defvert_find_weight(dvert, data->defgrp_index);
if (weight > 0.0f) {
BKE_lattice_deform_data_eval_co(
data->lattice_deform_data, data->vert_coords[index], weight * data->fac);
}
}
else {
BKE_lattice_deform_data_eval_co(
data->lattice_deform_data, data->vert_coords[index], data->fac);
}
}
static void lattice_deform_vert_task(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict /*tls*/)
{
const LatticeDeformUserdata *data = static_cast<const LatticeDeformUserdata *>(userdata);
lattice_deform_vert_with_dvert(data, index, data->dvert ? &data->dvert[index] : nullptr);
}
static void lattice_vert_task_editmesh(void *__restrict userdata,
MempoolIterData *iter,
const TaskParallelTLS *__restrict /*tls*/)
{
const LatticeDeformUserdata *data = static_cast<const LatticeDeformUserdata *>(userdata);
BMVert *v = (BMVert *)iter;
MDeformVert *dvert = static_cast<MDeformVert *>(
BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset));
lattice_deform_vert_with_dvert(data, BM_elem_index_get(v), dvert);
}
static void lattice_vert_task_editmesh_no_dvert(void *__restrict userdata,
MempoolIterData *iter,
const TaskParallelTLS *__restrict /*tls*/)
{
const LatticeDeformUserdata *data = static_cast<const LatticeDeformUserdata *>(userdata);
BMVert *v = (BMVert *)iter;
lattice_deform_vert_with_dvert(data, BM_elem_index_get(v), nullptr);
}
static void lattice_deform_coords_impl(const Object *ob_lattice,
const Object *ob_target,
float (*vert_coords)[3],
const int vert_coords_len,
const short flag,
const char *defgrp_name,
const float fac,
const Mesh *me_target,
BMEditMesh *em_target)
{
LatticeDeformData *lattice_deform_data;
const MDeformVert *dvert = nullptr;
int defgrp_index = -1;
int cd_dvert_offset = -1;
if (ob_lattice->type != OB_LATTICE) {
return;
}
lattice_deform_data = BKE_lattice_deform_data_create(ob_lattice, ob_target);
/* Check whether to use vertex groups (only possible if ob_target is a Mesh or Lattice).
* We want either a Mesh/Lattice with no derived data, or derived data with deformverts.
*/
if (defgrp_name && defgrp_name[0] && ob_target && ELEM(ob_target->type, OB_MESH, OB_LATTICE)) {
defgrp_index = BKE_id_defgroup_name_index(me_target ? &me_target->id : (ID *)ob_target->data,
defgrp_name);
if (defgrp_index != -1) {
/* if there's derived data without deformverts, don't use vgroups */
if (em_target) {
cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT);
}
else if (me_target) {
dvert = static_cast<const MDeformVert *>(
CustomData_get_layer(&me_target->vert_data, CD_MDEFORMVERT));
}
else if (ob_target->type == OB_LATTICE) {
dvert = ((Lattice *)ob_target->data)->dvert;
}
else {
dvert = ((Mesh *)ob_target->data)->deform_verts().data();
}
}
}
LatticeDeformUserdata data{};
data.lattice_deform_data = lattice_deform_data;
data.vert_coords = vert_coords;
data.dvert = dvert;
data.defgrp_index = defgrp_index;
data.fac = fac;
data.invert_vgroup = (flag & MOD_LATTICE_INVERT_VGROUP) != 0;
data.bmesh.cd_dvert_offset = cd_dvert_offset;
if (em_target != nullptr) {
/* While this could cause an extra loop over mesh data, in most cases this will
* have already been properly set. */
BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
TaskParallelSettings settings;
BLI_parallel_mempool_settings_defaults(&settings);
if (cd_dvert_offset != -1) {
BLI_task_parallel_mempool(
em_target->bm->vpool, &data, lattice_vert_task_editmesh, &settings);
}
else {
BLI_task_parallel_mempool(
em_target->bm->vpool, &data, lattice_vert_task_editmesh_no_dvert, &settings);
}
}
else {
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 32;
BLI_task_parallel_range(0, vert_coords_len, &data, lattice_deform_vert_task, &settings);
}
BKE_lattice_deform_data_destroy(lattice_deform_data);
}
void BKE_lattice_deform_coords(const Object *ob_lattice,
const Object *ob_target,
float (*vert_coords)[3],
const int vert_coords_len,
const short flag,
const char *defgrp_name,
float fac)
{
lattice_deform_coords_impl(ob_lattice,
ob_target,
vert_coords,
vert_coords_len,
flag,
defgrp_name,
fac,
nullptr,
nullptr);
}
void BKE_lattice_deform_coords_with_mesh(const Object *ob_lattice,
const Object *ob_target,
float (*vert_coords)[3],
const int vert_coords_len,
const short flag,
const char *defgrp_name,
const float fac,
const Mesh *me_target)
{
lattice_deform_coords_impl(ob_lattice,
ob_target,
vert_coords,
vert_coords_len,
flag,
defgrp_name,
fac,
me_target,
nullptr);
}
void BKE_lattice_deform_coords_with_editmesh(const Object *ob_lattice,
const Object *ob_target,
float (*vert_coords)[3],
const int vert_coords_len,
const short flag,
const char *defgrp_name,
const float fac,
BMEditMesh *em_target)
{
lattice_deform_coords_impl(ob_lattice,
ob_target,
vert_coords,
vert_coords_len,
flag,
defgrp_name,
fac,
nullptr,
em_target);
}
/** \} */