1
1

Added unit tests for testing different phases of collision

This commit is contained in:
2022-12-10 15:21:58 +00:00
parent 2438ada341
commit ef42d29b8a
6 changed files with 4963 additions and 3 deletions

View File

@@ -0,0 +1,86 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright Blender Foundation. All rights reserved. */
#pragma once
/** \file
* \ingroup bke
*/
#ifdef __cplusplus
extern "C" {
#endif
struct Depsgraph;
struct Object;
struct Scene;
struct SoftBody;
typedef struct BodyPoint {
float origS[3], origE[3], origT[3], pos[3], vec[3], force[3];
float goal;
float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */
float impdv[3], impdx[3];
int nofsprings;
int *springs;
float choke, choke2, frozen;
float colball;
short loc_flag; /* reserved by locale module specific states */
// char octantflag;
float mass;
float springweight;
} BodyPoint;
/**
* Allocates and initializes general main data.
*/
extern struct SoftBody *sbNew(void);
/**
* Frees internal data and soft-body itself.
*/
extern void sbFree(struct Object *ob);
/**
* Frees simulation data to reset simulation.
*/
extern void sbFreeSimulation(struct SoftBody *sb);
/**
* Do one simulation step, reading and writing vertex locs from given array.
* */
extern void sbObjectStep(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
float cfra,
float (*vertexCos)[3],
int numVerts);
/**
* Makes totally fresh start situation, resets time.
*/
extern void sbObjectToSoftbody(struct Object *ob);
/**
* Soft-body global visible functions.
* Links the soft-body module to a 'test for Interrupt' function, pass NULL to clear the callback.
*/
extern void sbSetInterruptCallBack(int (*f)(void));
/**
* A precise position vector denoting the motion of the center of mass give a rotation/scale matrix
* using averaging method, that's why estimate and not calculate see: this is kind of reverse
* engineering: having to states of a point cloud and recover what happened our advantage here we
* know the identity of the vertex there are others methods giving other results.
*
* \param ob: Any object that can do soft-body e.g. mesh, lattice, curve.
* \param lloc: Output of the calculated location (or NULL).
* \param lrot: Output of the calculated rotation (or NULL).
* \param lscale: Output for the calculated scale (or NULL).
*
* For velocity & 2nd order stuff see: #vcloud_estimate_transform_v3.
*/
extern void SB_estimate_transform(Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2004-2005 Blender Foundation. All rights reserved. */
/** \file
* \ingroup DNA
*/
#pragma once
#include "DNA_defs.h"
#include "DNA_listBase.h"
#ifdef __cplusplus
extern "C" {
#endif
struct BodySpring;
/** #PartDeflect.forcefield: Effector Fields types. */
typedef enum ePFieldType {
/** (this is used for general effector weight). */
PFIELD_NULL = 0,
/** Force away/towards a point depending on force strength. */
PFIELD_FORCE = 1,
/** Force around the effector normal. */
PFIELD_VORTEX = 2,
/** Force from the cross product of effector normal and point velocity. */
PFIELD_MAGNET = 3,
/** Force away and towards a point depending which side of the effector normal the point is. */
PFIELD_WIND = 4,
/** Force along curve for dynamics, a shaping curve for hair paths. */
PFIELD_GUIDE = 5,
/** Force based on texture values calculated at point coordinates. */
PFIELD_TEXTURE = 6,
/** Force of a harmonic (damped) oscillator. */
PFIELD_HARMONIC = 7,
/** Force away/towards a point depending on point charge. */
PFIELD_CHARGE = 8,
/** Force due to a Lennard-Jones potential. */
PFIELD_LENNARDJ = 9,
/** Defines predator / goal for boids. */
PFIELD_BOID = 10,
/** Force defined by BLI_noise_generic_turbulence. */
PFIELD_TURBULENCE = 11,
/** Linear & quadratic drag. */
PFIELD_DRAG = 12,
/** Force based on fluid simulation velocities. */
PFIELD_FLUIDFLOW = 13,
/* Keep last. */
NUM_PFIELD_TYPES,
} ePFieldType;
typedef struct PartDeflect {
/** General settings flag. */
int flag;
/** Deflection flag - does mesh deflect particles. */
short deflect;
/** Force field type, do the vertices attract / repel particles? */
short forcefield;
/** Fall-off type. */
short falloff;
/** Point, plane or surface. */
short shape;
/** Texture effector. */
short tex_mode;
/** For curve guide. */
short kink, kink_axis;
short zdir;
/* Main effector values */
/** The strength of the force (+ or - ). */
float f_strength;
/** Damping ratio of the harmonic effector. */
float f_damp;
/**
* How much force is converted into "air flow", i.e.
* force used as the velocity of surrounding medium. */
float f_flow;
/** How much force is reduced when acting parallel to a surface, e.g. cloth. */
float f_wind_factor;
char _pad0[4];
/** Noise size for noise effector, restlength for harmonic effector. */
float f_size;
/* fall-off */
/** The power law - real gravitation is 2 (square). */
float f_power;
/** If indicated, use this maximum. */
float maxdist;
/** If indicated, use this minimum. */
float mindist;
/** Radial fall-off power. */
float f_power_r;
/** Radial versions of above. */
float maxrad;
float minrad;
/* particle collisions */
/** Damping factor for particle deflection. */
float pdef_damp;
/** Random element of damping for deflection. */
float pdef_rdamp;
/** Chance of particle passing through mesh. */
float pdef_perm;
/** Friction factor for particle deflection. */
float pdef_frict;
/** Random element of friction for deflection. */
float pdef_rfrict;
/** Surface particle stickiness. */
float pdef_stickness;
/** Used for forces. */
float absorption;
/* softbody collisions */
/** Damping factor for softbody deflection. */
float pdef_sbdamp;
/** Inner face thickness for softbody deflection. */
float pdef_sbift;
/** Outer face thickness for softbody deflection. */
float pdef_sboft;
/* guide curve, same as for particle child effects */
float clump_fac, clump_pow;
float kink_freq, kink_shape, kink_amp, free_end;
/* texture effector */
/** Used for calculating partial derivatives. */
float tex_nabla;
/** Texture of the texture effector. */
struct Tex *tex;
/* effector noise */
/** Random noise generator for e.g. wind. */
struct RNG *rng;
/** Noise of force. */
float f_noise;
/** Noise random seed. */
int seed;
/* Display Size */
/** Runtime only : start of the curve or draw scale. */
float drawvec1[4];
/** Runtime only : end of the curve. */
float drawvec2[4];
/** Runtime only. */
float drawvec_falloff_min[3];
char _pad1[4];
/** Runtime only. */
float drawvec_falloff_max[3];
char _pad2[4];
/** Force source object. */
struct Object *f_source;
/** Friction of cloth collisions. */
float pdef_cfrict;
char _pad[4];
} PartDeflect;
typedef struct EffectorWeights {
/** Only use effectors from this group of objects. */
struct Collection *group;
/** Effector type specific weights. */
float weight[14];
float global_gravity;
short flag;
char _pad[2];
} EffectorWeights;
/* EffectorWeights->flag */
#define EFF_WEIGHT_DO_HAIR 1
typedef struct SBVertex {
float vec[4];
} SBVertex;
/* Container for data that is shared among CoW copies.
*
* This is placed in a separate struct so that values can be changed
* without having to update all CoW copies. */
typedef struct SoftBody_Shared {
struct PointCache *pointcache;
struct ListBase ptcaches;
} SoftBody_Shared;
typedef struct SoftBody {
/* dynamic data */
int totpoint, totspring;
/** Not saved in file. */
struct BodyPoint *bpoint;
/** Not saved in file. */
struct BodySpring *bspring;
char _pad;
char msg_lock;
short msg_value;
/* part of UI: */
/* general options */
/** Softbody mass of *vertex*. */
float nodemass;
/**
* Along with it introduce mass painting
* starting to fix old bug .. nastiness that VG are indexes
* rather find them by name tag to find it -> jow20090613.
* MAX_VGROUP_NAME */
char namedVG_Mass[64];
/** Softbody amount of gravitation to apply. */
float grav;
/** Friction to env. */
float mediafrict;
/** Error limit for ODE solver. */
float rklimit;
/** User control over simulation speed. */
float physics_speed;
/* goal */
/** Softbody goal springs. */
float goalspring;
/** Softbody goal springs friction. */
float goalfrict;
/** Quick limits for goal. */
float mingoal;
float maxgoal;
/** Default goal for vertices without vgroup. */
float defgoal;
/** Index starting at 1. */
short vertgroup;
/**
* Starting to fix old bug .. nastiness that VG are indexes
* rather find them by name tag to find it -> jow20090613.
* MAX_VGROUP_NAME */
char namedVG_Softgoal[64];
short fuzzyness;
/* springs */
/** Softbody inner springs. */
float inspring;
/** Softbody inner springs friction. */
float infrict;
/**
* Along with it introduce Spring_K painting
* starting to fix old bug .. nastiness that VG are indexes
* rather find them by name tag to find it -> jow20090613.
* MAX_VGROUP_NAME
*/
char namedVG_Spring_K[64];
/* baking */
char _pad1[6];
/** Local==1: use local coords for baking. */
char local, solverflags;
/* -- these must be kept for backwards compatibility -- */
/** Array of size totpointkey. */
SBVertex **keys;
/** If totpointkey != totpoint or totkey!- (efra-sfra)/interval -> free keys. */
int totpointkey, totkey;
/* ---------------------------------------------------- */
float secondspring;
/* Self collision. */
/** Fixed collision ball size if > 0. */
float colball;
/** Cooling down collision response. */
float balldamp;
/** Pressure the ball is loaded with. */
float ballstiff;
short sbc_mode;
short aeroedge;
short minloops;
short maxloops;
short choke;
short solver_ID;
short plastic;
short springpreload;
/** Scratchpad/cache on live time not saved in file. */
struct SBScratch *scratch;
float shearstiff;
float inpush;
struct SoftBody_Shared *shared;
/** Moved to SoftBody_Shared. */
struct PointCache *pointcache DNA_DEPRECATED;
/** Moved to SoftBody_Shared. */
struct ListBase ptcaches DNA_DEPRECATED;
struct Collection *collision_group;
struct EffectorWeights *effector_weights;
/* Reverse estimated object-matrix (run-time data, no need to store in the file). */
float lcom[3];
float lrot[3][3];
float lscale[3][3];
int last_frame;
} SoftBody;
/* pd->flag: various settings */
#define PFIELD_USEMAX (1 << 0)
// #define PDEFLE_DEFORM (1 << 1) /* UNUSED */
/** TODO: do_versions for below */
#define PFIELD_GUIDE_PATH_ADD (1 << 2)
/** used for do_versions */
#define PFIELD_PLANAR (1 << 3)
#define PDEFLE_KILL_PART (1 << 4)
/** used for do_versions */
#define PFIELD_POSZ (1 << 5)
#define PFIELD_TEX_OBJECT (1 << 6)
/** used for turbulence */
#define PFIELD_GLOBAL_CO (1 << 6)
#define PFIELD_TEX_2D (1 << 7)
/** used for harmonic force */
#define PFIELD_MULTIPLE_SPRINGS (1 << 7)
#define PFIELD_USEMIN (1 << 8)
#define PFIELD_USEMAXR (1 << 9)
#define PFIELD_USEMINR (1 << 10)
#define PFIELD_TEX_ROOTCO (1 << 11)
/** used for do_versions */
#define PFIELD_SURFACE (1 << 12)
#define PFIELD_VISIBILITY (1 << 13)
#define PFIELD_DO_LOCATION (1 << 14)
#define PFIELD_DO_ROTATION (1 << 15)
/** apply curve weights */
#define PFIELD_GUIDE_PATH_WEIGHT (1 << 16)
/** multiply smoke force by density */
#define PFIELD_SMOKE_DENSITY (1 << 17)
/** used for (simple) force */
#define PFIELD_GRAVITATION (1 << 18)
/** Enable cloth collision side detection based on normal. */
#define PFIELD_CLOTH_USE_CULLING (1 << 19)
/** Replace collision direction with collider normal. */
#define PFIELD_CLOTH_USE_NORMAL (1 << 20)
/* pd->falloff */
#define PFIELD_FALL_SPHERE 0
#define PFIELD_FALL_TUBE 1
#define PFIELD_FALL_CONE 2
/* pd->shape */
#define PFIELD_SHAPE_POINT 0
#define PFIELD_SHAPE_PLANE 1
#define PFIELD_SHAPE_SURFACE 2
#define PFIELD_SHAPE_POINTS 3
#define PFIELD_SHAPE_LINE 4
/* pd->tex_mode */
#define PFIELD_TEX_RGB 0
#define PFIELD_TEX_GRAD 1
#define PFIELD_TEX_CURL 2
/* pd->zdir */
#define PFIELD_Z_BOTH 0
#define PFIELD_Z_POS 1
#define PFIELD_Z_NEG 2
/* ob->softflag */
#define OB_SB_ENABLE 1 /* deprecated, use modifier */
#define OB_SB_GOAL 2
#define OB_SB_EDGES 4
#define OB_SB_QUADS 8
#define OB_SB_POSTDEF 16
// #define OB_SB_REDO 32
// #define OB_SB_BAKESET 64
// #define OB_SB_BAKEDO 128
// #define OB_SB_RESET 256
#define OB_SB_SELF 512
#define OB_SB_FACECOLL 1024
#define OB_SB_EDGECOLL 2048
/* #define OB_SB_COLLFINAL 4096 */ /* deprecated */
/* #define OB_SB_BIG_UI 8192 */ /* deprecated */
#define OB_SB_AERO_ANGLE 16384
/* sb->solverflags */
#define SBSO_MONITOR 1
#define SBSO_OLDERR 2
#define SBSO_ESTIMATEIPO 4
/* sb->sbc_mode */
#define SBC_MODE_MANUAL 0
#define SBC_MODE_AVG 1
#define SBC_MODE_MIN 2
#define SBC_MODE_MAX 3
#define SBC_MODE_AVGMINMAX 4
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,811 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2005 Blender Foundation. All rights reserved. */
/** \file
* \ingroup modifiers
*
* Weld modifier: Remove doubles.
*/
/* TODOs:
* - Review weight and vertex color interpolation.;
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_array.hh"
#include "BLI_index_range.hh"
#include "BLI_span.hh"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_math_vector.hh"
// #include "bmesh_construct.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "BLT_translation.h"
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "DEG_depsgraph.h"
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
#include "GEO_mesh_merge_by_distance.hh"
using blender::Array;
using blender::IndexMask;
using blender::Span;
using blender::Vector;
using namespace blender;
using namespace blender::math;
// using namespace std;
Vector<Vector<int>> tetFaces({{2,1,0}, {0,1,3}, {1,2,3}, {2,0,3}});
float directions[6][3] = {{1,0,0}, {-1,0,0}, {0,1,0}, {0,-1,0}, {0,0,1}, {0,0,-1}};
float inf = FLT_MAX;
float eps = 0.0f;
static float randomEps(){
float eps = 0.0001 ;
return -eps + 2.0 * (static_cast <float> (rand()) / static_cast <float> (RAND_MAX)) * eps;
// return 0.0f;
}
static bool isInside(float3 vert, BVHTreeFromMesh *treedata){
// float eps = 0.0001;
int count = 0;
float min_dist = 0.0f;
for(auto dir : directions){
float radius = 0.0f;
float max_length = FLT_MAX;
BVHTreeRayHit rayhit = {0};
rayhit.index = -1;
rayhit.dist = max_length;
BLI_bvhtree_ray_cast(treedata->tree, vert, dir, radius, &rayhit, treedata->raycast_callback, treedata);
if (rayhit.index != -1 && rayhit.dist <= max_length) {
if(dot_v3v3(rayhit.no, dir) > 0.0f){
count++;
}
if((min_dist > 0.0) && (min_dist - rayhit.dist) > 0.0)
return false;
}
}
return count > 3;
}
// Used to populate a tets face normals and planesD
static void setTetProperties(Vector<float3> &verts,
Vector<int> &tetVertId,
Vector<float3> &faceNormals,
Vector<float> &planesD,
int tetNr){
for(int i = 0; i<4; i++){
float3 p0 = verts[tetVertId[4*tetNr + tetFaces[i][0]]];
float3 p1 = verts[tetVertId[4*tetNr + tetFaces[i][1]]];
float3 p2 = verts[tetVertId[4*tetNr + tetFaces[i][2]]];
float3 normal = cross(p1 - p0, p2 - p0);
normal = normalize(normal);
faceNormals[4*tetNr + i] = normal;
planesD[4*tetNr + i] = dot(p0, normal);
}
}
static float3 getCircumCenter(float3 p0, float3 p1, float3 p2, float3 p3){
float3 b = p1 - p0;
float3 c = p2 - p0;
float3 d = p3 - p0;
float det = 2.0 * (b.x*(c.y*d.z - c.z*d.y) - b.y*(c.x*d.z - c.z*d.x) + b.z*(c.x*d.y - c.y*d.x));
if (det == 0.0f){
return p0;
}
else{
float3 v = cross(c, d)*dot(b, b) + cross(d, b)*dot(c, c) + cross(b, c)*dot(d, d);
v /= det;
return p0 + v;
}
}
// bool isSameSide(float3 p0, float3 p1, float3 p2, float3 p4, float3 vert){
// }
// bool isInsideTet(float3 p0, float3 p1, float3 p2, float3 p3, float3 vert){
// }
static int findContainingTet(Vector<float3> &verts,
Vector<int> &tetVertId,
Vector<int> &tetFaceNeighbors,
Vector<float3> &faceNormals,
Vector<float> &planesD,
int tetMarkId,
Vector<int> &tetMarks,
float3 currVert){
/* --------------------
Matthias Method
----------------------*/
// bool found = false;
// int tetNr = 0;
// while(tetNr < tetVertId.size()/4 && tetVertId[4*tetNr]<0)
// tetNr++;
// float3 center(0.0, 0.0, 0.0);
// while(!found){
// if(tetNr < 0 || tetMarks[tetNr] == tetMarkId){
// break;
// }
// tetMarks[tetNr] = tetMarkId;
// center = {0.0,0.0,0.0};
// for(int i = 0; i<4; i++){
// center += verts[tetVertId[4*tetNr + i]];
// }
// center*=0.25;
// float minT = inf; //
// int minFaceNr = -1;
// for(int i = 0; i<4; i++){
// float3 normal = faceNormals[4*tetNr + i];
// float d = planesD[4*tetNr + i];
// float hp = dot(normal, currVert) - d;
// float hc = dot(normal, center) - d;
// float t = hp - hc;
// if(t <= eps)
// continue;
// t = -hc/t;
// if((t >= eps) && ((t - minT) > eps)){
// minT = t;
// minFaceNr = i;
// }
// }
// if(minT >= 1.0){
// found = true;
// }
// else{
// tetNr = tetFaceNeighbors[4*tetNr + minFaceNr];
// }
// }
// if(found)
// return tetNr;
// else
// return -1;
/* --------------------
Brute force finding first violating tet
----------------------*/
for(int currTet = 0; currTet<tetVertId.size()/4; currTet++){
if(tetVertId[4*currTet] == -1)
continue;
float3 p0 = verts[tetVertId[4*currTet + 0]];
float3 p1 = verts[tetVertId[4*currTet + 1]];
float3 p2 = verts[tetVertId[4*currTet + 2]];
float3 p3 = verts[tetVertId[4*currTet + 3]];
float3 circumCenter = getCircumCenter(p0, p1, p2, p3);
float circumRadius = length(p0 - circumCenter);
// if((circumRadius - length(currVert - circumCenter)) >= eps){
if(length(currVert - circumCenter) < circumRadius){
return currTet;
}
}
/* -----------------------
My method, checks if point is inside tet, if not finds the face that is connecting to the point and center
------------------------*/
// bool found = false;
// int tetNr = 0;
// while(tetNr < tetVertId.size()/4 && tetVertId[4*tetNr] < 0)
// tetNr++;
// while(!found){
// float3 p0 = verts[tetVertId[4*currTet + 0]];
// float3 p1 = verts[tetVertId[4*currTet + 1]];
// float3 p2 = verts[tetVertId[4*currTet + 2]];
// float3 p3 = verts[tetVertId[4*currTet + 3]];
// if(isInsideTet(p0, p1, p2, p3, currVert))
// return tetNr;
// for(int i = 0; i<4; i++)
// }
return -1;
}
/*
The basic assumption employed is that 2 violating tets cannot not be neighbors.
Simple BFS approach that checks neighbors of all violating tets and adds them to stack
If a violating tet shares a face with a non-violating tet, that's a boundary face and for each violating tet, list of its boundary faces is returned
Note - BFS can be written better
*/
static Vector<std::pair<int, Vector<int>>> getViolatingTets(Vector<float3> &verts,
Vector<int> &tetVertId,
Vector<int> &tetFaceNeighbors,
int tetMarkId,
Vector<int> &tetMarks,
float3 currVert,
int containingTetNr
){
Vector< std::pair<int,Vector<int>> > violatingTets;
Vector<int> stack;
stack.append(containingTetNr);
while(stack.size()){
int currTet = stack.last();
stack.remove_last();
if(tetMarks[currTet] == tetMarkId){
continue;
}
tetMarks[currTet] = tetMarkId;
Vector<int> currTetBorderFaces;
for(int i = 0; i<4; i++){
int neighborTet = tetFaceNeighbors[4*currTet + i];
if(neighborTet<0){
currTetBorderFaces.append(i);
continue;
}
if(tetMarks[neighborTet]==tetMarkId){
continue;
}
float3 p0 = verts[tetVertId[4*neighborTet + 0]];
float3 p1 = verts[tetVertId[4*neighborTet + 1]];
float3 p2 = verts[tetVertId[4*neighborTet + 2]];
float3 p3 = verts[tetVertId[4*neighborTet + 3]];
float3 circumCenter = getCircumCenter(p0, p1, p2, p3);
float circumRadius = length(p0 - circumCenter);
// if((circumRadius - length(currVert - circumCenter)) >= eps){
if(length(currVert - circumCenter) < circumRadius){
stack.append(neighborTet);
// tetMarks[neighborTet] = tetMarkId;
}
else{
currTetBorderFaces.append(i);
}
}
violatingTets.append({currTet, currTetBorderFaces});
}
return violatingTets;
}
static Vector<int> createTets(Vector<float3> verts, BVHTreeFromMesh *treedata, float minTetQuality){
Vector<int> tetVertId; // Stores indices of vertex that form a tet. Every tet is stored as 4 indices
Vector<int> tetFaceNeighbors; // Stores index of tet that shares the face as per tetFaces order. 4 neighbors per tet, 1 for each face
int firstFreeTet = -1;
Vector<float3> faceNormals; // Stores the normal of each of face of the tet
Vector<float> planesD;
int tetMarkId = 0;
Vector<int> tetMarks;
/*Used to keep track of visited tets for various processess. In each step where visited tets need to avoided,
on visiting them, a unique number is stored representing that particular step
*/
int bigTet = verts.size() - 4;
for(int i = 0; i<4; i++){
tetVertId.append(bigTet + i);
tetFaceNeighbors.append(-1);
faceNormals.append({0.0, 0.0, 0.0});
planesD.append(0.0);
}
tetMarks.append(0);
setTetProperties(verts, tetVertId, faceNormals, planesD, 0);
for(int vertNr = 0; vertNr<bigTet; vertNr++){
float3 currVert = verts[vertNr];
// std::cout << "Adding vert " << vertNr << std::endl;
// Find containing tet (need to understand) center can lie outside the tet?
tetMarkId += 1;
int containingTetNr = -1;
containingTetNr = findContainingTet(verts, tetVertId, tetFaceNeighbors, faceNormals, planesD, tetMarkId, tetMarks, currVert);
if(containingTetNr == -1){
std::cout << "Couldn't add vert " << vertNr << std::endl;
continue;
}
// Find Violating Tets - Returns all tets that are violating the Delaunay condition along with a list of face indices that are boundary faces
// Boundary faces may be outermost face of the structure or the face shared by a violating tet and a non violating tet
tetMarkId += 1;
Vector<std::pair<int, Vector<int>>> violatingTets = getViolatingTets(verts, tetVertId, tetFaceNeighbors, tetMarkId, tetMarks, currVert, containingTetNr);
//Create new tets centered at the new point and including the boundary faces
Vector<int> newTets; // Stores tet indices of the new tets formed. Used for making neighbors of the new tets formed
for(int violatingTetNr = 0; violatingTetNr<violatingTets.size(); violatingTetNr++){
int violatingTet = violatingTets[violatingTetNr].first;
Vector<int> boundaryFaces = violatingTets[violatingTetNr].second;
// Copying old tet information
Vector<int> currTetVerts;
Vector<int> currTetNeighbors;
for(int i = 0; i<4; i++){
currTetVerts.append(tetVertId[4*violatingTet + i]);
currTetNeighbors.append(tetFaceNeighbors[4*violatingTet + i]);
}
// Deleting old tet. For each deleted tet, index 1 stores the index of next free tet
tetVertId[4*violatingTet] = -1;
tetVertId[4*violatingTet + 1] = firstFreeTet;
firstFreeTet = violatingTet;
for(int i = 0; i<boundaryFaces.size(); i++){
Vector<int> faceVerts;
for(int j = 2; j>=0; j--){
faceVerts.append(currTetVerts[tetFaces[boundaryFaces[i]][j]]);
}
// Make new tet
int newTetNr = -1;
if(firstFreeTet == -1){
newTetNr = tetVertId.size()/4;
for(int j = 0; j<4; j++){
tetVertId.append(j<3 ? faceVerts[j] : vertNr);
tetFaceNeighbors.append(-1);
faceNormals.append({0.0,0.0,0.0});
planesD.append(0.0);
}
tetMarks.append(0);
}
else{
newTetNr = firstFreeTet;
firstFreeTet = tetVertId[4*firstFreeTet + 1];
for(int j = 0; j<3; j++){
tetVertId[4*newTetNr + j] = faceVerts[j];
tetFaceNeighbors[4*newTetNr + j] = -1;
}
tetVertId[4*newTetNr + 3] = vertNr;
tetFaceNeighbors[4*newTetNr + 3] = -1;
tetMarks[newTetNr] = 0;
}
newTets.append(newTetNr);
tetFaceNeighbors[4*newTetNr] = currTetNeighbors[boundaryFaces[i]];
// If the boundary face has no neighboring tet
if(currTetNeighbors[boundaryFaces[i]] != -1){
// Else correcting the neighbors for the shared face
// tetFaceNeighbors[4*newTetNr] = currTetNeighbors[boundaryFaces[i]];
for(int j = 0; j<4; j++){
if(tetFaceNeighbors[4*currTetNeighbors[boundaryFaces[i]] + j] == violatingTet){
tetFaceNeighbors[4*currTetNeighbors[boundaryFaces[i]] + j] = newTetNr;
break;
}
}
}
setTetProperties(verts, tetVertId, faceNormals, planesD, newTetNr);
}
}
// Setting the neighbors of internal faces of new tets
for(int i = 0; i<newTets.size(); i++){
for(int j = 0; j<newTets.size(); j++){
if(j == i){
continue;
}
for(int facei = 0; facei<4; facei++){
int vertsI[3];
for(int k = 0; k<3; k++){
vertsI[k] = tetVertId[4*newTets[i] + tetFaces[facei][k]];
}
int count = 0;
for(int k = 0; k<3; k++){
for(int l = 0; l<4; l++){
if(vertsI[k] == tetVertId[4*newTets[j] + l]){
count++;
break;
}
}
}
if(count == 3){
tetFaceNeighbors[4*newTets[i] + facei] = newTets[j];
// tetFaceNeighbors[4*newTets[i] + facei] = newTets[j];
}
}
}
}
}
// Remove empty tets and tets that lie outside the structure
int emptyTet = 0;
int tetLen = tetVertId.size()/4;
for(int tetNr = 0; tetNr<tetLen; tetNr++){
int flag = 1;
float3 center(0.0f, 0.0f, 0.0f);
for(int i = 0; i<4; i++){
center += verts[tetVertId[4*tetNr + i]];
if(tetVertId[4*tetNr + i]<0 || tetVertId[4*tetNr + i]>=bigTet){
flag = 0;
break;
}
}
center*=0.25;
if(!flag || !isInside(center, treedata)){
continue;
}
for(int i = 0; i<4; i++){
tetVertId[4*emptyTet + i] = tetVertId[4*tetNr + i];
}
emptyTet++;
}
tetVertId.remove(4*emptyTet, 4*(tetLen - emptyTet));
return tetVertId;
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh)
{
// Parameters of tetrahedralization, need to be taken as user input, being defined here as placeholders
float interiorResolution = 0;
float minTetQuality = 0.001; // Exp goes from -4 to 0
bool oneFacePerTet = false;
float tetScale = 0.8;
BVHTreeFromMesh treedata = {NULL};
BKE_bvhtree_from_mesh_get(&treedata, mesh, BVHTREE_FROM_LOOPTRI, 2);
Mesh *result;
BMesh *bm;
bool use_operators = true;
BMeshCreateParams bmcParam;
bmcParam.use_toolflags = true;
bm = BM_mesh_create(&bm_mesh_allocsize_default,
&bmcParam);
Vector<float3> tetVerts;
float3 center(0,0,0);
float3 bmin(inf, inf, inf);
float3 bmax(-inf, -inf, -inf);
// Copying surface nodes to tetVerts
for(int i = 0; i<mesh->totvert; i++){
tetVerts.append({0.0, 0.0, 0.0});
tetVerts[i][0] = mesh->mvert[i].co[0] + randomEps();
tetVerts[i][1] = mesh->mvert[i].co[1] + randomEps();
tetVerts[i][2] = mesh->mvert[i].co[2] + randomEps();
center += tetVerts[i]; // Can cause overflow?
for(int axis = 0; axis<3; axis++){
bmin[axis] = min(bmin[axis], tetVerts[i][axis]);
bmax[axis] = max(bmax[axis], tetVerts[i][axis]);
}
}
center /= (float)tetVerts.size();
// Computing radius of bounding sphere (max dist of node from center)
float radius = 0.0;
for(int i = 0; i<tetVerts.size(); i++){
float dist = length(tetVerts[i] - center);
radius = max(dist, radius);
}
// Interior sampling
if(interiorResolution > 0.0){
float boundLen[3];
sub_v3_v3v3(boundLen, bmax, bmin);
float maxBoundLen = max_axis_v3(boundLen);
float sampleLen = maxBoundLen/interiorResolution;
for(int xi = 0; xi<(int)(boundLen[0]/sampleLen); xi++){
float x = bmin[0] + xi*sampleLen + randomEps();
for(int yi = 0; yi<(int)(boundLen[1]/sampleLen); yi++){
float y = bmin[1] + yi*sampleLen + randomEps();
for(int zi = 0; zi<(int)(boundLen[2]/sampleLen); zi++){
float z = bmin[2] + zi*sampleLen + randomEps();
if(isInside({x,y,z}, &treedata)){
tetVerts.append({x,y,z});
}
}
}
}
}
float bigTetSize = radius*5.0;
tetVerts.append({-bigTetSize, 0.0, -bigTetSize});
tetVerts.append({bigTetSize, 0.0, -bigTetSize});
tetVerts.append({0.0, bigTetSize, bigTetSize});
tetVerts.append({0.0, -bigTetSize, bigTetSize});
Vector<int> tetVertId = createTets(tetVerts, &treedata, minTetQuality);
Vector<BMVert *> bmverts;
if(oneFacePerTet){
for(int i = 0; i<mesh->totvert; i++){
bmverts.append(BM_vert_create(bm, mesh->mvert[i].co, NULL, BM_CREATE_NOP));
}
for(int i = mesh->totvert; i<tetVerts.size(); i++){
bmverts.append(BM_vert_create(bm, tetVerts[i], NULL, BM_CREATE_NOP));
}
}
else{
// Add vertices multiple times to make the tets distinctly visible
for(int tetNr = 0; tetNr<tetVertId.size()/4; tetNr++){
float3 center(0,0,0);
for(int j = 0; j<4; j++){
center += tetVerts[tetVertId[4*tetNr + j]];
}
center *= 0.25;
for(int faceNr = 0; faceNr < 4; faceNr++){
for(int faceVertNr = 0; faceVertNr < 3; faceVertNr++){
float3 vert = tetVerts[tetVertId[4*tetNr + tetFaces[faceNr][faceVertNr]]];
vert = center + (vert - center)*tetScale;
bmverts.append(BM_vert_create(bm, vert, NULL, BM_CREATE_NOP));
}
}
}
}
int numTets = tetVertId.size()/4;
int nr = 0;
for(int i = 0; i<numTets; i++){
if(oneFacePerTet){
if(tetVertId[4*i] < 0)
continue;
BM_face_create_quad_tri(bm, bmverts[tetVertId[4*i + 0]], bmverts[tetVertId[4*i + 1]], bmverts[tetVertId[4*i + 2]], bmverts[tetVertId[4*i + 3]], NULL, BM_CREATE_NO_DOUBLE);
}
else{
for(int faceNr = 0; faceNr<4; faceNr++){
BM_face_create_quad_tri(bm, bmverts[nr], bmverts[nr+1], bmverts[nr+2], NULL, NULL, BM_CREATE_NO_DOUBLE);
nr+=3;
}
}
}
CustomData_MeshMasks cd_mask_extra = {
.vmask = CD_MASK_ORIGINDEX, .emask = CD_MASK_ORIGINDEX, .pmask = CD_MASK_ORIGINDEX};
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
BM_mesh_free(bm);
return result;
}
// --------------------------------------
// --------------------------------------
// static Span<MDeformVert> get_vertex_group(const Mesh &mesh, const int defgrp_index)
// {
// if (defgrp_index == -1) {
// return {};
// }
// const MDeformVert *vertex_group = static_cast<const MDeformVert *>(
// CustomData_get_layer(&mesh.vdata, CD_MDEFORMVERT));
// if (!vertex_group) {
// return {};
// }
// return {vertex_group, mesh.totvert};
// }
// static Vector<int64_t> selected_indices_from_vertex_group(Span<MDeformVert> vertex_group,
// const int index,
// const bool invert)
// {
// Vector<int64_t> selected_indices;
// for (const int i : vertex_group.index_range()) {
// const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f;
// if (found != invert) {
// selected_indices.append(i);
// }
// }
// return selected_indices;
// }
// static Array<bool> selection_array_from_vertex_group(Span<MDeformVert> vertex_group,
// const int index,
// const bool invert)
// {
// Array<bool> selection(vertex_group.size());
// for (const int i : vertex_group.index_range()) {
// const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f;
// selection[i] = (found != invert);
// }
// return selection;
// }
// static std::optional<Mesh *> calculate_weld(const Mesh &mesh, const WeldModifierData &wmd)
// {
// const int defgrp_index = BKE_id_defgroup_name_index(&mesh.id, wmd.defgrp_name);
// Span<MDeformVert> vertex_group = get_vertex_group(mesh, defgrp_index);
// const bool invert = (wmd.flag & MOD_WELD_INVERT_VGROUP) != 0;
// if (wmd.mode == MOD_WELD_MODE_ALL) {
// if (!vertex_group.is_empty()) {
// Vector<int64_t> selected_indices = selected_indices_from_vertex_group(
// vertex_group, defgrp_index, invert);
// return blender::geometry::mesh_merge_by_distance_all(
// mesh, IndexMask(selected_indices), wmd.merge_dist);
// }
// return blender::geometry::mesh_merge_by_distance_all(
// mesh, IndexMask(mesh.totvert), wmd.merge_dist);
// }
// if (wmd.mode == MOD_WELD_MODE_CONNECTED) {
// const bool only_loose_edges = (wmd.flag & MOD_WELD_LOOSE_EDGES) != 0;
// if (!vertex_group.is_empty()) {
// Array<bool> selection = selection_array_from_vertex_group(
// vertex_group, defgrp_index, invert);
// return blender::geometry::mesh_merge_by_distance_connected(
// mesh, selection, wmd.merge_dist, only_loose_edges);
// }
// Array<bool> selection(mesh.totvert, true);
// return blender::geometry::mesh_merge_by_distance_connected(
// mesh, selection, wmd.merge_dist, only_loose_edges);
// }
// BLI_assert_unreachable();
// return nullptr;
// }
// static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh)
// {
// const WeldModifierData &wmd = reinterpret_cast<WeldModifierData &>(*md);
// std::optional<Mesh *> result = calculate_weld(*mesh, wmd);
// if (!result) {
// return mesh;
// }
// return *result;
// }
static void initData(ModifierData *md)
{
WeldModifierData *wmd = (WeldModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier));
MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeldModifierData), modifier);
}
static void requiredDataMask(Object *UNUSED(ob),
ModifierData *md,
CustomData_MeshMasks *r_cddata_masks)
{
WeldModifierData *wmd = (WeldModifierData *)md;
/* Ask for vertexgroups if we need them. */
if (wmd->defgrp_name[0] != '\0') {
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
int weld_mode = RNA_enum_get(ptr, "mode");
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE);
if (weld_mode == MOD_WELD_MODE_CONNECTED) {
uiItemR(layout, ptr, "loose_edges", 0, nullptr, ICON_NONE);
}
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr);
modifier_panel_end(layout, ptr);
}
static void panelRegister(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_Weld, panel_draw);
}
ModifierTypeInfo modifierType_Weld = {
/* name */ "Weld",
/* structName */ "WeldModifierData",
/* structSize */ sizeof(WeldModifierData),
/* srna */ &RNA_WeldModifier,
/* type */ eModifierTypeType_Constructive,
/* flags */
(ModifierTypeFlag)(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping |
eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_AcceptsCVs),
/* icon */ ICON_AUTOMERGE_OFF, /* TODO: Use correct icon. */
/* copyData */ BKE_modifier_copydata_generic,
/* deformVerts */ nullptr,
/* deformMatrices */ nullptr,
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
/* modifyGeometrySet */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ nullptr,
/* isDisabled */ nullptr,
/* updateDepsgraph */ nullptr,
/* dependsOnTime */ nullptr,
/* dependsOnNormals */ nullptr,
/* foreachIDLink */ nullptr,
/* foreachTexLink */ nullptr,
/* freeRuntimeData */ nullptr,
/* panelRegister */ panelRegister,
/* blendWrite */ nullptr,
/* blendRead */ nullptr,
};

View File

@@ -610,14 +610,36 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
Vector<int> tetVertId = createTets(tetVerts, &treedata, minTetQuality);
if(globalFlag){
return mesh;
// Creates a 2 tets to test collisions
bool testing = true;
if(testing){
tetVertId.remove(0, tetVertId.size());
tetVerts.remove(0, tetVerts.size());
tetVerts.append({-1, -1, 0});
tetVerts.append({-1, 1, 0});
tetVerts.append({1, -1, 0});
tetVerts.append({-1, -1, 1});
tetVerts.append({0, 0, 1});
tetVerts.append({0, 0, 2});
tetVerts.append({0, 1, 2});
tetVerts.append({1, 0, 2});
for(int i = 0; i<8; i++)
tetVertId.append(i);
}
// if(globalFlag){
// return mesh;
// }
Vector<BMVert *> bmverts;
if(oneFacePerTet){
for(int i = 0; i<mesh->totvert; i++){
bmverts.append(BM_vert_create(bm, mesh->mvert[i].co, NULL, BM_CREATE_NOP));
// bmverts.append(BM_vert_create(bm, mesh->mvert[i].co, NULL, BM_CREATE_NOP));
bmverts.append(BM_vert_create(bm, tetVerts[i], NULL, BM_CREATE_NOP));
}
for(int i = mesh->totvert; i<tetVerts.size()-4; i++){
bmverts.append(BM_vert_create(bm, tetVerts[i], NULL, BM_CREATE_NOP));

View File

@@ -267,7 +267,23 @@ static int find_intersecting_face(BodyPoint *bpoint_arr, BodyPoint *curr_point,
// Checking which is the closest face
// float min_dist = FLT_MAX;
// float min_face = -1;
// for(int facenr = 0; facenr < 4; facenr++){
// BodyPoint *face_point0 = &bpoint_arr[curr_tet->verts[tet_faces[facenr][0]]];
// BodyPoint *face_point1 = &bpoint_arr[curr_tet->verts[tet_faces[facenr][1]]];
// BodyPoint *face_point2 = &bpoint_arr[curr_tet->verts[tet_faces[facenr][2]]];
// float coeff[4];
// float diff10[3], diff20[3];
// sub_v3_v3v3(diff10, face_point1->x, face_point0->x);
// sub_v3_v3v3(diff20, face_point2->x, face_point0->x);
// cross_v3_v3v3(coeff, diff10, diff20);
// coeff[3] = -dot_v3v3(coeff, face_point0->x);
// }
// Is the point inside tet getting detected correctly?
// Is the intersecting face getting detected correctly?
// Is the correction working as expected?
}
void xpbd_solve_self_collision(SoftBody *sb){