There are currently 3 types of springs: basic linear springs, goal springs toward a fixed global target (not recommended, but works) and bending springs. These are agnostic to the specific spring definition in the cloth system so hair systems can use the same API without converting everything to cloth first. Conflicts: source/blender/physics/intern/implicit_blender.c
1866 lines
49 KiB
C
1866 lines
49 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) Blender Foundation
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/blenkernel/intern/implicit.c
|
|
* \ingroup bph
|
|
*/
|
|
|
|
#include "implicit.h"
|
|
|
|
#ifdef IMPLICIT_SOLVER_BLENDER
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_object_force.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_texture_types.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_linklist.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_cloth.h"
|
|
#include "BKE_collision.h"
|
|
#include "BKE_effect.h"
|
|
#include "BKE_global.h"
|
|
|
|
#include "BPH_mass_spring.h"
|
|
|
|
#ifdef __GNUC__
|
|
# pragma GCC diagnostic ignored "-Wtype-limits"
|
|
#endif
|
|
|
|
#ifdef _OPENMP
|
|
# define CLOTH_OPENMP_LIMIT 512
|
|
#endif
|
|
|
|
#if 0 /* debug timing */
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
static LARGE_INTEGER _itstart, _itend;
|
|
static LARGE_INTEGER ifreq;
|
|
static void itstart(void)
|
|
{
|
|
static int first = 1;
|
|
if (first) {
|
|
QueryPerformanceFrequency(&ifreq);
|
|
first = 0;
|
|
}
|
|
QueryPerformanceCounter(&_itstart);
|
|
}
|
|
static void itend(void)
|
|
{
|
|
QueryPerformanceCounter(&_itend);
|
|
}
|
|
double itval(void)
|
|
{
|
|
return ((double)_itend.QuadPart -
|
|
(double)_itstart.QuadPart)/((double)ifreq.QuadPart);
|
|
}
|
|
#else
|
|
#include <sys/time.h>
|
|
// intrinsics need better compile flag checking
|
|
// #include <xmmintrin.h>
|
|
// #include <pmmintrin.h>
|
|
// #include <pthread.h>
|
|
|
|
static struct timeval _itstart, _itend;
|
|
static struct timezone itz;
|
|
static void itstart(void)
|
|
{
|
|
gettimeofday(&_itstart, &itz);
|
|
}
|
|
static void itend(void)
|
|
{
|
|
gettimeofday(&_itend, &itz);
|
|
}
|
|
static double itval(void)
|
|
{
|
|
double t1, t2;
|
|
t1 = (double)_itstart.tv_sec + (double)_itstart.tv_usec/(1000*1000);
|
|
t2 = (double)_itend.tv_sec + (double)_itend.tv_usec/(1000*1000);
|
|
return t2-t1;
|
|
}
|
|
#endif
|
|
#endif /* debug timing */
|
|
|
|
static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
|
|
static float ZERO[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
|
|
|
/*
|
|
#define C99
|
|
#ifdef C99
|
|
#defineDO_INLINE inline
|
|
#else
|
|
#defineDO_INLINE static
|
|
#endif
|
|
*/
|
|
struct Cloth;
|
|
|
|
//////////////////////////////////////////
|
|
/* fast vector / matrix library, enhancements are welcome :) -dg */
|
|
/////////////////////////////////////////
|
|
|
|
/* DEFINITIONS */
|
|
typedef float lfVector[3];
|
|
typedef struct fmatrix3x3 {
|
|
float m[3][3]; /* 3x3 matrix */
|
|
unsigned int c, r; /* column and row number */
|
|
/* int pinned; // is this vertex allowed to move? */
|
|
float n1, n2, n3; /* three normal vectors for collision constrains */
|
|
unsigned int vcount; /* vertex count */
|
|
unsigned int scount; /* spring count */
|
|
} fmatrix3x3;
|
|
|
|
///////////////////////////
|
|
// float[3] vector
|
|
///////////////////////////
|
|
/* simple vector code */
|
|
/* STATUS: verified */
|
|
DO_INLINE void mul_fvector_S(float to[3], float from[3], float scalar)
|
|
{
|
|
to[0] = from[0] * scalar;
|
|
to[1] = from[1] * scalar;
|
|
to[2] = from[2] * scalar;
|
|
}
|
|
/* simple v^T * v product ("outer product") */
|
|
/* STATUS: HAS TO BE verified (*should* work) */
|
|
DO_INLINE void mul_fvectorT_fvector(float to[3][3], float vectorA[3], float vectorB[3])
|
|
{
|
|
mul_fvector_S(to[0], vectorB, vectorA[0]);
|
|
mul_fvector_S(to[1], vectorB, vectorA[1]);
|
|
mul_fvector_S(to[2], vectorB, vectorA[2]);
|
|
}
|
|
/* simple v^T * v product with scalar ("outer product") */
|
|
/* STATUS: HAS TO BE verified (*should* work) */
|
|
DO_INLINE void mul_fvectorT_fvectorS(float to[3][3], float vectorA[3], float vectorB[3], float aS)
|
|
{
|
|
mul_fvectorT_fvector(to, vectorA, vectorB);
|
|
|
|
mul_fvector_S(to[0], to[0], aS);
|
|
mul_fvector_S(to[1], to[1], aS);
|
|
mul_fvector_S(to[2], to[2], aS);
|
|
}
|
|
|
|
#if 0
|
|
/* printf vector[3] on console: for debug output */
|
|
static void print_fvector(float m3[3])
|
|
{
|
|
printf("%f\n%f\n%f\n\n", m3[0], m3[1], m3[2]);
|
|
}
|
|
|
|
///////////////////////////
|
|
// long float vector float (*)[3]
|
|
///////////////////////////
|
|
/* print long vector on console: for debug output */
|
|
DO_INLINE void print_lfvector(float (*fLongVector)[3], unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
for (i = 0; i < verts; i++) {
|
|
print_fvector(fLongVector[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* create long vector */
|
|
DO_INLINE lfVector *create_lfvector(unsigned int verts)
|
|
{
|
|
/* TODO: check if memory allocation was successful */
|
|
return (lfVector *)MEM_callocN(verts * sizeof(lfVector), "cloth_implicit_alloc_vector");
|
|
// return (lfVector *)cloth_aligned_malloc(&MEMORY_BASE, verts * sizeof(lfVector));
|
|
}
|
|
/* delete long vector */
|
|
DO_INLINE void del_lfvector(float (*fLongVector)[3])
|
|
{
|
|
if (fLongVector != NULL) {
|
|
MEM_freeN(fLongVector);
|
|
// cloth_aligned_free(&MEMORY_BASE, fLongVector);
|
|
}
|
|
}
|
|
/* copy long vector */
|
|
DO_INLINE void cp_lfvector(float (*to)[3], float (*from)[3], unsigned int verts)
|
|
{
|
|
memcpy(to, from, verts * sizeof(lfVector));
|
|
}
|
|
/* init long vector with float[3] */
|
|
DO_INLINE void init_lfvector(float (*fLongVector)[3], float vector[3], unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
for (i = 0; i < verts; i++) {
|
|
copy_v3_v3(fLongVector[i], vector);
|
|
}
|
|
}
|
|
/* zero long vector with float[3] */
|
|
DO_INLINE void zero_lfvector(float (*to)[3], unsigned int verts)
|
|
{
|
|
memset(to, 0.0f, verts * sizeof(lfVector));
|
|
}
|
|
/* multiply long vector with scalar*/
|
|
DO_INLINE void mul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < verts; i++) {
|
|
mul_fvector_S(to[i], fLongVector[i], scalar);
|
|
}
|
|
}
|
|
/* multiply long vector with scalar*/
|
|
/* A -= B * float */
|
|
DO_INLINE void submul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
for (i = 0; i < verts; i++) {
|
|
VECSUBMUL(to[i], fLongVector[i], scalar);
|
|
}
|
|
}
|
|
/* dot product for big vector */
|
|
DO_INLINE float dot_lfvector(float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts)
|
|
{
|
|
long i = 0;
|
|
float temp = 0.0;
|
|
// XXX brecht, disabled this for now (first schedule line was already disabled),
|
|
// due to non-commutative nature of floating point ops this makes the sim give
|
|
// different results each time you run it!
|
|
// schedule(guided, 2)
|
|
//#pragma omp parallel for reduction(+: temp) if (verts > CLOTH_OPENMP_LIMIT)
|
|
for (i = 0; i < (long)verts; i++) {
|
|
temp += dot_v3v3(fLongVectorA[i], fLongVectorB[i]);
|
|
}
|
|
return temp;
|
|
}
|
|
/* A = B + C --> for big vector */
|
|
DO_INLINE void add_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < verts; i++) {
|
|
VECADD(to[i], fLongVectorA[i], fLongVectorB[i]);
|
|
}
|
|
|
|
}
|
|
/* A = B + C * float --> for big vector */
|
|
DO_INLINE void add_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < verts; i++) {
|
|
VECADDS(to[i], fLongVectorA[i], fLongVectorB[i], bS);
|
|
|
|
}
|
|
}
|
|
/* A = B * float + C * float --> for big vector */
|
|
DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float aS, float (*fLongVectorB)[3], float bS, unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < verts; i++) {
|
|
VECADDSS(to[i], fLongVectorA[i], aS, fLongVectorB[i], bS);
|
|
}
|
|
}
|
|
/* A = B - C * float --> for big vector */
|
|
DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
for (i = 0; i < verts; i++) {
|
|
VECSUBS(to[i], fLongVectorA[i], fLongVectorB[i], bS);
|
|
}
|
|
|
|
}
|
|
/* A = B - C --> for big vector */
|
|
DO_INLINE void sub_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < verts; i++) {
|
|
sub_v3_v3v3(to[i], fLongVectorA[i], fLongVectorB[i]);
|
|
}
|
|
|
|
}
|
|
///////////////////////////
|
|
// 3x3 matrix
|
|
///////////////////////////
|
|
#if 0
|
|
/* printf 3x3 matrix on console: for debug output */
|
|
static void print_fmatrix(float m3[3][3])
|
|
{
|
|
printf("%f\t%f\t%f\n", m3[0][0], m3[0][1], m3[0][2]);
|
|
printf("%f\t%f\t%f\n", m3[1][0], m3[1][1], m3[1][2]);
|
|
printf("%f\t%f\t%f\n\n", m3[2][0], m3[2][1], m3[2][2]);
|
|
}
|
|
|
|
static void print_sparse_matrix(fmatrix3x3 *m)
|
|
{
|
|
if (m) {
|
|
unsigned int i;
|
|
for (i = 0; i < m[0].vcount + m[0].scount; i++) {
|
|
printf("%d:\n", i);
|
|
print_fmatrix(m[i].m);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void print_lvector(lfVector *v, int numverts)
|
|
{
|
|
int i;
|
|
for (i = 0; i < numverts; ++i) {
|
|
if (i > 0)
|
|
printf("\n");
|
|
|
|
printf("%f,\n", v[i][0]);
|
|
printf("%f,\n", v[i][1]);
|
|
printf("%f,\n", v[i][2]);
|
|
}
|
|
}
|
|
|
|
static void print_bfmatrix(fmatrix3x3 *m)
|
|
{
|
|
int tot = m[0].vcount + m[0].scount;
|
|
int size = m[0].vcount * 3;
|
|
float *t = MEM_callocN(sizeof(float) * size*size, "bfmatrix");
|
|
int q, i, j;
|
|
|
|
for (q = 0; q < tot; ++q) {
|
|
int k = 3 * m[q].r;
|
|
int l = 3 * m[q].c;
|
|
|
|
for (j = 0; j < 3; ++j) {
|
|
for (i = 0; i < 3; ++i) {
|
|
// if (t[k + i + (l + j) * size] != 0.0f) {
|
|
// printf("warning: overwriting value at %d, %d\n", m[q].r, m[q].c);
|
|
// }
|
|
if (k == l) {
|
|
t[k + i + (k + j) * size] += m[q].m[i][j];
|
|
}
|
|
else {
|
|
t[k + i + (l + j) * size] += m[q].m[i][j];
|
|
t[l + j + (k + i) * size] += m[q].m[j][i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < size; ++j) {
|
|
if (j > 0 && j % 3 == 0)
|
|
printf("\n");
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
if (i > 0 && i % 3 == 0)
|
|
printf(" ");
|
|
|
|
implicit_print_matrix_elem(t[i + j * size]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
MEM_freeN(t);
|
|
}
|
|
|
|
/* copy 3x3 matrix */
|
|
DO_INLINE void cp_fmatrix(float to[3][3], float from[3][3])
|
|
{
|
|
// memcpy(to, from, sizeof (float) * 9);
|
|
copy_v3_v3(to[0], from[0]);
|
|
copy_v3_v3(to[1], from[1]);
|
|
copy_v3_v3(to[2], from[2]);
|
|
}
|
|
|
|
/* copy 3x3 matrix */
|
|
DO_INLINE void initdiag_fmatrixS(float to[3][3], float aS)
|
|
{
|
|
cp_fmatrix(to, ZERO);
|
|
|
|
to[0][0] = aS;
|
|
to[1][1] = aS;
|
|
to[2][2] = aS;
|
|
}
|
|
|
|
#if 0
|
|
/* calculate determinant of 3x3 matrix */
|
|
DO_INLINE float det_fmatrix(float m[3][3])
|
|
{
|
|
return m[0][0]*m[1][1]*m[2][2] + m[1][0]*m[2][1]*m[0][2] + m[0][1]*m[1][2]*m[2][0]
|
|
-m[0][0]*m[1][2]*m[2][1] - m[0][1]*m[1][0]*m[2][2] - m[2][0]*m[1][1]*m[0][2];
|
|
}
|
|
|
|
DO_INLINE void inverse_fmatrix(float to[3][3], float from[3][3])
|
|
{
|
|
unsigned int i, j;
|
|
float d;
|
|
|
|
if ((d=det_fmatrix(from)) == 0) {
|
|
printf("can't build inverse");
|
|
exit(0);
|
|
}
|
|
for (i=0;i<3;i++) {
|
|
for (j=0;j<3;j++) {
|
|
int i1=(i+1)%3;
|
|
int i2=(i+2)%3;
|
|
int j1=(j+1)%3;
|
|
int j2=(j+2)%3;
|
|
// reverse indexs i&j to take transpose
|
|
to[j][i] = (from[i1][j1]*from[i2][j2]-from[i1][j2]*from[i2][j1])/d;
|
|
/*
|
|
if (i==j)
|
|
to[i][j] = 1.0f / from[i][j];
|
|
else
|
|
to[i][j] = 0;
|
|
*/
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
/* 3x3 matrix multiplied by a scalar */
|
|
/* STATUS: verified */
|
|
DO_INLINE void mul_fmatrix_S(float matrix[3][3], float scalar)
|
|
{
|
|
mul_fvector_S(matrix[0], matrix[0], scalar);
|
|
mul_fvector_S(matrix[1], matrix[1], scalar);
|
|
mul_fvector_S(matrix[2], matrix[2], scalar);
|
|
}
|
|
|
|
/* a vector multiplied by a 3x3 matrix */
|
|
/* STATUS: verified */
|
|
DO_INLINE void mul_fvector_fmatrix(float *to, float *from, float matrix[3][3])
|
|
{
|
|
to[0] = matrix[0][0]*from[0] + matrix[1][0]*from[1] + matrix[2][0]*from[2];
|
|
to[1] = matrix[0][1]*from[0] + matrix[1][1]*from[1] + matrix[2][1]*from[2];
|
|
to[2] = matrix[0][2]*from[0] + matrix[1][2]*from[1] + matrix[2][2]*from[2];
|
|
}
|
|
|
|
/* 3x3 matrix multiplied by a vector */
|
|
/* STATUS: verified */
|
|
DO_INLINE void mul_fmatrix_fvector(float *to, float matrix[3][3], float from[3])
|
|
{
|
|
to[0] = dot_v3v3(matrix[0], from);
|
|
to[1] = dot_v3v3(matrix[1], from);
|
|
to[2] = dot_v3v3(matrix[2], from);
|
|
}
|
|
/* 3x3 matrix addition with 3x3 matrix */
|
|
DO_INLINE void add_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3])
|
|
{
|
|
VECADD(to[0], matrixA[0], matrixB[0]);
|
|
VECADD(to[1], matrixA[1], matrixB[1]);
|
|
VECADD(to[2], matrixA[2], matrixB[2]);
|
|
}
|
|
/* A -= B*x + C*y (3x3 matrix sub-addition with 3x3 matrix) */
|
|
DO_INLINE void subadd_fmatrixS_fmatrixS(float to[3][3], float matrixA[3][3], float aS, float matrixB[3][3], float bS)
|
|
{
|
|
VECSUBADDSS(to[0], matrixA[0], aS, matrixB[0], bS);
|
|
VECSUBADDSS(to[1], matrixA[1], aS, matrixB[1], bS);
|
|
VECSUBADDSS(to[2], matrixA[2], aS, matrixB[2], bS);
|
|
}
|
|
/* A = B - C (3x3 matrix subtraction with 3x3 matrix) */
|
|
DO_INLINE void sub_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3])
|
|
{
|
|
sub_v3_v3v3(to[0], matrixA[0], matrixB[0]);
|
|
sub_v3_v3v3(to[1], matrixA[1], matrixB[1]);
|
|
sub_v3_v3v3(to[2], matrixA[2], matrixB[2]);
|
|
}
|
|
/////////////////////////////////////////////////////////////////
|
|
// special functions
|
|
/////////////////////////////////////////////////////////////////
|
|
/* 3x3 matrix multiplied+added by a vector */
|
|
/* STATUS: verified */
|
|
DO_INLINE void muladd_fmatrix_fvector(float to[3], float matrix[3][3], float from[3])
|
|
{
|
|
to[0] += dot_v3v3(matrix[0], from);
|
|
to[1] += dot_v3v3(matrix[1], from);
|
|
to[2] += dot_v3v3(matrix[2], from);
|
|
}
|
|
|
|
BLI_INLINE void outerproduct(float r[3][3], const float a[3], const float b[3])
|
|
{
|
|
mul_v3_v3fl(r[0], a, b[0]);
|
|
mul_v3_v3fl(r[1], a, b[1]);
|
|
mul_v3_v3fl(r[2], a, b[2]);
|
|
}
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////
|
|
// SPARSE SYMMETRIC big matrix with 3x3 matrix entries
|
|
///////////////////////////
|
|
/* printf a big matrix on console: for debug output */
|
|
#if 0
|
|
static void print_bfmatrix(fmatrix3x3 *m3)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < m3[0].vcount + m3[0].scount; i++)
|
|
{
|
|
print_fmatrix(m3[i].m);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
BLI_INLINE void init_fmatrix(fmatrix3x3 *matrix, int r, int c)
|
|
{
|
|
matrix->r = r;
|
|
matrix->c = c;
|
|
}
|
|
|
|
/* create big matrix */
|
|
DO_INLINE fmatrix3x3 *create_bfmatrix(unsigned int verts, unsigned int springs)
|
|
{
|
|
// TODO: check if memory allocation was successful */
|
|
fmatrix3x3 *temp = (fmatrix3x3 *)MEM_callocN(sizeof(fmatrix3x3) * (verts + springs), "cloth_implicit_alloc_matrix");
|
|
int i;
|
|
|
|
temp[0].vcount = verts;
|
|
temp[0].scount = springs;
|
|
|
|
/* vertex part of the matrix is diagonal blocks */
|
|
for (i = 0; i < verts; ++i) {
|
|
init_fmatrix(temp + i, i, i);
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
/* delete big matrix */
|
|
DO_INLINE void del_bfmatrix(fmatrix3x3 *matrix)
|
|
{
|
|
if (matrix != NULL) {
|
|
MEM_freeN(matrix);
|
|
}
|
|
}
|
|
|
|
/* copy big matrix */
|
|
DO_INLINE void cp_bfmatrix(fmatrix3x3 *to, fmatrix3x3 *from)
|
|
{
|
|
// TODO bounds checking
|
|
memcpy(to, from, sizeof(fmatrix3x3) * (from[0].vcount+from[0].scount));
|
|
}
|
|
|
|
/* init big matrix */
|
|
// slow in parallel
|
|
DO_INLINE void init_bfmatrix(fmatrix3x3 *matrix, float m3[3][3])
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) {
|
|
cp_fmatrix(matrix[i].m, m3);
|
|
}
|
|
}
|
|
|
|
/* init the diagonal of big matrix */
|
|
// slow in parallel
|
|
DO_INLINE void initdiag_bfmatrix(fmatrix3x3 *matrix, float m3[3][3])
|
|
{
|
|
unsigned int i, j;
|
|
float tmatrix[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
|
|
|
for (i = 0; i < matrix[0].vcount; i++) {
|
|
cp_fmatrix(matrix[i].m, m3);
|
|
}
|
|
for (j = matrix[0].vcount; j < matrix[0].vcount+matrix[0].scount; j++) {
|
|
cp_fmatrix(matrix[j].m, tmatrix);
|
|
}
|
|
}
|
|
|
|
/* SPARSE SYMMETRIC multiply big matrix with long vector*/
|
|
/* STATUS: verified */
|
|
DO_INLINE void mul_bfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector *fLongVector)
|
|
{
|
|
unsigned int i = 0;
|
|
unsigned int vcount = from[0].vcount;
|
|
lfVector *temp = create_lfvector(vcount);
|
|
|
|
zero_lfvector(to, vcount);
|
|
|
|
#pragma omp parallel sections private(i) if (vcount > CLOTH_OPENMP_LIMIT)
|
|
{
|
|
#pragma omp section
|
|
{
|
|
for (i = from[0].vcount; i < from[0].vcount+from[0].scount; i++) {
|
|
muladd_fmatrix_fvector(to[from[i].c], from[i].m, fLongVector[from[i].r]);
|
|
}
|
|
}
|
|
#pragma omp section
|
|
{
|
|
for (i = 0; i < from[0].vcount+from[0].scount; i++) {
|
|
muladd_fmatrix_fvector(temp[from[i].r], from[i].m, fLongVector[from[i].c]);
|
|
}
|
|
}
|
|
}
|
|
add_lfvector_lfvector(to, to, temp, from[0].vcount);
|
|
|
|
del_lfvector(temp);
|
|
|
|
|
|
}
|
|
|
|
/* SPARSE SYMMETRIC sub big matrix with big matrix*/
|
|
/* A -= B * float + C * float --> for big matrix */
|
|
/* VERIFIED */
|
|
DO_INLINE void subadd_bfmatrixS_bfmatrixS( fmatrix3x3 *to, fmatrix3x3 *from, float aS, fmatrix3x3 *matrix, float bS)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
/* process diagonal elements */
|
|
for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) {
|
|
subadd_fmatrixS_fmatrixS(to[i].m, from[i].m, aS, matrix[i].m, bS);
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// simulator start
|
|
///////////////////////////////////////////////////////////////////
|
|
typedef struct RootTransform {
|
|
float loc[3];
|
|
float rot[3][3];
|
|
|
|
float vel[3];
|
|
float omega[3];
|
|
|
|
float acc[3];
|
|
float domega_dt[3];
|
|
} RootTransform;
|
|
|
|
typedef struct Implicit_Data {
|
|
/* inputs */
|
|
fmatrix3x3 *bigI; /* identity (constant) */
|
|
fmatrix3x3 *M; /* masses */
|
|
lfVector *F; /* forces */
|
|
fmatrix3x3 *dFdV, *dFdX; /* force jacobians */
|
|
RootTransform *root; /* root transforms */
|
|
|
|
/* motion state data */
|
|
lfVector *X, *Xnew; /* positions */
|
|
lfVector *V, *Vnew; /* velocities */
|
|
|
|
/* internal solver data */
|
|
lfVector *B; /* B for A*dV = B */
|
|
fmatrix3x3 *A; /* A for A*dV = B */
|
|
|
|
lfVector *dV; /* velocity change (solution of A*dV = B) */
|
|
lfVector *z; /* target velocity in constrained directions */
|
|
fmatrix3x3 *S; /* filtering matrix for constraints */
|
|
fmatrix3x3 *P, *Pinv; /* pre-conditioning matrix */
|
|
} Implicit_Data;
|
|
|
|
Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings)
|
|
{
|
|
Implicit_Data *id = (Implicit_Data *)MEM_callocN(sizeof(Implicit_Data), "implicit vecmat");
|
|
|
|
/* process diagonal elements */
|
|
id->A = create_bfmatrix(numverts, numsprings);
|
|
id->dFdV = create_bfmatrix(numverts, numsprings);
|
|
id->dFdX = create_bfmatrix(numverts, numsprings);
|
|
id->S = create_bfmatrix(numverts, 0);
|
|
id->Pinv = create_bfmatrix(numverts, numsprings);
|
|
id->P = create_bfmatrix(numverts, numsprings);
|
|
id->bigI = create_bfmatrix(numverts, numsprings); // TODO 0 springs
|
|
id->M = create_bfmatrix(numverts, numsprings);
|
|
id->X = create_lfvector(numverts);
|
|
id->Xnew = create_lfvector(numverts);
|
|
id->V = create_lfvector(numverts);
|
|
id->Vnew = create_lfvector(numverts);
|
|
id->F = create_lfvector(numverts);
|
|
id->B = create_lfvector(numverts);
|
|
id->dV = create_lfvector(numverts);
|
|
id->z = create_lfvector(numverts);
|
|
|
|
id->root = MEM_callocN(sizeof(RootTransform) * numverts, "root transforms");
|
|
|
|
initdiag_bfmatrix(id->bigI, I);
|
|
|
|
return id;
|
|
}
|
|
|
|
void BPH_mass_spring_solver_free(Implicit_Data *id)
|
|
{
|
|
del_bfmatrix(id->A);
|
|
del_bfmatrix(id->dFdV);
|
|
del_bfmatrix(id->dFdX);
|
|
del_bfmatrix(id->S);
|
|
del_bfmatrix(id->P);
|
|
del_bfmatrix(id->Pinv);
|
|
del_bfmatrix(id->bigI);
|
|
del_bfmatrix(id->M);
|
|
|
|
del_lfvector(id->X);
|
|
del_lfvector(id->Xnew);
|
|
del_lfvector(id->V);
|
|
del_lfvector(id->Vnew);
|
|
del_lfvector(id->F);
|
|
del_lfvector(id->B);
|
|
del_lfvector(id->dV);
|
|
del_lfvector(id->z);
|
|
|
|
MEM_freeN(id->root);
|
|
|
|
MEM_freeN(id);
|
|
}
|
|
|
|
/* ==== Transformation of Moving Reference Frame ====
|
|
* x_world, v_world, f_world, a_world, dfdx_world, dfdv_world : state variables in world space
|
|
* x_root, v_root, f_root, a_root, dfdx_root, dfdv_root : state variables in root space
|
|
*
|
|
* x0 : translation of the root frame (hair root location)
|
|
* v0 : linear velocity of the root frame
|
|
* a0 : acceleration of the root frame
|
|
* R : rotation matrix of the root frame
|
|
* w : angular velocity of the root frame
|
|
* dwdt : angular acceleration of the root frame
|
|
*/
|
|
|
|
/* x_root = R^T * x_world */
|
|
BLI_INLINE void loc_world_to_root(float r[3], const float v[3], const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
sub_v3_v3v3(r, v, root->loc);
|
|
mul_transposed_m3_v3((float (*)[3])root->rot, r);
|
|
#else
|
|
copy_v3_v3(r, v);
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* x_world = R * x_root */
|
|
BLI_INLINE void loc_root_to_world(float r[3], const float v[3], const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
copy_v3_v3(r, v);
|
|
mul_m3_v3((float (*)[3])root->rot, r);
|
|
add_v3_v3(r, root->loc);
|
|
#else
|
|
copy_v3_v3(r, v);
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* v_root = cross(w, x_root) + R^T*(v_world - v0) */
|
|
BLI_INLINE void vel_world_to_root(float r[3], const float x_root[3], const float v[3], const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float angvel[3];
|
|
cross_v3_v3v3(angvel, root->omega, x_root);
|
|
|
|
sub_v3_v3v3(r, v, root->vel);
|
|
mul_transposed_m3_v3((float (*)[3])root->rot, r);
|
|
add_v3_v3(r, angvel);
|
|
#else
|
|
copy_v3_v3(r, v);
|
|
(void)x_root;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* v_world = R*(v_root - cross(w, x_root)) + v0 */
|
|
BLI_INLINE void vel_root_to_world(float r[3], const float x_root[3], const float v[3], const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float angvel[3];
|
|
cross_v3_v3v3(angvel, root->omega, x_root);
|
|
|
|
sub_v3_v3v3(r, v, angvel);
|
|
mul_m3_v3((float (*)[3])root->rot, r);
|
|
add_v3_v3(r, root->vel);
|
|
#else
|
|
copy_v3_v3(r, v);
|
|
(void)x_root;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* a_root = -cross(dwdt, x_root) - 2*cross(w, v_root) - cross(w, cross(w, x_root)) + R^T*(a_world - a0) */
|
|
BLI_INLINE void force_world_to_root(float r[3], const float x_root[3], const float v_root[3], const float force[3], float mass, const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float euler[3], coriolis[3], centrifugal[3], rotvel[3];
|
|
|
|
cross_v3_v3v3(euler, root->domega_dt, x_root);
|
|
cross_v3_v3v3(coriolis, root->omega, v_root);
|
|
mul_v3_fl(coriolis, 2.0f);
|
|
cross_v3_v3v3(rotvel, root->omega, x_root);
|
|
cross_v3_v3v3(centrifugal, root->omega, rotvel);
|
|
|
|
madd_v3_v3v3fl(r, force, root->acc, mass);
|
|
mul_transposed_m3_v3((float (*)[3])root->rot, r);
|
|
madd_v3_v3fl(r, euler, mass);
|
|
madd_v3_v3fl(r, coriolis, mass);
|
|
madd_v3_v3fl(r, centrifugal, mass);
|
|
#else
|
|
copy_v3_v3(r, force);
|
|
(void)x_root;
|
|
(void)v_root;
|
|
(void)mass;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* a_world = R*[ a_root + cross(dwdt, x_root) + 2*cross(w, v_root) + cross(w, cross(w, x_root)) ] + a0 */
|
|
BLI_INLINE void force_root_to_world(float r[3], const float x_root[3], const float v_root[3], const float force[3], float mass, const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float euler[3], coriolis[3], centrifugal[3], rotvel[3];
|
|
|
|
cross_v3_v3v3(euler, root->domega_dt, x_root);
|
|
cross_v3_v3v3(coriolis, root->omega, v_root);
|
|
mul_v3_fl(coriolis, 2.0f);
|
|
cross_v3_v3v3(rotvel, root->omega, x_root);
|
|
cross_v3_v3v3(centrifugal, root->omega, rotvel);
|
|
|
|
madd_v3_v3v3fl(r, force, euler, mass);
|
|
madd_v3_v3fl(r, coriolis, mass);
|
|
madd_v3_v3fl(r, centrifugal, mass);
|
|
mul_m3_v3((float (*)[3])root->rot, r);
|
|
madd_v3_v3fl(r, root->acc, mass);
|
|
#else
|
|
copy_v3_v3(r, force);
|
|
(void)x_root;
|
|
(void)v_root;
|
|
(void)mass;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
BLI_INLINE void acc_world_to_root(float r[3], const float x_root[3], const float v_root[3], const float acc[3], const RootTransform *root)
|
|
{
|
|
force_world_to_root(r, x_root, v_root, acc, 1.0f, root);
|
|
}
|
|
|
|
BLI_INLINE void acc_root_to_world(float r[3], const float x_root[3], const float v_root[3], const float acc[3], const RootTransform *root)
|
|
{
|
|
force_root_to_world(r, x_root, v_root, acc, 1.0f, root);
|
|
}
|
|
|
|
BLI_INLINE void cross_m3_v3m3(float r[3][3], const float v[3], float m[3][3])
|
|
{
|
|
cross_v3_v3v3(r[0], v, m[0]);
|
|
cross_v3_v3v3(r[1], v, m[1]);
|
|
cross_v3_v3v3(r[2], v, m[2]);
|
|
}
|
|
|
|
BLI_INLINE void cross_v3_identity(float r[3][3], const float v[3])
|
|
{
|
|
r[0][0] = 0.0f; r[1][0] = v[2]; r[2][0] = -v[1];
|
|
r[0][1] = -v[2]; r[1][1] = 0.0f; r[2][1] = v[0];
|
|
r[0][2] = v[1]; r[1][2] = -v[0]; r[2][2] = 0.0f;
|
|
}
|
|
|
|
/* dfdx_root = m*[ -cross(dwdt, I) - cross(w, cross(w, I)) ] + R^T*(dfdx_world) */
|
|
BLI_INLINE void dfdx_world_to_root(float m[3][3], float dfdx[3][3], float mass, const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float t[3][3], u[3][3];
|
|
|
|
copy_m3_m3(t, (float (*)[3])root->rot);
|
|
transpose_m3(t);
|
|
mul_m3_m3m3(m, t, dfdx);
|
|
|
|
cross_v3_identity(t, root->domega_dt);
|
|
mul_m3_fl(t, mass);
|
|
sub_m3_m3m3(m, m, t);
|
|
|
|
cross_v3_identity(u, root->omega);
|
|
cross_m3_v3m3(t, root->omega, u);
|
|
mul_m3_fl(t, mass);
|
|
sub_m3_m3m3(m, m, t);
|
|
#else
|
|
copy_m3_m3(m, dfdx);
|
|
(void)mass;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* dfdx_world = R*(dfdx_root + m*[ cross(dwdt, I) + cross(w, cross(w, I)) ]) */
|
|
BLI_INLINE void dfdx_root_to_world(float m[3][3], float dfdx[3][3], float mass, const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float t[3][3], u[3][3];
|
|
|
|
cross_v3_identity(t, root->domega_dt);
|
|
mul_m3_fl(t, mass);
|
|
add_m3_m3m3(m, dfdx, t);
|
|
|
|
cross_v3_identity(u, root->omega);
|
|
cross_m3_v3m3(t, root->omega, u);
|
|
mul_m3_fl(t, mass);
|
|
add_m3_m3m3(m, m, t);
|
|
|
|
mul_m3_m3m3(m, (float (*)[3])root->rot, m);
|
|
#else
|
|
copy_m3_m3(m, dfdx);
|
|
(void)mass;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* dfdv_root = -2*m*cross(w, I) + R^T*(dfdv_world) */
|
|
BLI_INLINE void dfdv_world_to_root(float m[3][3], float dfdv[3][3], float mass, const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float t[3][3];
|
|
|
|
copy_m3_m3(t, (float (*)[3])root->rot);
|
|
transpose_m3(t);
|
|
mul_m3_m3m3(m, t, dfdv);
|
|
|
|
cross_v3_identity(t, root->omega);
|
|
mul_m3_fl(t, 2.0f*mass);
|
|
sub_m3_m3m3(m, m, t);
|
|
#else
|
|
copy_m3_m3(m, dfdv);
|
|
(void)mass;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* dfdv_world = R*(dfdv_root + 2*m*cross(w, I)) */
|
|
BLI_INLINE void dfdv_root_to_world(float m[3][3], float dfdv[3][3], float mass, const RootTransform *root)
|
|
{
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
float t[3][3];
|
|
|
|
cross_v3_identity(t, root->omega);
|
|
mul_m3_fl(t, 2.0f*mass);
|
|
add_m3_m3m3(m, dfdv, t);
|
|
|
|
mul_m3_m3m3(m, (float (*)[3])root->rot, m);
|
|
#else
|
|
copy_m3_m3(m, dfdv);
|
|
(void)mass;
|
|
(void)root;
|
|
#endif
|
|
}
|
|
|
|
/* ================================ */
|
|
|
|
#if 0
|
|
DO_INLINE float fb(float length, float L)
|
|
{
|
|
float x = length / L;
|
|
float xx = x * x;
|
|
float xxx = xx * x;
|
|
float xxxx = xxx * x;
|
|
return (-11.541f * xxxx + 34.193f * xxx - 39.083f * xx + 23.116f * x - 9.713f);
|
|
}
|
|
|
|
DO_INLINE float fbderiv(float length, float L)
|
|
{
|
|
float x = length/L;
|
|
float xx = x * x;
|
|
float xxx = xx * x;
|
|
return (-46.164f * xxx + 102.579f * xx - 78.166f * x + 23.116f);
|
|
}
|
|
|
|
DO_INLINE float fbstar(float length, float L, float kb, float cb)
|
|
{
|
|
float tempfb_fl = kb * fb(length, L);
|
|
float fbstar_fl = cb * (length - L);
|
|
|
|
if (tempfb_fl < fbstar_fl)
|
|
return fbstar_fl;
|
|
else
|
|
return tempfb_fl;
|
|
}
|
|
|
|
// function to calculae bending spring force (taken from Choi & Co)
|
|
DO_INLINE float fbstar_jacobi(float length, float L, float kb, float cb)
|
|
{
|
|
float tempfb_fl = kb * fb(length, L);
|
|
float fbstar_fl = cb * (length - L);
|
|
|
|
if (tempfb_fl < fbstar_fl) {
|
|
return cb;
|
|
}
|
|
else {
|
|
return kb * fbderiv(length, L);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
|
|
{
|
|
unsigned int i=0;
|
|
|
|
for (i = 0; i < S[0].vcount; i++) {
|
|
mul_m3_v3(S[i].m, V[S[i].r]);
|
|
}
|
|
}
|
|
|
|
#if 0 /* this version of the CG algorithm does not work very well with partial constraints (where S has non-zero elements) */
|
|
static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S)
|
|
{
|
|
// Solves for unknown X in equation AX=B
|
|
unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100;
|
|
float conjgrad_epsilon=0.0001f /* , conjgrad_lasterror=0 */ /* UNUSED */;
|
|
lfVector *q, *d, *tmp, *r;
|
|
float s, starget, a, s_prev;
|
|
unsigned int numverts = lA[0].vcount;
|
|
q = create_lfvector(numverts);
|
|
d = create_lfvector(numverts);
|
|
tmp = create_lfvector(numverts);
|
|
r = create_lfvector(numverts);
|
|
|
|
// zero_lfvector(ldV, CLOTHPARTICLES);
|
|
filter(ldV, S);
|
|
|
|
add_lfvector_lfvector(ldV, ldV, z, numverts);
|
|
|
|
// r = B - Mul(tmp, A, X); // just use B if X known to be zero
|
|
cp_lfvector(r, lB, numverts);
|
|
mul_bfmatrix_lfvector(tmp, lA, ldV);
|
|
sub_lfvector_lfvector(r, r, tmp, numverts);
|
|
|
|
filter(r, S);
|
|
|
|
cp_lfvector(d, r, numverts);
|
|
|
|
s = dot_lfvector(r, r, numverts);
|
|
starget = s * sqrtf(conjgrad_epsilon);
|
|
|
|
while (s>starget && conjgrad_loopcount < conjgrad_looplimit) {
|
|
// Mul(q, A, d); // q = A*d;
|
|
mul_bfmatrix_lfvector(q, lA, d);
|
|
|
|
filter(q, S);
|
|
|
|
a = s/dot_lfvector(d, q, numverts);
|
|
|
|
// X = X + d*a;
|
|
add_lfvector_lfvectorS(ldV, ldV, d, a, numverts);
|
|
|
|
// r = r - q*a;
|
|
sub_lfvector_lfvectorS(r, r, q, a, numverts);
|
|
|
|
s_prev = s;
|
|
s = dot_lfvector(r, r, numverts);
|
|
|
|
//d = r+d*(s/s_prev);
|
|
add_lfvector_lfvectorS(d, r, d, (s/s_prev), numverts);
|
|
|
|
filter(d, S);
|
|
|
|
conjgrad_loopcount++;
|
|
}
|
|
/* conjgrad_lasterror = s; */ /* UNUSED */
|
|
|
|
del_lfvector(q);
|
|
del_lfvector(d);
|
|
del_lfvector(tmp);
|
|
del_lfvector(r);
|
|
// printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount);
|
|
|
|
return conjgrad_loopcount<conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable
|
|
}
|
|
#endif
|
|
|
|
static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S)
|
|
{
|
|
// Solves for unknown X in equation AX=B
|
|
unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100;
|
|
float conjgrad_epsilon=0.01f;
|
|
|
|
unsigned int numverts = lA[0].vcount;
|
|
lfVector *fB = create_lfvector(numverts);
|
|
lfVector *AdV = create_lfvector(numverts);
|
|
lfVector *r = create_lfvector(numverts);
|
|
lfVector *c = create_lfvector(numverts);
|
|
lfVector *q = create_lfvector(numverts);
|
|
lfVector *s = create_lfvector(numverts);
|
|
float delta_new, delta_old, delta_target, alpha;
|
|
|
|
cp_lfvector(ldV, z, numverts);
|
|
|
|
/* d0 = filter(B)^T * P * filter(B) */
|
|
cp_lfvector(fB, lB, numverts);
|
|
filter(fB, S);
|
|
delta_target = conjgrad_epsilon*conjgrad_epsilon * dot_lfvector(fB, fB, numverts);
|
|
|
|
/* r = filter(B - A * dV) */
|
|
mul_bfmatrix_lfvector(AdV, lA, ldV);
|
|
sub_lfvector_lfvector(r, lB, AdV, numverts);
|
|
filter(r, S);
|
|
|
|
/* c = filter(P^-1 * r) */
|
|
cp_lfvector(c, r, numverts);
|
|
filter(c, S);
|
|
|
|
/* delta = r^T * c */
|
|
delta_new = dot_lfvector(r, c, numverts);
|
|
|
|
#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT
|
|
printf("==== A ====\n");
|
|
print_bfmatrix(lA);
|
|
printf("==== z ====\n");
|
|
print_lvector(z, numverts);
|
|
printf("==== B ====\n");
|
|
print_lvector(lB, numverts);
|
|
printf("==== S ====\n");
|
|
print_bfmatrix(S);
|
|
#endif
|
|
|
|
while (delta_new > delta_target && conjgrad_loopcount < conjgrad_looplimit) {
|
|
mul_bfmatrix_lfvector(q, lA, c);
|
|
filter(q, S);
|
|
|
|
alpha = delta_new / dot_lfvector(c, q, numverts);
|
|
|
|
add_lfvector_lfvectorS(ldV, ldV, c, alpha, numverts);
|
|
|
|
add_lfvector_lfvectorS(r, r, q, -alpha, numverts);
|
|
|
|
/* s = P^-1 * r */
|
|
cp_lfvector(s, r, numverts);
|
|
delta_old = delta_new;
|
|
delta_new = dot_lfvector(r, s, numverts);
|
|
|
|
add_lfvector_lfvectorS(c, s, c, delta_new / delta_old, numverts);
|
|
filter(c, S);
|
|
|
|
conjgrad_loopcount++;
|
|
}
|
|
|
|
#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT
|
|
printf("==== dV ====\n");
|
|
print_lvector(ldV, numverts);
|
|
printf("========\n");
|
|
#endif
|
|
|
|
del_lfvector(fB);
|
|
del_lfvector(AdV);
|
|
del_lfvector(r);
|
|
del_lfvector(c);
|
|
del_lfvector(q);
|
|
del_lfvector(s);
|
|
// printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount);
|
|
|
|
return conjgrad_loopcount < conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable
|
|
}
|
|
|
|
#if 0
|
|
// block diagonalizer
|
|
DO_INLINE void BuildPPinv(fmatrix3x3 *lA, fmatrix3x3 *P, fmatrix3x3 *Pinv)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
// Take only the diagonal blocks of A
|
|
// #pragma omp parallel for private(i) if (lA[0].vcount > CLOTH_OPENMP_LIMIT)
|
|
for (i = 0; i<lA[0].vcount; i++) {
|
|
// block diagonalizer
|
|
cp_fmatrix(P[i].m, lA[i].m);
|
|
inverse_fmatrix(Pinv[i].m, P[i].m);
|
|
|
|
}
|
|
}
|
|
/*
|
|
// version 1.3
|
|
static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv)
|
|
{
|
|
unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100;
|
|
float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0;
|
|
float conjgrad_epsilon=0.0001; // 0.2 is dt for steps=5
|
|
lfVector *r = create_lfvector(numverts);
|
|
lfVector *p = create_lfvector(numverts);
|
|
lfVector *s = create_lfvector(numverts);
|
|
lfVector *h = create_lfvector(numverts);
|
|
|
|
BuildPPinv(lA, P, Pinv);
|
|
|
|
filter(dv, S);
|
|
add_lfvector_lfvector(dv, dv, z, numverts);
|
|
|
|
mul_bfmatrix_lfvector(r, lA, dv);
|
|
sub_lfvector_lfvector(r, lB, r, numverts);
|
|
filter(r, S);
|
|
|
|
mul_prevfmatrix_lfvector(p, Pinv, r);
|
|
filter(p, S);
|
|
|
|
deltaNew = dot_lfvector(r, p, numverts);
|
|
|
|
delta0 = deltaNew * sqrt(conjgrad_epsilon);
|
|
|
|
// itstart();
|
|
|
|
while ((deltaNew > delta0) && (iterations < conjgrad_looplimit))
|
|
{
|
|
iterations++;
|
|
|
|
mul_bfmatrix_lfvector(s, lA, p);
|
|
filter(s, S);
|
|
|
|
alpha = deltaNew / dot_lfvector(p, s, numverts);
|
|
|
|
add_lfvector_lfvectorS(dv, dv, p, alpha, numverts);
|
|
|
|
add_lfvector_lfvectorS(r, r, s, -alpha, numverts);
|
|
|
|
mul_prevfmatrix_lfvector(h, Pinv, r);
|
|
filter(h, S);
|
|
|
|
deltaOld = deltaNew;
|
|
|
|
deltaNew = dot_lfvector(r, h, numverts);
|
|
|
|
add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts);
|
|
|
|
filter(p, S);
|
|
|
|
}
|
|
|
|
// itend();
|
|
// printf("cg_filtered_pre time: %f\n", (float)itval());
|
|
|
|
del_lfvector(h);
|
|
del_lfvector(s);
|
|
del_lfvector(p);
|
|
del_lfvector(r);
|
|
|
|
printf("iterations: %d\n", iterations);
|
|
|
|
return iterations<conjgrad_looplimit;
|
|
}
|
|
*/
|
|
// version 1.4
|
|
static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv, fmatrix3x3 *bigI)
|
|
{
|
|
unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100;
|
|
float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0, tol = 0;
|
|
lfVector *r = create_lfvector(numverts);
|
|
lfVector *p = create_lfvector(numverts);
|
|
lfVector *s = create_lfvector(numverts);
|
|
lfVector *h = create_lfvector(numverts);
|
|
lfVector *bhat = create_lfvector(numverts);
|
|
lfVector *btemp = create_lfvector(numverts);
|
|
|
|
BuildPPinv(lA, P, Pinv);
|
|
|
|
initdiag_bfmatrix(bigI, I);
|
|
sub_bfmatrix_Smatrix(bigI, bigI, S);
|
|
|
|
// x = Sx_0+(I-S)z
|
|
filter(dv, S);
|
|
add_lfvector_lfvector(dv, dv, z, numverts);
|
|
|
|
// b_hat = S(b-A(I-S)z)
|
|
mul_bfmatrix_lfvector(r, lA, z);
|
|
mul_bfmatrix_lfvector(bhat, bigI, r);
|
|
sub_lfvector_lfvector(bhat, lB, bhat, numverts);
|
|
|
|
// r = S(b-Ax)
|
|
mul_bfmatrix_lfvector(r, lA, dv);
|
|
sub_lfvector_lfvector(r, lB, r, numverts);
|
|
filter(r, S);
|
|
|
|
// p = SP^-1r
|
|
mul_prevfmatrix_lfvector(p, Pinv, r);
|
|
filter(p, S);
|
|
|
|
// delta0 = bhat^TP^-1bhat
|
|
mul_prevfmatrix_lfvector(btemp, Pinv, bhat);
|
|
delta0 = dot_lfvector(bhat, btemp, numverts);
|
|
|
|
// deltaNew = r^TP
|
|
deltaNew = dot_lfvector(r, p, numverts);
|
|
|
|
/*
|
|
filter(dv, S);
|
|
add_lfvector_lfvector(dv, dv, z, numverts);
|
|
|
|
mul_bfmatrix_lfvector(r, lA, dv);
|
|
sub_lfvector_lfvector(r, lB, r, numverts);
|
|
filter(r, S);
|
|
|
|
mul_prevfmatrix_lfvector(p, Pinv, r);
|
|
filter(p, S);
|
|
|
|
deltaNew = dot_lfvector(r, p, numverts);
|
|
|
|
delta0 = deltaNew * sqrt(conjgrad_epsilon);
|
|
*/
|
|
|
|
// itstart();
|
|
|
|
tol = (0.01*0.2);
|
|
|
|
while ((deltaNew > delta0*tol*tol) && (iterations < conjgrad_looplimit))
|
|
{
|
|
iterations++;
|
|
|
|
mul_bfmatrix_lfvector(s, lA, p);
|
|
filter(s, S);
|
|
|
|
alpha = deltaNew / dot_lfvector(p, s, numverts);
|
|
|
|
add_lfvector_lfvectorS(dv, dv, p, alpha, numverts);
|
|
|
|
add_lfvector_lfvectorS(r, r, s, -alpha, numverts);
|
|
|
|
mul_prevfmatrix_lfvector(h, Pinv, r);
|
|
filter(h, S);
|
|
|
|
deltaOld = deltaNew;
|
|
|
|
deltaNew = dot_lfvector(r, h, numverts);
|
|
|
|
add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts);
|
|
|
|
filter(p, S);
|
|
|
|
}
|
|
|
|
// itend();
|
|
// printf("cg_filtered_pre time: %f\n", (float)itval());
|
|
|
|
del_lfvector(btemp);
|
|
del_lfvector(bhat);
|
|
del_lfvector(h);
|
|
del_lfvector(s);
|
|
del_lfvector(p);
|
|
del_lfvector(r);
|
|
|
|
// printf("iterations: %d\n", iterations);
|
|
|
|
return iterations<conjgrad_looplimit;
|
|
}
|
|
#endif
|
|
|
|
bool BPH_mass_spring_solve(Implicit_Data *data, float dt)
|
|
{
|
|
unsigned int numverts = data->dFdV[0].vcount;
|
|
bool ok;
|
|
|
|
lfVector *dFdXmV = create_lfvector(numverts);
|
|
zero_lfvector(data->dV, numverts);
|
|
|
|
cp_bfmatrix(data->A, data->M);
|
|
|
|
subadd_bfmatrixS_bfmatrixS(data->A, data->dFdV, dt, data->dFdX, (dt*dt));
|
|
|
|
mul_bfmatrix_lfvector(dFdXmV, data->dFdX, data->V);
|
|
|
|
add_lfvectorS_lfvectorS(data->B, data->F, dt, dFdXmV, (dt*dt), numverts);
|
|
|
|
// itstart();
|
|
|
|
ok = cg_filtered(data->dV, data->A, data->B, data->z, data->S); /* conjugate gradient algorithm to solve Ax=b */
|
|
// cg_filtered_pre(id->dV, id->A, id->B, id->z, id->S, id->P, id->Pinv, id->bigI);
|
|
|
|
// itend();
|
|
// printf("cg_filtered calc time: %f\n", (float)itval());
|
|
|
|
// advance velocities
|
|
add_lfvector_lfvector(data->Vnew, data->V, data->dV, numverts);
|
|
// advance positions
|
|
add_lfvector_lfvectorS(data->Xnew, data->X, data->Vnew, dt, numverts);
|
|
|
|
del_lfvector(dFdXmV);
|
|
|
|
return ok;
|
|
}
|
|
|
|
void BPH_mass_spring_apply_result(Implicit_Data *data)
|
|
{
|
|
int numverts = data->M[0].vcount;
|
|
cp_lfvector(data->X, data->Xnew, numverts);
|
|
cp_lfvector(data->V, data->Vnew, numverts);
|
|
}
|
|
|
|
void BPH_mass_spring_set_root_motion(Implicit_Data *data, int index, const float loc[3], const float vel[3], float rot[3][3], const float angvel[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
|
|
#ifdef CLOTH_ROOT_FRAME
|
|
copy_v3_v3(root->loc, loc);
|
|
copy_v3_v3(root->vel, vel);
|
|
copy_m3_m3(root->rot, rot);
|
|
copy_v3_v3(root->omega, angvel);
|
|
/* XXX root frame acceleration ignored for now */
|
|
zero_v3(root->acc);
|
|
zero_v3(root->domega_dt);
|
|
#else
|
|
zero_v3(root->loc);
|
|
zero_v3(root->vel);
|
|
unit_m3(root->rot);
|
|
zero_v3(root->omega);
|
|
zero_v3(root->acc);
|
|
zero_v3(root->domega_dt);
|
|
(void)loc;
|
|
(void)vel;
|
|
(void)rot;
|
|
(void)angvel;
|
|
#endif
|
|
}
|
|
|
|
void BPH_mass_spring_set_motion_state(Implicit_Data *data, int index, const float x[3], const float v[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
loc_world_to_root(data->X[index], x, root);
|
|
vel_world_to_root(data->V[index], data->X[index], v, root);
|
|
}
|
|
|
|
void BPH_mass_spring_set_position(Implicit_Data *data, int index, const float x[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
loc_world_to_root(data->X[index], x, root);
|
|
}
|
|
|
|
void BPH_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
vel_world_to_root(data->V[index], data->X[index], v, root);
|
|
}
|
|
|
|
void BPH_mass_spring_get_motion_state(struct Implicit_Data *data, int index, float x[3], float v[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
if (x) loc_root_to_world(x, data->X[index], root);
|
|
if (v) vel_root_to_world(v, data->X[index], data->V[index], root);
|
|
}
|
|
|
|
void BPH_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass)
|
|
{
|
|
unit_m3(data->M[index].m);
|
|
mul_m3_fl(data->M[index].m, mass);
|
|
}
|
|
|
|
int BPH_mass_spring_init_spring(Implicit_Data *data, int index, int v1, int v2)
|
|
{
|
|
int s = data->M[0].vcount + index; /* index from array start */
|
|
|
|
init_fmatrix(data->bigI + s, v1, v2);
|
|
init_fmatrix(data->M + s, v1, v2);
|
|
init_fmatrix(data->dFdX + s, v1, v2);
|
|
init_fmatrix(data->dFdV + s, v1, v2);
|
|
init_fmatrix(data->A + s, v1, v2);
|
|
// init_fmatrix(data->S + s, v1, v2); // has no off-diagonal spring entries
|
|
init_fmatrix(data->P + s, v1, v2);
|
|
init_fmatrix(data->Pinv + s, v1, v2);
|
|
|
|
return s;
|
|
}
|
|
|
|
void BPH_mass_spring_clear_constraints(Implicit_Data *data)
|
|
{
|
|
int i, numverts = data->S[0].vcount;
|
|
for (i = 0; i < numverts; ++i) {
|
|
unit_m3(data->S[i].m);
|
|
zero_v3(data->z[i]);
|
|
}
|
|
}
|
|
|
|
void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
|
|
zero_m3(data->S[index].m);
|
|
|
|
copy_v3_v3(data->z[index], dV);
|
|
mul_transposed_m3_v3(root->rot, data->z[index]);
|
|
}
|
|
|
|
void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
float m[3][3], p[3], q[3], u[3], cmat[3][3];
|
|
|
|
copy_v3_v3(p, c1);
|
|
mul_transposed_m3_v3(root->rot, p);
|
|
mul_fvectorT_fvector(cmat, p, p);
|
|
sub_m3_m3m3(m, I, cmat);
|
|
|
|
copy_v3_v3(q, c2);
|
|
mul_transposed_m3_v3(root->rot, q);
|
|
mul_fvectorT_fvector(cmat, q, q);
|
|
sub_m3_m3m3(m, m, cmat);
|
|
|
|
/* XXX not sure but multiplication should work here */
|
|
copy_m3_m3(data->S[index].m, m);
|
|
// mul_m3_m3m3(data->S[index].m, data->S[index].m, m);
|
|
|
|
copy_v3_v3(u, dV);
|
|
mul_transposed_m3_v3(root->rot, u);
|
|
add_v3_v3(data->z[index], u);
|
|
}
|
|
|
|
void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data, int index, const float c1[3], const float dV[3])
|
|
{
|
|
RootTransform *root = &data->root[index];
|
|
float m[3][3], p[3], u[3], cmat[3][3];
|
|
|
|
copy_v3_v3(p, c1);
|
|
mul_transposed_m3_v3(root->rot, p);
|
|
mul_fvectorT_fvector(cmat, p, p);
|
|
sub_m3_m3m3(m, I, cmat);
|
|
|
|
copy_m3_m3(data->S[index].m, m);
|
|
// mul_m3_m3m3(data->S[index].m, data->S[index].m, m);
|
|
|
|
copy_v3_v3(u, dV);
|
|
mul_transposed_m3_v3(root->rot, u);
|
|
add_v3_v3(data->z[index], u);
|
|
}
|
|
|
|
void BPH_mass_spring_force_clear(Implicit_Data *data)
|
|
{
|
|
int numverts = data->M[0].vcount;
|
|
zero_lfvector(data->F, numverts);
|
|
init_bfmatrix(data->dFdX, ZERO);
|
|
init_bfmatrix(data->dFdV, ZERO);
|
|
}
|
|
|
|
void BPH_mass_spring_force_gravity(Implicit_Data *data, const float g[3])
|
|
{
|
|
int i, numverts = data->M[0].vcount;
|
|
/* multiply F with mass matrix
|
|
* force = mass * acceleration (in this case: gravity)
|
|
*/
|
|
for (i = 0; i < numverts; i++) {
|
|
float f[3];
|
|
acc_world_to_root(f, data->X[i], data->V[i], g, &data->root[i]);
|
|
mul_m3_v3(data->M[i].m, f);
|
|
|
|
add_v3_v3(data->F[i], f);
|
|
}
|
|
}
|
|
|
|
void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
|
|
{
|
|
int i, numverts = data->M[0].vcount;
|
|
for (i = 0; i < numverts; i++) {
|
|
float tmp[3][3];
|
|
|
|
/* NB: uses root space velocity, no need to transform */
|
|
madd_v3_v3fl(data->F[i], data->V[i], -drag);
|
|
|
|
copy_m3_m3(tmp, I);
|
|
mul_m3_fl(tmp, -drag);
|
|
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, tmp);
|
|
}
|
|
}
|
|
|
|
static float calc_nor_area_tri(float nor[3], const float v1[3], const float v2[3], const float v3[3])
|
|
{
|
|
float n1[3], n2[3];
|
|
|
|
sub_v3_v3v3(n1, v1, v2);
|
|
sub_v3_v3v3(n2, v2, v3);
|
|
|
|
cross_v3_v3v3(nor, n1, n2);
|
|
return normalize_v3(nor);
|
|
}
|
|
|
|
static float calc_nor_area_quad(float nor[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3])
|
|
{
|
|
float n1[3], n2[3];
|
|
|
|
sub_v3_v3v3(n1, v1, v3);
|
|
sub_v3_v3v3(n2, v2, v4);
|
|
|
|
cross_v3_v3v3(nor, n1, n2);
|
|
return normalize_v3(nor);
|
|
}
|
|
|
|
/* XXX does not support force jacobians yet, since the effector system does not provide them either */
|
|
void BPH_mass_spring_force_face_wind(Implicit_Data *data, int v1, int v2, int v3, int v4, const float (*winvec)[3])
|
|
{
|
|
const float effector_scale = 0.02f;
|
|
float nor[3], area;
|
|
float factor;
|
|
|
|
// calculate face normal and area
|
|
if (v4) {
|
|
area = calc_nor_area_quad(nor, data->X[v1], data->X[v2], data->X[v3], data->X[v4]);
|
|
factor = effector_scale * area * 0.25f;
|
|
}
|
|
else {
|
|
area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
|
|
factor = effector_scale * area / 3.0f;
|
|
}
|
|
|
|
madd_v3_v3fl(data->F[v1], nor, factor * dot_v3v3(winvec[v1], nor));
|
|
madd_v3_v3fl(data->F[v2], nor, factor * dot_v3v3(winvec[v2], nor));
|
|
madd_v3_v3fl(data->F[v3], nor, factor * dot_v3v3(winvec[v3], nor));
|
|
if (v4)
|
|
madd_v3_v3fl(data->F[v4], nor, factor * dot_v3v3(winvec[v4], nor));
|
|
|
|
|
|
}
|
|
|
|
void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, const float (*winvec)[3])
|
|
{
|
|
const float effector_scale = 0.01;
|
|
const float *win1 = winvec[v1];
|
|
const float *win2 = winvec[v2];
|
|
float win_ortho[3], dir[3], length;
|
|
|
|
sub_v3_v3v3(dir, data->X[v1], data->X[v2]);
|
|
length = normalize_v3(dir);
|
|
|
|
madd_v3_v3v3fl(win_ortho, win1, dir, -dot_v3v3(win1, dir));
|
|
madd_v3_v3fl(data->F[v1], win_ortho, effector_scale * length);
|
|
|
|
madd_v3_v3v3fl(win_ortho, win2, dir, -dot_v3v3(win2, dir));
|
|
madd_v3_v3fl(data->F[v2], win_ortho, effector_scale * length);
|
|
}
|
|
|
|
BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, float L, float k)
|
|
{
|
|
// dir is unit length direction, rest is spring's restlength, k is spring constant.
|
|
//return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k;
|
|
outerproduct(to, dir, dir);
|
|
sub_m3_m3m3(to, I, to);
|
|
|
|
mul_m3_fl(to, (L/length));
|
|
sub_m3_m3m3(to, to, I);
|
|
mul_m3_fl(to, k);
|
|
}
|
|
|
|
/* unused */
|
|
#if 0
|
|
BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping)
|
|
{
|
|
// inner spring damping vel is the relative velocity of the endpoints.
|
|
// return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest)));
|
|
mul_fvectorT_fvector(to, dir, dir);
|
|
sub_fmatrix_fmatrix(to, I, to);
|
|
mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel)/MAX2(length, rest))));
|
|
}
|
|
#endif
|
|
|
|
BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping)
|
|
{
|
|
// derivative of force wrt velocity
|
|
outerproduct(to, dir, dir);
|
|
mul_m3_fl(to, -damping);
|
|
}
|
|
|
|
BLI_INLINE float fb(float length, float L)
|
|
{
|
|
float x = length / L;
|
|
return (-11.541f * powf(x, 4) + 34.193f * powf(x, 3) - 39.083f * powf(x, 2) + 23.116f * x - 9.713f);
|
|
}
|
|
|
|
BLI_INLINE float fbderiv(float length, float L)
|
|
{
|
|
float x = length/L;
|
|
|
|
return (-46.164f * powf(x, 3) + 102.579f * powf(x, 2) - 78.166f * x + 23.116f);
|
|
}
|
|
|
|
BLI_INLINE float fbstar(float length, float L, float kb, float cb)
|
|
{
|
|
float tempfb_fl = kb * fb(length, L);
|
|
float fbstar_fl = cb * (length - L);
|
|
|
|
if (tempfb_fl < fbstar_fl)
|
|
return fbstar_fl;
|
|
else
|
|
return tempfb_fl;
|
|
}
|
|
|
|
// function to calculae bending spring force (taken from Choi & Co)
|
|
BLI_INLINE float fbstar_jacobi(float length, float L, float kb, float cb)
|
|
{
|
|
float tempfb_fl = kb * fb(length, L);
|
|
float fbstar_fl = cb * (length - L);
|
|
|
|
if (tempfb_fl < fbstar_fl) {
|
|
return -cb;
|
|
}
|
|
else {
|
|
return -kb * fbderiv(length, L);
|
|
}
|
|
}
|
|
|
|
/* calculate elonglation */
|
|
BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[3], float r_dir[3], float *r_length, float r_vel[3])
|
|
{
|
|
sub_v3_v3v3(r_extent, data->X[j], data->X[i]);
|
|
sub_v3_v3v3(r_vel, data->V[j], data->V[i]);
|
|
*r_length = len_v3(r_extent);
|
|
|
|
if (*r_length > ALMOST_ZERO) {
|
|
/*
|
|
if (length>L) {
|
|
if ((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) &&
|
|
( ((length-L)*100.0f/L) > clmd->sim_parms->maxspringlen )) {
|
|
// cut spring!
|
|
s->flags |= CSPRING_FLAG_DEACTIVATE;
|
|
return false;
|
|
}
|
|
}
|
|
*/
|
|
mul_v3_v3fl(r_dir, r_extent, 1.0f/(*r_length));
|
|
}
|
|
else {
|
|
zero_v3(r_dir);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BLI_INLINE void apply_spring(Implicit_Data *data, int i, int j, int spring_index, const float f[3], float dfdx[3][3], float dfdv[3][3])
|
|
{
|
|
add_v3_v3(data->F[i], f);
|
|
sub_v3_v3(data->F[j], f);
|
|
|
|
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx);
|
|
add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfdx);
|
|
sub_m3_m3m3(data->dFdX[spring_index].m, data->dFdX[spring_index].m, dfdx);
|
|
|
|
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv);
|
|
add_m3_m3m3(data->dFdV[j].m, data->dFdV[j].m, dfdv);
|
|
sub_m3_m3m3(data->dFdV[spring_index].m, data->dFdV[spring_index].m, dfdv);
|
|
}
|
|
|
|
bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, int spring_index, float restlen,
|
|
float stiffness, float damping, bool no_compress, float clamp_force,
|
|
float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3])
|
|
{
|
|
float extent[3], length, dir[3], vel[3];
|
|
|
|
// calculate elonglation
|
|
spring_length(data, i, j, extent, dir, &length, vel);
|
|
|
|
if (length > restlen || no_compress) {
|
|
float stretch_force, f[3], dfdx[3][3], dfdv[3][3];
|
|
|
|
stretch_force = stiffness * (length - restlen);
|
|
if (clamp_force > 0.0f && stretch_force > clamp_force) {
|
|
stretch_force = clamp_force;
|
|
}
|
|
mul_v3_v3fl(f, dir, stretch_force);
|
|
|
|
// Ascher & Boxman, p.21: Damping only during elonglation
|
|
// something wrong with it...
|
|
madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir));
|
|
|
|
dfdx_spring(dfdx, dir, length, restlen, stiffness);
|
|
dfdv_damp(dfdv, dir, damping);
|
|
|
|
apply_spring(data, i, j, spring_index, f, dfdx, dfdv);
|
|
|
|
if (r_f) copy_v3_v3(r_f, f);
|
|
if (r_dfdx) copy_m3_m3(r_dfdx, dfdx);
|
|
if (r_dfdv) copy_m3_m3(r_dfdv, dfdv);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
if (r_f) zero_v3(r_f);
|
|
if (r_dfdx) zero_m3(r_dfdx);
|
|
if (r_dfdv) zero_m3(r_dfdv);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* See "Stable but Responsive Cloth" (Choi, Ko 2005) */
|
|
bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, int spring_index, float restlen,
|
|
float kb, float cb,
|
|
float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3])
|
|
{
|
|
float extent[3], length, dir[3], vel[3];
|
|
|
|
// calculate elonglation
|
|
spring_length(data, i, j, extent, dir, &length, vel);
|
|
|
|
if (length < restlen) {
|
|
float f[3], dfdx[3][3], dfdv[3][3];
|
|
|
|
mul_v3_v3fl(f, dir, fbstar(length, restlen, kb, cb));
|
|
|
|
outerproduct(dfdx, dir, dir);
|
|
mul_m3_fl(dfdx, fbstar_jacobi(length, restlen, kb, cb));
|
|
|
|
/* XXX damping not supported */
|
|
zero_m3(dfdv);
|
|
|
|
apply_spring(data, i, j, spring_index, f, dfdx, dfdv);
|
|
|
|
if (r_f) copy_v3_v3(r_f, f);
|
|
if (r_dfdx) copy_m3_m3(r_dfdx, dfdx);
|
|
if (r_dfdv) copy_m3_m3(r_dfdv, dfdv);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
if (r_f) zero_v3(r_f);
|
|
if (r_dfdx) zero_m3(r_dfdx);
|
|
if (r_dfdv) zero_m3(r_dfdv);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, int UNUSED(spring_index), const float goal_x[3], const float goal_v[3],
|
|
float stiffness, float damping,
|
|
float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3])
|
|
{
|
|
float root_goal_x[3], root_goal_v[3], extent[3], length, dir[3], vel[3];
|
|
float f[3], dfdx[3][3], dfdv[3][3];
|
|
|
|
/* goal is in world space */
|
|
loc_world_to_root(root_goal_x, goal_x, &data->root[i]);
|
|
vel_world_to_root(root_goal_v, root_goal_x, goal_v, &data->root[i]);
|
|
|
|
sub_v3_v3v3(extent, root_goal_x, data->X[i]);
|
|
sub_v3_v3v3(vel, root_goal_v, data->V[i]);
|
|
length = normalize_v3_v3(dir, extent);
|
|
|
|
if (length > ALMOST_ZERO) {
|
|
mul_v3_v3fl(f, dir, stiffness * length);
|
|
|
|
// Ascher & Boxman, p.21: Damping only during elonglation
|
|
// something wrong with it...
|
|
madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir));
|
|
|
|
dfdx_spring(dfdx, dir, length, 0.0f, stiffness);
|
|
dfdv_damp(dfdv, dir, damping);
|
|
|
|
add_v3_v3(data->F[i], f);
|
|
|
|
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx);
|
|
|
|
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv);
|
|
|
|
if (r_f) copy_v3_v3(r_f, f);
|
|
if (r_dfdx) copy_m3_m3(r_dfdx, dfdx);
|
|
if (r_dfdv) copy_m3_m3(r_dfdv, dfdv);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
if (r_f) zero_v3(r_f);
|
|
if (r_dfdx) zero_m3(r_dfdx);
|
|
if (r_dfdv) zero_m3(r_dfdv);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif /* IMPLICIT_SOLVER_BLENDER */
|