📄 ivp_3d_solver.cxx
字号:
// Copyright (C) Ipion Software GmbH 1999-2000. All rights reserved.
// IVP_EXPORT_PRIVATE
#include <ivp_physics.hxx>
#include <ivp_actuator_info.hxx>
#ifndef WIN32
# pragma implementation "ivp_3d_solver.hxx"
#endif
#include <ivp_cache_object.hxx>
//#include <ivp_cache_ledge_point.hxx>
#include "ivp_3d_solver.hxx"
int IVP_U_Matrix_Cache::hits = 0;
int IVP_U_Matrix_Cache::misses = 0;
IVP_Time IVP_3D_Solver::calc_nullstelle(IVP_Time t0, IVP_Time t1, IVP_DOUBLE value, IVP_DOUBLE v0, IVP_DOUBLE v1,
IVP_Real_Object *solver_a, IVP_Real_Object *solver_b)
{
// tests for 'value' rather than '0.0f'
// uses 'sekantenverfahren' und modified mid point
IVP_ASSERT(v0 >= value);
IVP_ASSERT(v1 <= value);
IVP_U_Matrix A,B;
int counter = 0;
while(1){
IVP_DOUBLE dv = v1-v0;
IVP_Time tt;
// There are two algorithms to reduce the size of the interval:
// 1. midpoint
// 2. zero value of connecting straight
// only alg. 1 ensures termination, alg 2 is better on the average so
// mix both alg.
tt = t0;
tt += (value - v0) * (t1 - t0) / dv;
// modified midpoint
if ( (counter & 0x3) == 3){ // sometimes split interval int
if (counter > 64) return t0; // endless loop, take left value
const IVP_DOUBLE ratio = 0.25f; // int
const IVP_DOUBLE inv_ratio = 1.0f - ratio;
IVP_DOUBLE dt0 = t0 - tt;
IVP_DOUBLE dt1 = t1 - tt;
IVP_DOUBLE mid2 = dt0 + dt1;
tt += mid2 * (inv_ratio * 0.5f);
}
solver_a->calc_at_matrix(tt, &A);
solver_b->calc_at_matrix(tt, &B);
IVP_DOUBLE vv = get_value(&A,&B);
// printf("%i %G %G:%G %G:%G %G:%G %G\n", counter,value, t0.get_time(),v0, tt.get_time(),vv, t1.get_time(),v1, vv-value);
if(IVP_Inline_Math::fabsd(vv-value) < IVP_3D_SOLVER_NULLSTELLE_EPS){
return tt;
}
counter++;
// take left or right interval?
if(vv >= value){
v0 = vv;
t0 = tt;
}else{
v1 = vv;
t1 = tt;
}
}
return t0;
}
/** calc zero value when inly deviation is known */
IVP_BOOL IVP_3D_Solver::find_first_t_for_value_max_dev(IVP_DOUBLE value,
IVP_Time t_now, IVP_Time t_max, int t_now_cache_index,
IVP_U_Matrix_Cache *mc_A, IVP_U_Matrix_Cache *mc_B,
IVP_DOUBLE *opt_val_at_t_now,
IVP_Time *t_out
)
{
/***** first 'null'-stelle from t_now to t_max *******/
/*** calc max allowed t step (we want the FIRST nullstelle!) ***/
IVP_DOUBLE v0;
if (opt_val_at_t_now) {
v0 = *opt_val_at_t_now;
}else{
IVP_ASSERT( mc_A->base_time - t_now == 0.0f);
IVP_ASSERT( mc_B->base_time - t_now == 0.0f);
IVP_ASSERT( t_now_cache_index == 0);
IVP_U_Matrix *A = mc_A->calc_matrix_at_now( t_now, 0);
IVP_U_Matrix *B = mc_B->calc_matrix_at_now( t_now, 0);
v0 = get_value(A,B);
}
if(v0 <= value){
// 'collision' already took place
*t_out = t_now;
return IVP_TRUE;
}
// find step interval with different signs
IVP_Time t0=t_now;
int cache1_index = t_now_cache_index;
IVP_ASSERT (t_now - t_max <= 0.0f);
while(1){
IVP_DOUBLE t_step = (v0 - value) * inv_max_deviation; // max_dev is >> 0.0
if ( t0 - t_max + t_step > 0.0f ){
// assert value > 0
return IVP_FALSE;
}
t_step += t_step; // because of parabolic behavior @@@@@
if ( t0 - t_max + t_step > 0.0f ){
// assert value > 0
t_step = t_max - t0 + P_RES_EPS;
}
// guarantee minimal/ maximal step + rasterize t_steps
int int_t_step = (int) (t_step * ( IVP_3D_SOLVER_MAX_STEPS_PER_PSI * IVP_3D_SOLVER_PSIS_PER_SECOND));
if (int_t_step <1){
int_t_step = 1;
}
cache1_index += int_t_step;
IVP_ASSERT(cache1_index <= IVP_3D_SOLVER_MAX_STEPS_PER_PSI);
IVP_DOUBLE d_tstep = int_t_step * ( 1.0f/ (IVP_3D_SOLVER_MAX_STEPS_PER_PSI * IVP_3D_SOLVER_PSIS_PER_SECOND));
IVP_Time t1 = t0; t1 += d_tstep;
IVP_U_Matrix *A = mc_A->calc_matrix_at( t1, cache1_index);
IVP_U_Matrix *B = mc_B->calc_matrix_at( t1, cache1_index);
IVP_DOUBLE v1 = get_value(A,B);
if(v1 <= value){
// in this interval somewhere the first nullstelle is lurking
IVP_Time t = calc_nullstelle(t0, t1, value, v0, v1, mc_A->object, mc_B->object);
if ( t - t_max > 0.0f){
return IVP_FALSE;
}
*t_out = t;
return IVP_TRUE;
}
if (cache1_index == IVP_3D_SOLVER_MAX_STEPS_PER_PSI) return IVP_FALSE;
t0 = t1;
v0 = v1;
}
// no nullstelle crossed our way
return IVP_FALSE;
}
/** we know that there will be no zero value for first deviation !!! */
IVP_BOOL IVP_3D_Solver::find_first_t_for_value_max_dev2(IVP_DOUBLE value,
IVP_Time t_now, IVP_Time t_max, int t_now_cache_index,
IVP_U_Matrix_Cache *mc_A, IVP_U_Matrix_Cache *mc_B,
IVP_DOUBLE *opt_val_at_t_now,
IVP_Time *t_out
)
{
return find_first_t_for_value_max_dev(value, t_now, t_max, t_now_cache_index,
mc_A, mc_B, opt_val_at_t_now, t_out);
IVP_ASSERT(0==1);
return IVP_FALSE;
}
#if 0
/** we know that there will be no zero value for first deviation !!! */
IVP_BOOL IVP_3D_Solver::find_first_t_for_value_no_zero_dev(IVP_DOUBLE value,
IVP_DOUBLE t_now, IVP_DOUBLE t_max, int t_now_cache_index,
IVP_U_Matrix_Cache *mc_A, IVP_U_Matrix_Cache *mc_B,
IVP_DOUBLE *opt_val_at_t_now,
IVP_DOUBLE *opt_val_at_next_psi,
IVP_DOUBLE *t_out
)
{
/***** first 'null'-stelle from t_now to t_max *******/
/*** calc max allowed t step (we want the FIRST nullstelle!) ***/
IVP_DOUBLE v0; // value at left side of interval
IVP_DOUBLE v1; // value at right side of interval
// first of all check right side
if (opt_val_at_next_psi){
v1 = *opt_val_at_next_psi;
IVP_ASSERT(v1 <= value); // right side should be checked already
}else{
IVP_U_Matrix *A = mc_A->m_world_f_core_next_PSI;
IVP_U_Matrix *B = mc_B->m_world_f_core_next_PSI; // maximum values
v1 = get_value(A,B);
if (v1 >= value) return IVP_FALSE; // no zero value
}
// ok we know that there will be a zero value
if (t_max != mc_A->core->time_of_next_psi){ // check more in more detail
IVP_U_Matrix A,B;
IVP_Core *solver_a = mc_A->core;
IVP_Core *solver_b = mc_B->core;
solver_a->calc_at_matrix(t_max, &A);
solver_b->calc_at_matrix(t_max, &B);
v1 = get_value(&A,&B);
if (v1 >= value) return IVP_FALSE; // no zero value
}
/** now get first value */
if (opt_val_at_t_now) {
v0 = *opt_val_at_t_now;
}else{
IVP_ASSERT( mc_A->base_time == t_now);
IVP_ASSERT( mc_B->base_time == t_now);
IVP_ASSERT( t_now_cache_index == 0);
IVP_U_Matrix *A = mc_A->calc_matrix_at_now( t_now, 0);
IVP_U_Matrix *B = mc_B->calc_matrix_at( t_now, 0);
v0 = get_value(A,B);
}
IVP_ASSERT( v0> value);
{
*t_out = calc_nullstelle(t_now, t_max, value, v0, v1, mc_A->core, mc_B->core);
if ( *t_out < t_max) return IVP_TRUE;
}
// no nullstelle crossed our way
return IVP_FALSE;
}
#endif
/** only max deviation ist known */
IVP_BOOL IVP_3D_Solver::find_first_t_for_value_coll(IVP_DOUBLE value, IVP_DOUBLE value2,
IVP_Time t_now, IVP_Time t_max,
IVP_U_Matrix_Cache *mc_A, IVP_U_Matrix_Cache *mc_B,
IVP_DOUBLE *opt_val_at_t_now, IVP_Time *t_out
)
{
/***** first 'null'-stelle from t_now to t_max *******/
// spezial fuer kollisionsbestimmung:
// ziel: nicht sofort collision ausloesen, wenn am start unterhalb coll_dist.
// gucken, ob werte niedriger als startwert werden:
// (dann war vector in richtung des objekts) -> erneute kollision.
// wenn stets value(=coll_dist) > werte > value2(=echte oberflaeche), dann nix.
// wenn ein wert > value, dann ab dort ganz normal den naechsten zeitpunkt ausrechnen.
/*** calc max allowed t step (we don't want to hit the ground!) ***/
IVP_DOUBLE tnow_val;
{
IVP_U_Matrix *A,*B;
IVP_ASSERT( mc_A->base_time - t_now == 0.0f);
IVP_ASSERT( mc_B->base_time - t_now == 0.0f);
A = mc_A->calc_matrix_at_now( t_now, 0);
B = mc_B->calc_matrix_at_now( t_now, 0);
if (!opt_val_at_t_now){
tnow_val = get_value(A,B);
}else{
tnow_val = *opt_val_at_t_now;
}
}
if(tnow_val > value){
// standard situation
return find_first_t_for_value_max_dev(value, t_now, t_max, 0 /* cache_index*/, mc_A, mc_B, &tnow_val, t_out);
}
// we start in collision situation
IVP_DOUBLE inv_max_dev = 1.0f / max_deviation;
// step through time interval to find a t where val(t) > value
// throw collision when val(t) becomes <= val(now)
IVP_DOUBLE v0 =tnow_val;
IVP_Time t0=t_now;
int cache1_index = 0;
while(t0 - t_max < 0.0f) {
//printf("coll T0=%f\n",t0);
IVP_DOUBLE t_step = (v0 - value2) * inv_max_dev; // max_dev is >> 0.0
if ( t0 -t_max + t_step > 0.0f){
// assert value > 0
t_step = t_max - t0 + P_RES_EPS;
}else if ( t_step < 0.0f ){
t_step = 0.0f;
}
// guarantee minimal/ maximal step + rasterize t_steps
int int_t_step = (int) (t_step * ( IVP_3D_SOLVER_MAX_STEPS_PER_PSI * IVP_3D_SOLVER_PSIS_PER_SECOND));
if (int_t_step <1) int_t_step = 1;
IVP_U_Matrix *A;
IVP_U_Matrix *B;
IVP_Time t1;
IVP_DOUBLE real_t_step = int_t_step * ( 1.0f/ (IVP_3D_SOLVER_MAX_STEPS_PER_PSI * IVP_3D_SOLVER_PSIS_PER_SECOND));
t1 = t0 + real_t_step;
cache1_index += int_t_step;
A = mc_A->calc_matrix_at( t1, cache1_index);
B = mc_B->calc_matrix_at( t1, cache1_index);
IVP_DOUBLE v1 = get_value(A,B);
if(v1 > value){
if (t1 - t_max > 0.0f) return IVP_FALSE;
// now we reached standard situation
IVP_DOUBLE t1_val = v1;
return find_first_t_for_value_max_dev(value, t1, t_max, cache1_index,
mc_A,mc_B, &t1_val,t_out);
}
if(v1 <= tnow_val){
// in t1 we are nearer to object than in t_now -> indicate another collision at t0
*t_out = t0;
return IVP_TRUE;
}
t0 = t1;
v0 = v1;
}
// we still are below coll_dist but no real collision happened -> do nothing
return IVP_FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -