Blender V2.61 - r43446

cloth.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) Blender Foundation
00019  * All rights reserved.
00020  *
00021  * Contributor(s): Daniel Genrich
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include "MEM_guardedalloc.h"
00032 
00033 #include "DNA_cloth_types.h"
00034 #include "DNA_scene_types.h"
00035 #include "DNA_object_types.h"
00036 #include "DNA_meshdata_types.h"
00037 
00038 #include "BLI_math.h"
00039 #include "BLI_edgehash.h"
00040 #include "BLI_utildefines.h"
00041 #include "BLI_linklist.h"
00042 
00043 #include "BKE_cdderivedmesh.h"
00044 #include "BKE_cloth.h"
00045 #include "BKE_effect.h"
00046 #include "BKE_global.h"
00047 #include "BKE_modifier.h"
00048 #include "BKE_pointcache.h"
00049 
00050 #ifdef _WIN32
00051 void tstart ( void )
00052 {}
00053 void tend ( void )
00054 {
00055 }
00056 double tval( void )
00057 {
00058     return 0;
00059 }
00060 #else
00061 #include <sys/time.h>
00062 static struct timeval _tstart, _tend;
00063 static struct timezone tz;
00064 void tstart ( void )
00065 {
00066     gettimeofday ( &_tstart, &tz );
00067 }
00068 void tend ( void )
00069 {
00070     gettimeofday ( &_tend,&tz );
00071 }
00072 double tval(void)
00073 {
00074     double t1, t2;
00075     t1 = ( double ) _tstart.tv_sec + ( double ) _tstart.tv_usec/ ( 1000*1000 );
00076     t2 = ( double ) _tend.tv_sec + ( double ) _tend.tv_usec/ ( 1000*1000 );
00077     return t2-t1;
00078 }
00079 #endif
00080 
00081 /* Our available solvers. */
00082 // 255 is the magic reserved number, so NEVER try to put 255 solvers in here!
00083 // 254 = MAX!
00084 static CM_SOLVER_DEF    solvers [] =
00085 {
00086     { "Implicit", CM_IMPLICIT, implicit_init, implicit_solver, implicit_free },
00087         // { "Implicit C++", CM_IMPLICITCPP, implicitcpp_init, implicitcpp_solver, implicitcpp_free },
00088 };
00089 
00090 /* ********** cloth engine ******* */
00091 /* Prototypes for internal functions.
00092 */
00093 static void cloth_to_object (Object *ob,  ClothModifierData *clmd, DerivedMesh *dm);
00094 static void cloth_from_mesh ( ClothModifierData *clmd, DerivedMesh *dm );
00095 static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first);
00096 static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm );
00097 static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm );
00098 
00099 
00100 /******************************************************************************
00101 *
00102 * External interface called by modifier.c clothModifier functions.
00103 *
00104 ******************************************************************************/
00111 void cloth_init ( ClothModifierData *clmd )
00112 {   
00113     /* Initialize our new data structure to reasonable values. */
00114     clmd->sim_parms->gravity [0] = 0.0;
00115     clmd->sim_parms->gravity [1] = 0.0;
00116     clmd->sim_parms->gravity [2] = -9.81;
00117     clmd->sim_parms->structural = 15.0;
00118     clmd->sim_parms->shear = 15.0;
00119     clmd->sim_parms->bending = 0.5;
00120     clmd->sim_parms->Cdis = 5.0; 
00121     clmd->sim_parms->Cvi = 1.0;
00122     clmd->sim_parms->mass = 0.3f;
00123     clmd->sim_parms->stepsPerFrame = 5;
00124     clmd->sim_parms->flags = 0;
00125     clmd->sim_parms->solver_type = 0;
00126     clmd->sim_parms->preroll = 0;
00127     clmd->sim_parms->maxspringlen = 10;
00128     clmd->sim_parms->vgroup_mass = 0;
00129     clmd->sim_parms->avg_spring_len = 0.0;
00130     clmd->sim_parms->presets = 2; /* cotton as start setting */
00131     clmd->sim_parms->timescale = 1.0f; /* speed factor, describes how fast cloth moves */
00132     clmd->sim_parms->reset = 0;
00133     
00134     clmd->coll_parms->self_friction = 5.0;
00135     clmd->coll_parms->friction = 5.0;
00136     clmd->coll_parms->loop_count = 2;
00137     clmd->coll_parms->epsilon = 0.015f;
00138     clmd->coll_parms->flags = CLOTH_COLLSETTINGS_FLAG_ENABLED;
00139     clmd->coll_parms->collision_list = NULL;
00140     clmd->coll_parms->self_loop_count = 1.0;
00141     clmd->coll_parms->selfepsilon = 0.75;
00142 
00143     /* These defaults are copied from softbody.c's
00144     * softbody_calc_forces() function.
00145     */
00146     clmd->sim_parms->eff_force_scale = 1000.0;
00147     clmd->sim_parms->eff_wind_scale = 250.0;
00148 
00149     // also from softbodies
00150     clmd->sim_parms->maxgoal = 1.0f;
00151     clmd->sim_parms->mingoal = 0.0f;
00152     clmd->sim_parms->defgoal = 0.0f;
00153     clmd->sim_parms->goalspring = 1.0f;
00154     clmd->sim_parms->goalfrict = 0.0f;
00155     clmd->sim_parms->velocity_smooth = 0.0f;
00156 
00157     if(!clmd->sim_parms->effector_weights)
00158         clmd->sim_parms->effector_weights = BKE_add_effector_weights(NULL);
00159 
00160     if(clmd->point_cache)
00161         clmd->point_cache->step = 1;
00162 }
00163 
00164 static BVHTree *bvhselftree_build_from_cloth (ClothModifierData *clmd, float epsilon)
00165 {
00166     unsigned int i;
00167     BVHTree *bvhtree;
00168     Cloth *cloth;
00169     ClothVertex *verts;
00170     float co[12];
00171 
00172     if(!clmd)
00173         return NULL;
00174 
00175     cloth = clmd->clothObject;
00176 
00177     if(!cloth)
00178         return NULL;
00179     
00180     verts = cloth->verts;
00181     
00182     // in the moment, return zero if no faces there
00183     if(!cloth->numverts)
00184         return NULL;
00185     
00186     // create quadtree with k=26
00187     bvhtree = BLI_bvhtree_new(cloth->numverts, epsilon, 4, 6);
00188     
00189     // fill tree
00190     for(i = 0; i < cloth->numverts; i++, verts++)
00191     {
00192         copy_v3_v3(&co[0*3], verts->xold);
00193         
00194         BLI_bvhtree_insert(bvhtree, i, co, 1);
00195     }
00196     
00197     // balance tree
00198     BLI_bvhtree_balance(bvhtree);
00199     
00200     return bvhtree;
00201 }
00202 
00203 static BVHTree *bvhtree_build_from_cloth (ClothModifierData *clmd, float epsilon)
00204 {
00205     unsigned int i;
00206     BVHTree *bvhtree;
00207     Cloth *cloth;
00208     ClothVertex *verts;
00209     MFace *mfaces;
00210     float co[12];
00211 
00212     if(!clmd)
00213         return NULL;
00214 
00215     cloth = clmd->clothObject;
00216 
00217     if(!cloth)
00218         return NULL;
00219     
00220     verts = cloth->verts;
00221     mfaces = cloth->mfaces;
00222     
00223     // in the moment, return zero if no faces there
00224     if(!cloth->numfaces)
00225         return NULL;
00226     
00227     // create quadtree with k=26
00228     bvhtree = BLI_bvhtree_new(cloth->numfaces, epsilon, 4, 26);
00229     
00230     // fill tree
00231     for(i = 0; i < cloth->numfaces; i++, mfaces++)
00232     {
00233         copy_v3_v3(&co[0*3], verts[mfaces->v1].xold);
00234         copy_v3_v3(&co[1*3], verts[mfaces->v2].xold);
00235         copy_v3_v3(&co[2*3], verts[mfaces->v3].xold);
00236         
00237         if(mfaces->v4)
00238             copy_v3_v3(&co[3*3], verts[mfaces->v4].xold);
00239         
00240         BLI_bvhtree_insert(bvhtree, i, co, (mfaces->v4 ? 4 : 3));
00241     }
00242     
00243     // balance tree
00244     BLI_bvhtree_balance(bvhtree);
00245     
00246     return bvhtree;
00247 }
00248 
00249 void bvhtree_update_from_cloth(ClothModifierData *clmd, int moving)
00250 {   
00251     unsigned int i = 0;
00252     Cloth *cloth = clmd->clothObject;
00253     BVHTree *bvhtree = cloth->bvhtree;
00254     ClothVertex *verts = cloth->verts;
00255     MFace *mfaces;
00256     float co[12], co_moving[12];
00257     int ret = 0;
00258     
00259     if(!bvhtree)
00260         return;
00261     
00262     mfaces = cloth->mfaces;
00263     
00264     // update vertex position in bvh tree
00265     if(verts && mfaces)
00266     {
00267         for(i = 0; i < cloth->numfaces; i++, mfaces++)
00268         {
00269             copy_v3_v3(&co[0*3], verts[mfaces->v1].txold);
00270             copy_v3_v3(&co[1*3], verts[mfaces->v2].txold);
00271             copy_v3_v3(&co[2*3], verts[mfaces->v3].txold);
00272             
00273             if(mfaces->v4)
00274                 copy_v3_v3(&co[3*3], verts[mfaces->v4].txold);
00275         
00276             // copy new locations into array
00277             if(moving)
00278             {
00279                 // update moving positions
00280                 copy_v3_v3(&co_moving[0*3], verts[mfaces->v1].tx);
00281                 copy_v3_v3(&co_moving[1*3], verts[mfaces->v2].tx);
00282                 copy_v3_v3(&co_moving[2*3], verts[mfaces->v3].tx);
00283                 
00284                 if(mfaces->v4)
00285                     copy_v3_v3(&co_moving[3*3], verts[mfaces->v4].tx);
00286                 
00287                 ret = BLI_bvhtree_update_node(bvhtree, i, co, co_moving, (mfaces->v4 ? 4 : 3));
00288             }
00289             else
00290             {
00291                 ret = BLI_bvhtree_update_node(bvhtree, i, co, NULL, (mfaces->v4 ? 4 : 3));
00292             }
00293             
00294             // check if tree is already full
00295             if(!ret)
00296                 break;
00297         }
00298         
00299         BLI_bvhtree_update_tree(bvhtree);
00300     }
00301 }
00302 
00303 void bvhselftree_update_from_cloth(ClothModifierData *clmd, int moving)
00304 {   
00305     unsigned int i = 0;
00306     Cloth *cloth = clmd->clothObject;
00307     BVHTree *bvhtree = cloth->bvhselftree;
00308     ClothVertex *verts = cloth->verts;
00309     MFace *mfaces;
00310     float co[12], co_moving[12];
00311     int ret = 0;
00312     
00313     if(!bvhtree)
00314         return;
00315     
00316     mfaces = cloth->mfaces;
00317     
00318     // update vertex position in bvh tree
00319     if(verts && mfaces)
00320     {
00321         for(i = 0; i < cloth->numverts; i++, verts++)
00322         {
00323             copy_v3_v3(&co[0*3], verts->txold);
00324             
00325             // copy new locations into array
00326             if(moving)
00327             {
00328                 // update moving positions
00329                 copy_v3_v3(&co_moving[0*3], verts->tx);
00330                 
00331                 ret = BLI_bvhtree_update_node(bvhtree, i, co, co_moving, 1);
00332             }
00333             else
00334             {
00335                 ret = BLI_bvhtree_update_node(bvhtree, i, co, NULL, 1);
00336             }
00337             
00338             // check if tree is already full
00339             if(!ret)
00340                 break;
00341         }
00342         
00343         BLI_bvhtree_update_tree(bvhtree);
00344     }
00345 }
00346 
00347 void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr)
00348 {
00349     PTCacheID pid;
00350     
00351     BKE_ptcache_id_from_cloth(&pid, ob, clmd);
00352 
00353     // don't do anything as long as we're in editmode!
00354     if(pid.cache->edit && ob->mode & OB_MODE_PARTICLE_EDIT)
00355         return;
00356     
00357     BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_AFTER, framenr);
00358 }
00359 
00360 static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *result, int framenr)
00361 {
00362     PointCache *cache;
00363 
00364     cache= clmd->point_cache;
00365 
00366     /* initialize simulation data if it didn't exist already */
00367     if(clmd->clothObject == NULL) { 
00368         if(!cloth_from_object(ob, clmd, result, framenr, 1)) {
00369             BKE_ptcache_invalidate(cache);
00370             return 0;
00371         }
00372     
00373         if(clmd->clothObject == NULL) {
00374             BKE_ptcache_invalidate(cache);
00375             return 0;
00376         }
00377     
00378         implicit_set_positions(clmd);
00379 
00380         clmd->clothObject->last_frame= MINFRAME-1;
00381     }
00382 
00383     return 1;
00384 }
00385 
00386 static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *result, int framenr)
00387 {
00388     ClothVertex *verts = NULL;
00389     Cloth *cloth;
00390     ListBase *effectors = NULL;
00391     MVert *mvert;
00392     unsigned int i = 0;
00393     int ret = 0;
00394 
00395     /* simulate 1 frame forward */
00396     cloth = clmd->clothObject;
00397     verts = cloth->verts;
00398     mvert = result->getVertArray(result);
00399 
00400     /* force any pinned verts to their constrained location. */
00401     for(i = 0; i < clmd->clothObject->numverts; i++, verts++) {
00402         /* save the previous position. */
00403         copy_v3_v3(verts->xold, verts->xconst);
00404         copy_v3_v3(verts->txold, verts->x);
00405 
00406         /* Get the current position. */
00407         copy_v3_v3(verts->xconst, mvert[i].co);
00408         mul_m4_v3(ob->obmat, verts->xconst);
00409     }
00410 
00411     effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights);
00412     
00413     tstart();
00414 
00415     /* call the solver. */
00416     if(solvers [clmd->sim_parms->solver_type].solver)
00417         ret = solvers[clmd->sim_parms->solver_type].solver(ob, framenr, clmd, effectors);
00418 
00419     tend();
00420 
00421     pdEndEffectors(&effectors);
00422 
00423     // printf ( "%f\n", ( float ) tval() );
00424     
00425     return ret;
00426 }
00427 
00428 /************************************************
00429  * clothModifier_do - main simulation function
00430 ************************************************/
00431 DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, DerivedMesh *dm)
00432 {
00433     DerivedMesh *result;
00434     PointCache *cache;
00435     PTCacheID pid;
00436     float timescale;
00437     int framenr, startframe, endframe;
00438     int cache_result;
00439 
00440     clmd->scene= scene; /* nice to pass on later :) */
00441     framenr= (int)scene->r.cfra;
00442     cache= clmd->point_cache;
00443     result = CDDM_copy(dm);
00444 
00445     BKE_ptcache_id_from_cloth(&pid, ob, clmd);
00446     BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, &timescale);
00447     clmd->sim_parms->timescale= timescale;
00448 
00449     if(!result) {
00450         BKE_ptcache_invalidate(cache);
00451         return dm;
00452     }
00453 
00454     if(clmd->sim_parms->reset
00455         || (framenr == (startframe - clmd->sim_parms->preroll) && clmd->sim_parms->preroll != 0)
00456         || (clmd->clothObject && result->getNumVerts(result) != clmd->clothObject->numverts))
00457     {
00458         clmd->sim_parms->reset = 0;
00459         cache->flag |= PTCACHE_OUTDATED;
00460         BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
00461         BKE_ptcache_validate(cache, 0);
00462         cache->last_exact= 0;
00463         cache->flag &= ~PTCACHE_REDO_NEEDED;
00464         return result;
00465     }
00466     
00467     // unused in the moment, calculated separately in implicit.c
00468     clmd->sim_parms->dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
00469 
00470     /* handle continuous simulation with the play button */
00471     if(BKE_ptcache_get_continue_physics() || ((clmd->sim_parms->preroll > 0) && (framenr > startframe - clmd->sim_parms->preroll) && (framenr < startframe))) {
00472         BKE_ptcache_invalidate(cache);
00473 
00474         /* do simulation */
00475         if(!do_init_cloth(ob, clmd, dm, framenr))
00476             return result;
00477 
00478         do_step_cloth(ob, clmd, dm, framenr);
00479         cloth_to_object(ob, clmd, result);
00480 
00481         clmd->clothObject->last_frame= framenr;
00482 
00483         return result;
00484     }
00485 
00486     /* simulation is only active during a specific period */
00487     if(framenr < startframe) {
00488         BKE_ptcache_invalidate(cache);
00489         return result;
00490     }
00491     else if(framenr > endframe) {
00492         framenr= endframe;
00493     }
00494 
00495     /* initialize simulation data if it didn't exist already */
00496     if(!do_init_cloth(ob, clmd, dm, framenr))
00497         return result;
00498 
00499     if((framenr == startframe) && (clmd->sim_parms->preroll == 0)) {
00500         BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
00501         do_init_cloth(ob, clmd, dm, framenr);
00502         BKE_ptcache_validate(cache, framenr);
00503         cache->flag &= ~PTCACHE_REDO_NEEDED;
00504         clmd->clothObject->last_frame= framenr;
00505         return result;
00506     }
00507 
00508     /* try to read from cache */
00509     cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe);
00510 
00511     if(cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) {
00512         implicit_set_positions(clmd);
00513         cloth_to_object (ob, clmd, result);
00514 
00515         BKE_ptcache_validate(cache, framenr);
00516 
00517         if(cache_result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED)
00518             BKE_ptcache_write(&pid, framenr);
00519 
00520         clmd->clothObject->last_frame= framenr;
00521 
00522         return result;
00523     }
00524     else if(cache_result==PTCACHE_READ_OLD) {
00525         implicit_set_positions(clmd);
00526     }
00527     else if( /*ob->id.lib ||*/ (cache->flag & PTCACHE_BAKED)) { /* 2.4x disabled lib, but this can be used in some cases, testing further - campbell */
00528         /* if baked and nothing in cache, do nothing */
00529         BKE_ptcache_invalidate(cache);
00530         return result;
00531     }
00532 
00533     if(framenr!=clmd->clothObject->last_frame+1)
00534         return result;
00535 
00536     /* if on second frame, write cache for first frame */
00537     if(cache->simframe == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0))
00538         BKE_ptcache_write(&pid, startframe);
00539 
00540     clmd->sim_parms->timescale *= framenr - cache->simframe;
00541 
00542     /* do simulation */
00543     BKE_ptcache_validate(cache, framenr);
00544 
00545     if(!do_step_cloth(ob, clmd, dm, framenr)) {
00546         BKE_ptcache_invalidate(cache);
00547     }
00548     else
00549         BKE_ptcache_write(&pid, framenr);
00550 
00551     cloth_to_object (ob, clmd, result);
00552     clmd->clothObject->last_frame= framenr;
00553 
00554     return result;
00555 }
00556 
00557 /* frees all */
00558 void cloth_free_modifier(ClothModifierData *clmd )
00559 {
00560     Cloth   *cloth = NULL;
00561     
00562     if ( !clmd )
00563         return;
00564 
00565     cloth = clmd->clothObject;
00566 
00567     
00568     if ( cloth )
00569     {   
00570         // If our solver provides a free function, call it
00571         if ( solvers [clmd->sim_parms->solver_type].free )
00572         {
00573             solvers [clmd->sim_parms->solver_type].free ( clmd );
00574         }
00575 
00576         // Free the verts.
00577         if ( cloth->verts != NULL )
00578             MEM_freeN ( cloth->verts );
00579 
00580         cloth->verts = NULL;
00581         cloth->numverts = 0;
00582 
00583         // Free the springs.
00584         if ( cloth->springs != NULL )
00585         {
00586             LinkNode *search = cloth->springs;
00587             while(search)
00588             {
00589                 ClothSpring *spring = search->link;
00590                         
00591                 MEM_freeN ( spring );
00592                 search = search->next;
00593             }
00594             BLI_linklist_free(cloth->springs, NULL);
00595         
00596             cloth->springs = NULL;
00597         }
00598 
00599         cloth->springs = NULL;
00600         cloth->numsprings = 0;
00601 
00602         // free BVH collision tree
00603         if ( cloth->bvhtree )
00604             BLI_bvhtree_free ( cloth->bvhtree );
00605         
00606         if ( cloth->bvhselftree )
00607             BLI_bvhtree_free ( cloth->bvhselftree );
00608 
00609         // we save our faces for collision objects
00610         if ( cloth->mfaces )
00611             MEM_freeN ( cloth->mfaces );
00612         
00613         if(cloth->edgehash)
00614             BLI_edgehash_free ( cloth->edgehash, NULL );
00615         
00616         
00617         /*
00618         if(clmd->clothObject->facemarks)
00619         MEM_freeN(clmd->clothObject->facemarks);
00620         */
00621         MEM_freeN ( cloth );
00622         clmd->clothObject = NULL;
00623     }
00624 }
00625 
00626 /* frees all */
00627 void cloth_free_modifier_extern ( ClothModifierData *clmd )
00628 {
00629     Cloth   *cloth = NULL;
00630     if(G.rt > 0)
00631         printf("cloth_free_modifier_extern\n");
00632     
00633     if ( !clmd )
00634         return;
00635 
00636     cloth = clmd->clothObject;
00637     
00638     if ( cloth )
00639     {   
00640         if(G.rt > 0)
00641             printf("cloth_free_modifier_extern in\n");
00642         
00643         // If our solver provides a free function, call it
00644         if ( solvers [clmd->sim_parms->solver_type].free )
00645         {
00646             solvers [clmd->sim_parms->solver_type].free ( clmd );
00647         }
00648 
00649         // Free the verts.
00650         if ( cloth->verts != NULL )
00651             MEM_freeN ( cloth->verts );
00652 
00653         cloth->verts = NULL;
00654         cloth->numverts = 0;
00655 
00656         // Free the springs.
00657         if ( cloth->springs != NULL )
00658         {
00659             LinkNode *search = cloth->springs;
00660             while(search)
00661             {
00662                 ClothSpring *spring = search->link;
00663                         
00664                 MEM_freeN ( spring );
00665                 search = search->next;
00666             }
00667             BLI_linklist_free(cloth->springs, NULL);
00668         
00669             cloth->springs = NULL;
00670         }
00671 
00672         cloth->springs = NULL;
00673         cloth->numsprings = 0;
00674 
00675         // free BVH collision tree
00676         if ( cloth->bvhtree )
00677             BLI_bvhtree_free ( cloth->bvhtree );
00678         
00679         if ( cloth->bvhselftree )
00680             BLI_bvhtree_free ( cloth->bvhselftree );
00681 
00682         // we save our faces for collision objects
00683         if ( cloth->mfaces )
00684             MEM_freeN ( cloth->mfaces );
00685         
00686         if(cloth->edgehash)
00687             BLI_edgehash_free ( cloth->edgehash, NULL );
00688         
00689         
00690         /*
00691         if(clmd->clothObject->facemarks)
00692         MEM_freeN(clmd->clothObject->facemarks);
00693         */
00694         MEM_freeN ( cloth );
00695         clmd->clothObject = NULL;
00696     }
00697 }
00698 
00699 /******************************************************************************
00700 *
00701 * Internal functions.
00702 *
00703 ******************************************************************************/
00704 
00709 static void cloth_to_object (Object *ob,  ClothModifierData *clmd, DerivedMesh *dm)
00710 {
00711     unsigned int    i = 0;
00712     MVert *mvert = NULL;
00713     unsigned int numverts;
00714     Cloth *cloth = clmd->clothObject;
00715 
00716     if (clmd->clothObject) {
00717         /* inverse matrix is not uptodate... */
00718         invert_m4_m4(ob->imat, ob->obmat);
00719 
00720         mvert = CDDM_get_verts(dm);
00721         numverts = dm->getNumVerts(dm);
00722 
00723         for (i = 0; i < numverts; i++)
00724         {
00725             copy_v3_v3 (mvert[i].co, cloth->verts[i].x);
00726             mul_m4_v3(ob->imat, mvert[i].co);   /* cloth is in global coords */
00727         }
00728     }
00729 }
00730 
00731 
00732 int cloth_uses_vgroup(ClothModifierData *clmd)
00733 {
00734     return (((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING ) || 
00735         (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL )) && 
00736         ((clmd->sim_parms->vgroup_mass>0) || 
00737         (clmd->sim_parms->vgroup_struct>0)||
00738         (clmd->sim_parms->vgroup_bend>0)));
00739 }
00740 
00745 /* can be optimized to do all groups in one loop */
00746 static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm )
00747 {
00748     int i = 0;
00749     int j = 0;
00750     MDeformVert *dvert = NULL;
00751     Cloth *clothObj = NULL;
00752     int numverts;
00753     /* float goalfac = 0; */ /* UNUSED */
00754     ClothVertex *verts = NULL;
00755 
00756     if (!clmd || !dm) return;
00757 
00758     clothObj = clmd->clothObject;
00759 
00760     numverts = dm->getNumVerts ( dm );
00761 
00762     verts = clothObj->verts;
00763     
00764     if (cloth_uses_vgroup(clmd))
00765     {
00766         for ( i = 0; i < numverts; i++, verts++ )
00767         {   
00768             dvert = dm->getVertData ( dm, i, CD_MDEFORMVERT );
00769             if ( dvert )
00770             {
00771                 for ( j = 0; j < dvert->totweight; j++ )
00772                 {
00773                     if (( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_mass-1)) && (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ))
00774                     {
00775                         verts->goal = dvert->dw [j].weight;
00776                         /* goalfac= 1.0f; */ /* UNUSED */
00777                         
00778                         /*
00779                         // Kicking goal factor to simplify things...who uses that anyway?
00780                         // ABS ( clmd->sim_parms->maxgoal - clmd->sim_parms->mingoal );
00781                         */
00782                         
00783                         verts->goal  = ( float ) pow ( verts->goal , 4.0f );
00784                         if ( verts->goal >=SOFTGOALSNAP )
00785                         {
00786                              verts->flags |= CLOTH_VERT_FLAG_PINNED;
00787                         }
00788                     }
00789                     
00790                     if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING )
00791                     {
00792                         if( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_struct-1))
00793                         {
00794                             verts->struct_stiff = dvert->dw [j].weight;
00795                             verts->shear_stiff = dvert->dw [j].weight;
00796                         }
00797                         
00798                         if( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_bend-1))
00799                         {
00800                             verts->bend_stiff = dvert->dw [j].weight;
00801                         }
00802                     }
00803                     /*
00804                     // for later
00805                     if( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_weight-1))
00806                     {
00807                         verts->mass = dvert->dw [j].weight;
00808                     }
00809                     */
00810                 }
00811             }
00812         }
00813     }
00814 }
00815 
00816 static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float UNUSED(framenr), int first)
00817 {
00818     int i = 0;
00819     MVert *mvert = NULL;
00820     ClothVertex *verts = NULL;
00821     float (*shapekey_rest)[3]= NULL;
00822     float tnull[3] = {0,0,0};
00823     Cloth *cloth = NULL;
00824     float maxdist = 0;
00825 
00826     // If we have a clothObject, free it. 
00827     if ( clmd->clothObject != NULL )
00828     {
00829         cloth_free_modifier ( clmd );
00830         if(G.rt > 0)
00831             printf("cloth_free_modifier cloth_from_object\n");
00832     }
00833 
00834     // Allocate a new cloth object.
00835     clmd->clothObject = MEM_callocN ( sizeof ( Cloth ), "cloth" );
00836     if ( clmd->clothObject )
00837     {
00838         clmd->clothObject->old_solver_type = 255;
00839         // clmd->clothObject->old_collision_type = 255;
00840         cloth = clmd->clothObject;
00841         clmd->clothObject->edgehash = NULL;
00842     }
00843     else if ( !clmd->clothObject )
00844     {
00845         modifier_setError ( & ( clmd->modifier ), "Out of memory on allocating clmd->clothObject." );
00846         return 0;
00847     }
00848 
00849     // mesh input objects need DerivedMesh
00850     if ( !dm )
00851         return 0;
00852 
00853     cloth_from_mesh ( clmd, dm );
00854 
00855     // create springs 
00856     clmd->clothObject->springs = NULL;
00857     clmd->clothObject->numsprings = -1;
00858     
00859     if( clmd->sim_parms->shapekey_rest )
00860         shapekey_rest = dm->getVertDataArray ( dm, CD_CLOTH_ORCO );
00861 
00862     mvert = dm->getVertArray ( dm );
00863 
00864     verts = clmd->clothObject->verts;
00865 
00866     // set initial values
00867     for ( i = 0; i < dm->getNumVerts(dm); i++, verts++ )
00868     {
00869         if(first)
00870         {
00871             copy_v3_v3( verts->x, mvert[i].co );
00872 
00873             mul_m4_v3( ob->obmat, verts->x );
00874 
00875             if( shapekey_rest ) {
00876                 verts->xrest= shapekey_rest[i];
00877                 mul_m4_v3( ob->obmat, verts->xrest );
00878             }
00879             else
00880                 verts->xrest = verts->x;
00881         }
00882         
00883         /* no GUI interface yet */
00884         verts->mass = clmd->sim_parms->mass; 
00885         verts->impulse_count = 0;
00886 
00887         if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL )
00888             verts->goal= clmd->sim_parms->defgoal;
00889         else
00890             verts->goal= 0.0f;
00891 
00892         verts->flags = 0;
00893         copy_v3_v3 ( verts->xold, verts->x );
00894         copy_v3_v3 ( verts->xconst, verts->x );
00895         copy_v3_v3 ( verts->txold, verts->x );
00896         copy_v3_v3 ( verts->tx, verts->x );
00897         mul_v3_fl( verts->v, 0.0f );
00898 
00899         verts->impulse_count = 0;
00900         copy_v3_v3 ( verts->impulse, tnull );
00901     }
00902     
00903     // apply / set vertex groups
00904     // has to be happen before springs are build!
00905     cloth_apply_vgroup (clmd, dm);
00906 
00907     if ( !cloth_build_springs ( clmd, dm ) )
00908     {
00909         cloth_free_modifier ( clmd );
00910         modifier_setError ( & ( clmd->modifier ), "Can't build springs." );
00911         printf("cloth_free_modifier cloth_build_springs\n");
00912         return 0;
00913     }
00914     
00915     for ( i = 0; i < dm->getNumVerts(dm); i++)
00916     {
00917         if((!(cloth->verts[i].flags & CLOTH_VERT_FLAG_PINNED)) && (cloth->verts[i].goal > ALMOST_ZERO))
00918         {
00919             cloth_add_spring (clmd, i, i, 0.0, CLOTH_SPRING_TYPE_GOAL);
00920         }
00921     }
00922     
00923     // init our solver
00924     if ( solvers [clmd->sim_parms->solver_type].init ) {
00925         solvers [clmd->sim_parms->solver_type].init ( ob, clmd );
00926     }
00927     
00928     if(!first)
00929         implicit_set_positions(clmd);
00930 
00931     clmd->clothObject->bvhtree = bvhtree_build_from_cloth ( clmd, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel) );
00932     
00933     for(i = 0; i < dm->getNumVerts(dm); i++)
00934     {
00935         maxdist = MAX2(maxdist, clmd->coll_parms->selfepsilon* ( cloth->verts[i].avg_spring_len*2.0f));
00936     }
00937     
00938     clmd->clothObject->bvhselftree = bvhselftree_build_from_cloth ( clmd, maxdist );
00939 
00940     return 1;
00941 }
00942 
00943 static void cloth_from_mesh ( ClothModifierData *clmd, DerivedMesh *dm )
00944 {
00945     unsigned int numverts = dm->getNumVerts ( dm );
00946     unsigned int numfaces = dm->getNumFaces ( dm );
00947     MFace *mface = dm->getFaceArray( dm );
00948     unsigned int i = 0;
00949 
00950     /* Allocate our vertices. */
00951     clmd->clothObject->numverts = numverts;
00952     clmd->clothObject->verts = MEM_callocN ( sizeof ( ClothVertex ) * clmd->clothObject->numverts, "clothVertex" );
00953     if ( clmd->clothObject->verts == NULL )
00954     {
00955         cloth_free_modifier ( clmd );
00956         modifier_setError ( & ( clmd->modifier ), "Out of memory on allocating clmd->clothObject->verts." );
00957         printf("cloth_free_modifier clmd->clothObject->verts\n");
00958         return;
00959     }
00960 
00961     // save face information
00962     clmd->clothObject->numfaces = numfaces;
00963     clmd->clothObject->mfaces = MEM_callocN ( sizeof ( MFace ) * clmd->clothObject->numfaces, "clothMFaces" );
00964     if ( clmd->clothObject->mfaces == NULL )
00965     {
00966         cloth_free_modifier ( clmd );
00967         modifier_setError ( & ( clmd->modifier ), "Out of memory on allocating clmd->clothObject->mfaces." );
00968         printf("cloth_free_modifier clmd->clothObject->mfaces\n");
00969         return;
00970     }
00971     for ( i = 0; i < numfaces; i++ )
00972         memcpy ( &clmd->clothObject->mfaces[i], &mface[i], sizeof ( MFace ) );
00973 
00974     /* Free the springs since they can't be correct if the vertices
00975     * changed.
00976     */
00977     if ( clmd->clothObject->springs != NULL )
00978         MEM_freeN ( clmd->clothObject->springs );
00979 
00980 }
00981 
00982 /***************************************************************************************
00983 * SPRING NETWORK BUILDING IMPLEMENTATION BEGIN
00984 ***************************************************************************************/
00985 
00986 // be careful: implicit solver has to be resettet when using this one!
00987 // --> only for implicit handling of this spring!
00988 int cloth_add_spring ( ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type)
00989 {
00990     Cloth *cloth = clmd->clothObject;
00991     ClothSpring *spring = NULL;
00992     
00993     if(cloth)
00994     {
00995         // TODO: look if this spring is already there
00996         
00997         spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
00998         
00999         if(!spring)
01000             return 0;
01001         
01002         spring->ij = indexA;
01003         spring->kl = indexB;
01004         spring->restlen =  restlength;
01005         spring->type = spring_type;
01006         spring->flags = 0;
01007         spring->stiffness = 0;
01008         
01009         cloth->numsprings++;
01010     
01011         BLI_linklist_prepend ( &cloth->springs, spring );
01012         
01013         return 1;
01014     }
01015     return 0;
01016 }
01017 
01018 static void cloth_free_errorsprings(Cloth *cloth, EdgeHash *UNUSED(edgehash), LinkNode **edgelist)
01019 {
01020     unsigned int i = 0;
01021     
01022     if ( cloth->springs != NULL )
01023     {
01024         LinkNode *search = cloth->springs;
01025         while(search)
01026         {
01027             ClothSpring *spring = search->link;
01028                         
01029             MEM_freeN ( spring );
01030             search = search->next;
01031         }
01032         BLI_linklist_free(cloth->springs, NULL);
01033         
01034         cloth->springs = NULL;
01035     }
01036     
01037     if(edgelist)
01038     {
01039         for ( i = 0; i < cloth->numverts; i++ )
01040         {
01041             BLI_linklist_free ( edgelist[i],NULL );
01042         }
01043 
01044         MEM_freeN ( edgelist );
01045     }
01046     
01047     if(cloth->edgehash)
01048         BLI_edgehash_free ( cloth->edgehash, NULL );
01049 }
01050 
01051 static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm )
01052 {
01053     Cloth *cloth = clmd->clothObject;
01054     ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL;
01055     unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0;
01056     unsigned int i = 0;
01057     unsigned int numverts = (unsigned int)dm->getNumVerts ( dm );
01058     unsigned int numedges = (unsigned int)dm->getNumEdges ( dm );
01059     unsigned int numfaces = (unsigned int)dm->getNumFaces ( dm );
01060     MEdge *medge = dm->getEdgeArray ( dm );
01061     MFace *mface = dm->getFaceArray ( dm );
01062     int index2 = 0; // our second vertex index
01063     LinkNode **edgelist = NULL;
01064     EdgeHash *edgehash = NULL;
01065     LinkNode *search = NULL, *search2 = NULL;
01066     
01067     // error handling
01068     if ( numedges==0 )
01069         return 0;
01070 
01071     cloth->springs = NULL;
01072 
01073     edgelist = MEM_callocN ( sizeof ( LinkNode * ) * numverts, "cloth_edgelist_alloc" );
01074     
01075     if(!edgelist)
01076         return 0;
01077     
01078     for ( i = 0; i < numverts; i++ )
01079     {
01080         edgelist[i] = NULL;
01081     }
01082 
01083     if ( cloth->springs )
01084         MEM_freeN ( cloth->springs );
01085 
01086     // create spring network hash
01087     edgehash = BLI_edgehash_new();
01088 
01089     // structural springs
01090     for ( i = 0; i < numedges; i++ )
01091     {
01092         spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
01093 
01094         if ( spring )
01095         {
01096             spring->ij = MIN2(medge[i].v1, medge[i].v2);
01097             spring->kl = MAX2(medge[i].v2, medge[i].v1);
01098             spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
01099             clmd->sim_parms->avg_spring_len += spring->restlen;
01100             cloth->verts[spring->ij].avg_spring_len += spring->restlen;
01101             cloth->verts[spring->kl].avg_spring_len += spring->restlen;
01102             cloth->verts[spring->ij].spring_count++;
01103             cloth->verts[spring->kl].spring_count++;
01104             spring->type = CLOTH_SPRING_TYPE_STRUCTURAL;
01105             spring->flags = 0;
01106             spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
01107             struct_springs++;
01108             
01109             BLI_linklist_prepend ( &cloth->springs, spring );
01110         }
01111         else
01112         {
01113             cloth_free_errorsprings(cloth, edgehash, edgelist);
01114             return 0;
01115         }
01116     }
01117     
01118     if(struct_springs > 0)
01119         clmd->sim_parms->avg_spring_len /= struct_springs;
01120     
01121     for(i = 0; i < numverts; i++)
01122     {
01123         cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count);
01124     }
01125     
01126     // shear springs
01127     for ( i = 0; i < numfaces; i++ )
01128     {
01129         // triangle faces already have shear springs due to structural geometry
01130         if ( !mface[i].v4 )
01131             continue; 
01132         
01133         spring = ( ClothSpring *) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
01134         
01135         if(!spring)
01136         {
01137             cloth_free_errorsprings(cloth, edgehash, edgelist);
01138             return 0;
01139         }
01140 
01141         spring->ij = MIN2(mface[i].v1, mface[i].v3);
01142         spring->kl = MAX2(mface[i].v3, mface[i].v1);
01143         spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
01144         spring->type = CLOTH_SPRING_TYPE_SHEAR;
01145         spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;
01146 
01147         BLI_linklist_append ( &edgelist[spring->ij], spring );
01148         BLI_linklist_append ( &edgelist[spring->kl], spring );
01149         shear_springs++;
01150 
01151         BLI_linklist_prepend ( &cloth->springs, spring );
01152 
01153         
01154         // if ( mface[i].v4 ) --> Quad face
01155         spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
01156         
01157         if(!spring)
01158         {
01159             cloth_free_errorsprings(cloth, edgehash, edgelist);
01160             return 0;
01161         }
01162 
01163         spring->ij = MIN2(mface[i].v2, mface[i].v4);
01164         spring->kl = MAX2(mface[i].v4, mface[i].v2);
01165         spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
01166         spring->type = CLOTH_SPRING_TYPE_SHEAR;
01167         spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0;
01168 
01169         BLI_linklist_append ( &edgelist[spring->ij], spring );
01170         BLI_linklist_append ( &edgelist[spring->kl], spring );
01171         shear_springs++;
01172 
01173         BLI_linklist_prepend ( &cloth->springs, spring );
01174     }
01175     
01176     if(numfaces) {
01177         // bending springs
01178         search2 = cloth->springs;
01179         for ( i = struct_springs; i < struct_springs+shear_springs; i++ )
01180         {
01181             if ( !search2 )
01182                 break;
01183 
01184             tspring2 = search2->link;
01185             search = edgelist[tspring2->kl];
01186             while ( search )
01187             {
01188                 tspring = search->link;
01189                 index2 = ( ( tspring->ij==tspring2->kl ) ? ( tspring->kl ) : ( tspring->ij ) );
01190                 
01191                 // check for existing spring
01192                 // check also if startpoint is equal to endpoint
01193                 if ( !BLI_edgehash_haskey ( edgehash, MIN2(tspring2->ij, index2), MAX2(tspring2->ij, index2) )
01194                 && ( index2!=tspring2->ij ) )
01195                 {
01196                     spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
01197                     
01198                     if(!spring)
01199                     {
01200                         cloth_free_errorsprings(cloth, edgehash, edgelist);
01201                         return 0;
01202                     }
01203 
01204                     spring->ij = MIN2(tspring2->ij, index2);
01205                     spring->kl = MAX2(tspring2->ij, index2);
01206                     spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
01207                     spring->type = CLOTH_SPRING_TYPE_BENDING;
01208                     spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
01209                     BLI_edgehash_insert ( edgehash, spring->ij, spring->kl, NULL );
01210                     bend_springs++;
01211 
01212                     BLI_linklist_prepend ( &cloth->springs, spring );
01213                 }
01214                 search = search->next;
01215             }
01216             search2 = search2->next;
01217         }
01218     }
01219     else if(struct_springs > 2) {
01220         /* bending springs for hair strands */
01221         /* The current algorightm only goes through the edges in order of the mesh edges list   */
01222         /* and makes springs between the outer vert of edges sharing a vertice. This works just */
01223         /* fine for hair, but not for user generated string meshes. This could/should be later  */
01224         /* extended to work with non-ordered edges so that it can be used for general "rope     */
01225         /* dynamics" without the need for the vertices or edges to be ordered through the length*/
01226         /* of the strands. -jahka */
01227         search = cloth->springs;
01228         search2 = search->next;
01229         while(search && search2)
01230         {
01231             tspring = search->link;
01232             tspring2 = search2->link;
01233 
01234             if(tspring->ij == tspring2->kl) {
01235                 spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
01236                 
01237                 if(!spring)
01238                 {
01239                     cloth_free_errorsprings(cloth, edgehash, edgelist);
01240                     return 0;
01241                 }
01242 
01243                 spring->ij = tspring2->ij;
01244                 spring->kl = tspring->kl;
01245                 spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
01246                 spring->type = CLOTH_SPRING_TYPE_BENDING;
01247                 spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
01248                 bend_springs++;
01249 
01250                 BLI_linklist_prepend ( &cloth->springs, spring );
01251             }
01252             
01253             search = search->next;
01254             search2 = search2->next;
01255         }
01256     }
01257     
01258     /* insert other near springs in edgehash AFTER bending springs are calculated (for selfcolls) */
01259     for ( i = 0; i < numedges; i++ ) // struct springs
01260         BLI_edgehash_insert ( edgehash, MIN2(medge[i].v1, medge[i].v2), MAX2(medge[i].v2, medge[i].v1), NULL );
01261     
01262     for ( i = 0; i < numfaces; i++ ) // edge springs
01263     {
01264         if(mface[i].v4)
01265         {
01266             BLI_edgehash_insert ( edgehash, MIN2(mface[i].v1, mface[i].v3), MAX2(mface[i].v3, mface[i].v1), NULL );
01267             
01268             BLI_edgehash_insert ( edgehash, MIN2(mface[i].v2, mface[i].v4), MAX2(mface[i].v2, mface[i].v4), NULL );
01269         }
01270     }
01271     
01272     
01273     cloth->numsprings = struct_springs + shear_springs + bend_springs;
01274     
01275     if ( edgelist )
01276     {
01277         for ( i = 0; i < numverts; i++ )
01278         {
01279             BLI_linklist_free ( edgelist[i],NULL );
01280         }
01281     
01282         MEM_freeN ( edgelist );
01283     }
01284     
01285     cloth->edgehash = edgehash;
01286     
01287     if(G.rt>0)
01288         printf("avg_len: %f\n",clmd->sim_parms->avg_spring_len);
01289 
01290     return 1;
01291 
01292 } /* cloth_build_springs */
01293 /***************************************************************************************
01294 * SPRING NETWORK BUILDING IMPLEMENTATION END
01295 ***************************************************************************************/
01296