forked from bartvdbraak/blender
Basic solver result feedback from the mass-spring (cloth/hair) solver.
This returns a general status (success/no-convergence/other) along with basic statistics (min/max/average) for the error value and the number of iterations. It allows some general estimation of the simulation quality and detection of critical settings that could become a problem. Better visualization and extended feedback can follow later.
This commit is contained in:
parent
00bb836e17
commit
491e7493c7
@ -304,6 +304,7 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
|
|||||||
|
|
||||||
cloth_md = psys.cloth
|
cloth_md = psys.cloth
|
||||||
cloth = cloth_md.settings
|
cloth = cloth_md.settings
|
||||||
|
result = cloth_md.solver_result
|
||||||
|
|
||||||
layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
|
layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
|
||||||
|
|
||||||
@ -335,6 +336,24 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
|
|||||||
|
|
||||||
col.prop(cloth_md, "show_debug_data", text="Debug")
|
col.prop(cloth_md, "show_debug_data", text="Debug")
|
||||||
|
|
||||||
|
if result:
|
||||||
|
box = layout.box()
|
||||||
|
|
||||||
|
if not result.status:
|
||||||
|
label = " "
|
||||||
|
icon = 'NONE'
|
||||||
|
elif result.status == {'SUCCESS'}:
|
||||||
|
label = "Success"
|
||||||
|
icon = 'NONE'
|
||||||
|
elif result.status - {'SUCCESS'} == {'NO_CONVERGENCE'}:
|
||||||
|
label = "No Convergence"
|
||||||
|
icon = 'ERROR'
|
||||||
|
else:
|
||||||
|
label = "ERROR"
|
||||||
|
icon = 'ERROR'
|
||||||
|
box.label(label, icon=icon)
|
||||||
|
box.label("Iterations: %d .. %d (avg. %d)" % (result.min_iterations, result.max_iterations, result.avg_iterations))
|
||||||
|
box.label("Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error))
|
||||||
|
|
||||||
|
|
||||||
class PARTICLE_PT_cache(ParticleButtonsPanel, Panel):
|
class PARTICLE_PT_cache(ParticleButtonsPanel, Panel):
|
||||||
|
@ -64,6 +64,14 @@ typedef struct ClothHairRoot {
|
|||||||
float rot[3][3];
|
float rot[3][3];
|
||||||
} ClothHairRoot;
|
} ClothHairRoot;
|
||||||
|
|
||||||
|
typedef struct ClothSolverResult {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
int max_iterations, min_iterations;
|
||||||
|
float avg_iterations;
|
||||||
|
float max_error, min_error, avg_error;
|
||||||
|
} ClothSolverResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This structure describes a cloth object against which the
|
* This structure describes a cloth object against which the
|
||||||
* simulation can run.
|
* simulation can run.
|
||||||
|
@ -4683,6 +4683,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clmd->solver_result = NULL;
|
||||||
clmd->debug_data = NULL;
|
clmd->debug_data = NULL;
|
||||||
}
|
}
|
||||||
else if (md->type == eModifierType_Fluidsim) {
|
else if (md->type == eModifierType_Fluidsim) {
|
||||||
|
@ -567,6 +567,8 @@ typedef struct ClothModifierData {
|
|||||||
/* XXX nasty hack, remove once hair can be separated from cloth modifier data */
|
/* XXX nasty hack, remove once hair can be separated from cloth modifier data */
|
||||||
struct ClothHairRoot *roots;
|
struct ClothHairRoot *roots;
|
||||||
|
|
||||||
|
struct ClothSolverResult *solver_result;
|
||||||
|
|
||||||
struct SimDebugData *debug_data; /* debug info */
|
struct SimDebugData *debug_data; /* debug info */
|
||||||
} ClothModifierData;
|
} ClothModifierData;
|
||||||
|
|
||||||
|
@ -293,6 +293,7 @@ blender_include_dirs(
|
|||||||
../../ikplugin
|
../../ikplugin
|
||||||
../../makesdna
|
../../makesdna
|
||||||
../../nodes/
|
../../nodes/
|
||||||
|
../../physics
|
||||||
../../windowmanager
|
../../windowmanager
|
||||||
../../editors/include
|
../../editors/include
|
||||||
../../render/extern/include
|
../../render/extern/include
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
#include "BKE_cloth.h"
|
#include "BKE_cloth.h"
|
||||||
#include "BKE_modifier.h"
|
#include "BKE_modifier.h"
|
||||||
|
|
||||||
|
#include "BPH_mass_spring.h"
|
||||||
|
|
||||||
#include "WM_api.h"
|
#include "WM_api.h"
|
||||||
#include "WM_types.h"
|
#include "WM_types.h"
|
||||||
|
|
||||||
@ -268,6 +270,64 @@ static char *rna_ClothCollisionSettings_path(PointerRNA *ptr)
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
static void rna_def_cloth_solver_result(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
static EnumPropertyItem status_items[] = {
|
||||||
|
{BPH_SOLVER_SUCCESS, "SUCCESS", 0, "Success", "Computation was successful"},
|
||||||
|
{BPH_SOLVER_NUMERICAL_ISSUE, "NUMERICAL_ISSUE", 0, "Numerical Issue", "The provided data did not satisfy the prerequisites"},
|
||||||
|
{BPH_SOLVER_NO_CONVERGENCE, "NO_CONVERGENCE", 0, "No Convergence", "Iterative procedure did not converge"},
|
||||||
|
{BPH_SOLVER_INVALID_INPUT, "INVALID_INPUT", 0, "Invalid Input", "The inputs are invalid, or the algorithm has been improperly called"},
|
||||||
|
{0, NULL, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "ClothSolverResult", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Solver Result", "Result of cloth solver iteration");
|
||||||
|
|
||||||
|
RNA_define_verify_sdna(0);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "status", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_items(prop, status_items);
|
||||||
|
RNA_def_property_enum_sdna(prop, NULL, "status");
|
||||||
|
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Status", "Status of the solver iteration");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "max_error", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "max_error");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Maximum Error", "Maximum error during substeps");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "min_error", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "min_error");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Minimum Error", "Minimum error during substeps");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "avg_error", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "avg_error");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Average Error", "Average error during substeps");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "max_iterations", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "max_iterations");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Maximum Iterations", "Maximum iterations during substeps");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "min_iterations", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "min_iterations");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Minimum Iterations", "Minimum iterations during substeps");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "avg_iterations", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "avg_iterations");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Average Iterations", "Average iterations during substeps");
|
||||||
|
|
||||||
|
RNA_define_verify_sdna(1);
|
||||||
|
}
|
||||||
|
|
||||||
static void rna_def_cloth_sim_settings(BlenderRNA *brna)
|
static void rna_def_cloth_sim_settings(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
@ -660,6 +720,7 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
|
|||||||
|
|
||||||
void RNA_def_cloth(BlenderRNA *brna)
|
void RNA_def_cloth(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
|
rna_def_cloth_solver_result(brna);
|
||||||
rna_def_cloth_sim_settings(brna);
|
rna_def_cloth_sim_settings(brna);
|
||||||
rna_def_cloth_collision_settings(brna);
|
rna_def_cloth_collision_settings(brna);
|
||||||
}
|
}
|
||||||
|
@ -2488,6 +2488,11 @@ static void rna_def_modifier_cloth(BlenderRNA *brna)
|
|||||||
RNA_def_property_pointer_sdna(prop, NULL, "coll_parms");
|
RNA_def_property_pointer_sdna(prop, NULL, "coll_parms");
|
||||||
RNA_def_property_ui_text(prop, "Cloth Collision Settings", "");
|
RNA_def_property_ui_text(prop, "Cloth Collision Settings", "");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "solver_result", PROP_POINTER, PROP_NONE);
|
||||||
|
RNA_def_property_struct_type(prop, "ClothSolverResult");
|
||||||
|
RNA_def_property_pointer_sdna(prop, NULL, "solver_result");
|
||||||
|
RNA_def_property_ui_text(prop, "Solver Result", "");
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
|
prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
|
||||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||||
RNA_def_property_ui_text(prop, "Point Cache", "");
|
RNA_def_property_ui_text(prop, "Point Cache", "");
|
||||||
|
@ -179,6 +179,7 @@ static void copyData(ModifierData *md, ModifierData *target)
|
|||||||
tclmd->point_cache->step = 1;
|
tclmd->point_cache->step = 1;
|
||||||
tclmd->clothObject = NULL;
|
tclmd->clothObject = NULL;
|
||||||
tclmd->roots = NULL;
|
tclmd->roots = NULL;
|
||||||
|
tclmd->solver_result = NULL;
|
||||||
tclmd->debug_data = NULL;
|
tclmd->debug_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +212,9 @@ static void freeData(ModifierData *md)
|
|||||||
if (clmd->roots)
|
if (clmd->roots)
|
||||||
MEM_freeN(clmd->roots);
|
MEM_freeN(clmd->roots);
|
||||||
|
|
||||||
|
if (clmd->solver_result)
|
||||||
|
MEM_freeN(clmd->solver_result);
|
||||||
|
|
||||||
BKE_sim_debug_data_free(clmd->debug_data);
|
BKE_sim_debug_data_free(clmd->debug_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,18 @@ extern "C" {
|
|||||||
struct Implicit_Data;
|
struct Implicit_Data;
|
||||||
struct ClothModifierData;
|
struct ClothModifierData;
|
||||||
|
|
||||||
|
typedef enum eMassSpringSolverStatus {
|
||||||
|
BPH_SOLVER_SUCCESS = (1 << 0),
|
||||||
|
BPH_SOLVER_NUMERICAL_ISSUE = (1 << 1),
|
||||||
|
BPH_SOLVER_NO_CONVERGENCE = (1 << 2),
|
||||||
|
BPH_SOLVER_INVALID_INPUT = (1 << 3),
|
||||||
|
} eMassSpringSolverStatus;
|
||||||
|
|
||||||
struct Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings);
|
struct Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings);
|
||||||
void BPH_mass_spring_solver_free(struct Implicit_Data *id);
|
void BPH_mass_spring_solver_free(struct Implicit_Data *id);
|
||||||
|
|
||||||
|
const struct MassSpringSolverResult *BPH_mass_spring_solver_result(struct Implicit_Data *data);
|
||||||
|
|
||||||
int BPH_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd);
|
int BPH_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd);
|
||||||
void BPH_cloth_solver_free(struct ClothModifierData *clmd);
|
void BPH_cloth_solver_free(struct ClothModifierData *clmd);
|
||||||
int BPH_cloth_solve(struct Object *ob, float frame, struct ClothModifierData *clmd, struct ListBase *effectors);
|
int BPH_cloth_solve(struct Object *ob, float frame, struct ClothModifierData *clmd, struct ListBase *effectors);
|
||||||
|
@ -530,6 +530,46 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cloth_clear_result(ClothModifierData *clmd)
|
||||||
|
{
|
||||||
|
ClothSolverResult *sres = clmd->solver_result;
|
||||||
|
|
||||||
|
sres->status = 0;
|
||||||
|
sres->max_error = sres->min_error = sres->avg_error = 0.0f;
|
||||||
|
sres->max_iterations = sres->min_iterations = 0;
|
||||||
|
sres->avg_iterations = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
|
||||||
|
{
|
||||||
|
ClothSolverResult *sres = clmd->solver_result;
|
||||||
|
|
||||||
|
if (sres->status) { /* already initialized ? */
|
||||||
|
/* error only makes sense for successful iterations */
|
||||||
|
if (result->status == BPH_SOLVER_SUCCESS) {
|
||||||
|
sres->min_error = min_ff(sres->min_error, result->error);
|
||||||
|
sres->max_error = max_ff(sres->max_error, result->error);
|
||||||
|
sres->avg_error += result->error / (float)steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
|
||||||
|
sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
|
||||||
|
sres->avg_iterations += (float)result->iterations / (float)steps;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* error only makes sense for successful iterations */
|
||||||
|
if (result->status == BPH_SOLVER_SUCCESS) {
|
||||||
|
sres->min_error = sres->max_error = result->error;
|
||||||
|
sres->avg_error += result->error / (float)steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
sres->min_iterations = sres->max_iterations = result->iterations;
|
||||||
|
sres->avg_iterations += (float)result->iterations / (float)steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
sres->status |= result->status;
|
||||||
|
}
|
||||||
|
|
||||||
int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
|
int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
|
||||||
{
|
{
|
||||||
unsigned int i=0;
|
unsigned int i=0;
|
||||||
@ -546,6 +586,10 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
|
|||||||
|
|
||||||
BKE_sim_debug_data_clear_category(clmd->debug_data, "collision");
|
BKE_sim_debug_data_clear_category(clmd->debug_data, "collision");
|
||||||
|
|
||||||
|
if (!clmd->solver_result)
|
||||||
|
clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
|
||||||
|
cloth_clear_result(clmd);
|
||||||
|
|
||||||
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
|
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
|
||||||
for (i = 0; i < numverts; i++) {
|
for (i = 0; i < numverts; i++) {
|
||||||
// update velocities with constrained velocities from pinned verts
|
// update velocities with constrained velocities from pinned verts
|
||||||
@ -565,6 +609,7 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (step < tf) {
|
while (step < tf) {
|
||||||
|
ImplicitSolverResult result;
|
||||||
|
|
||||||
/* copy velocities for collision */
|
/* copy velocities for collision */
|
||||||
for (i = 0; i < numverts; i++) {
|
for (i = 0; i < numverts; i++) {
|
||||||
@ -597,7 +642,8 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
|
|||||||
cloth_calc_force(clmd, frame, effectors, step);
|
cloth_calc_force(clmd, frame, effectors, step);
|
||||||
|
|
||||||
// calculate new velocity and position
|
// calculate new velocity and position
|
||||||
BPH_mass_spring_solve(id, dt);
|
BPH_mass_spring_solve(id, dt, &result);
|
||||||
|
cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
|
||||||
|
|
||||||
BPH_mass_spring_apply_result(id);
|
BPH_mass_spring_apply_result(id);
|
||||||
|
|
||||||
|
@ -61,6 +61,13 @@ extern "C" {
|
|||||||
struct Implicit_Data;
|
struct Implicit_Data;
|
||||||
struct SimDebugData;
|
struct SimDebugData;
|
||||||
|
|
||||||
|
typedef struct ImplicitSolverResult {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
int iterations;
|
||||||
|
float error;
|
||||||
|
} ImplicitSolverResult;
|
||||||
|
|
||||||
BLI_INLINE void implicit_print_matrix_elem(float v)
|
BLI_INLINE void implicit_print_matrix_elem(float v)
|
||||||
{
|
{
|
||||||
printf("%-8.3f", v);
|
printf("%-8.3f", v);
|
||||||
@ -118,7 +125,7 @@ void BPH_mass_spring_add_constraint_ndof0(struct Implicit_Data *data, int index,
|
|||||||
void BPH_mass_spring_add_constraint_ndof1(struct Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3]);
|
void BPH_mass_spring_add_constraint_ndof1(struct Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3]);
|
||||||
void BPH_mass_spring_add_constraint_ndof2(struct Implicit_Data *data, int index, const float c1[3], const float dV[3]);
|
void BPH_mass_spring_add_constraint_ndof2(struct Implicit_Data *data, int index, const float c1[3], const float dV[3]);
|
||||||
|
|
||||||
bool BPH_mass_spring_solve(struct Implicit_Data *data, float dt);
|
bool BPH_mass_spring_solve(struct Implicit_Data *data, float dt, struct ImplicitSolverResult *result);
|
||||||
void BPH_mass_spring_apply_result(struct Implicit_Data *data);
|
void BPH_mass_spring_apply_result(struct Implicit_Data *data);
|
||||||
|
|
||||||
/* Clear the force vector at the beginning of the time step */
|
/* Clear the force vector at the beginning of the time step */
|
||||||
|
@ -834,7 +834,7 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S)
|
static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, ImplicitSolverResult *result)
|
||||||
{
|
{
|
||||||
// Solves for unknown X in equation AX=B
|
// Solves for unknown X in equation AX=B
|
||||||
unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100;
|
unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100;
|
||||||
@ -847,14 +847,15 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
|
|||||||
lfVector *c = create_lfvector(numverts);
|
lfVector *c = create_lfvector(numverts);
|
||||||
lfVector *q = create_lfvector(numverts);
|
lfVector *q = create_lfvector(numverts);
|
||||||
lfVector *s = create_lfvector(numverts);
|
lfVector *s = create_lfvector(numverts);
|
||||||
float delta_new, delta_old, delta_target, alpha;
|
float bnorm2, delta_new, delta_old, delta_target, alpha;
|
||||||
|
|
||||||
cp_lfvector(ldV, z, numverts);
|
cp_lfvector(ldV, z, numverts);
|
||||||
|
|
||||||
/* d0 = filter(B)^T * P * filter(B) */
|
/* d0 = filter(B)^T * P * filter(B) */
|
||||||
cp_lfvector(fB, lB, numverts);
|
cp_lfvector(fB, lB, numverts);
|
||||||
filter(fB, S);
|
filter(fB, S);
|
||||||
delta_target = conjgrad_epsilon*conjgrad_epsilon * dot_lfvector(fB, fB, numverts);
|
bnorm2 = dot_lfvector(fB, fB, numverts);
|
||||||
|
delta_target = conjgrad_epsilon*conjgrad_epsilon * bnorm2;
|
||||||
|
|
||||||
/* r = filter(B - A * dV) */
|
/* r = filter(B - A * dV) */
|
||||||
mul_bfmatrix_lfvector(AdV, lA, ldV);
|
mul_bfmatrix_lfvector(AdV, lA, ldV);
|
||||||
@ -914,6 +915,10 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
|
|||||||
del_lfvector(s);
|
del_lfvector(s);
|
||||||
// printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount);
|
// printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount);
|
||||||
|
|
||||||
|
result->status = conjgrad_loopcount < conjgrad_looplimit ? BPH_SOLVER_SUCCESS : BPH_SOLVER_NO_CONVERGENCE;
|
||||||
|
result->iterations = conjgrad_loopcount;
|
||||||
|
result->error = bnorm2 > 0.0f ? sqrt(delta_new / bnorm2) : 0.0f;
|
||||||
|
|
||||||
return conjgrad_loopcount < conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable
|
return conjgrad_loopcount < conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1105,10 +1110,9 @@ static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool BPH_mass_spring_solve(Implicit_Data *data, float dt)
|
bool BPH_mass_spring_solve(Implicit_Data *data, float dt, ImplicitSolverResult *result)
|
||||||
{
|
{
|
||||||
unsigned int numverts = data->dFdV[0].vcount;
|
unsigned int numverts = data->dFdV[0].vcount;
|
||||||
bool ok;
|
|
||||||
|
|
||||||
lfVector *dFdXmV = create_lfvector(numverts);
|
lfVector *dFdXmV = create_lfvector(numverts);
|
||||||
zero_lfvector(data->dV, numverts);
|
zero_lfvector(data->dV, numverts);
|
||||||
@ -1123,7 +1127,7 @@ bool BPH_mass_spring_solve(Implicit_Data *data, float dt)
|
|||||||
|
|
||||||
// itstart();
|
// itstart();
|
||||||
|
|
||||||
ok = cg_filtered(data->dV, data->A, data->B, data->z, data->S); /* conjugate gradient algorithm to solve Ax=b */
|
cg_filtered(data->dV, data->A, data->B, data->z, data->S, result); /* 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);
|
// cg_filtered_pre(id->dV, id->A, id->B, id->z, id->S, id->P, id->Pinv, id->bigI);
|
||||||
|
|
||||||
// itend();
|
// itend();
|
||||||
@ -1136,7 +1140,7 @@ bool BPH_mass_spring_solve(Implicit_Data *data, float dt)
|
|||||||
|
|
||||||
del_lfvector(dFdXmV);
|
del_lfvector(dFdXmV);
|
||||||
|
|
||||||
return ok;
|
return result->status == BPH_SOLVER_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BPH_mass_spring_apply_result(Implicit_Data *data)
|
void BPH_mass_spring_apply_result(Implicit_Data *data)
|
||||||
|
Loading…
Reference in New Issue
Block a user