Added an option "Cast Approximate" to control if a material should cast
[blender.git] / source / blender / blenkernel / intern / nla.c
index c697f6390212f94b791e026ca8f0af079d511c65..2af9159f70f23226d5451489b9c656db37a5ff00 100644 (file)
 #include <stdlib.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <string.h>
 #include <math.h>
 #include <float.h>
 
 #include "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
+#include "BLI_ghash.h"
 
 #include "DNA_anim_types.h"
 #include "DNA_action_types.h"
@@ -217,6 +219,9 @@ void copy_nladata (ListBase *dst, ListBase *src)
        if ELEM(NULL, dst, src)
                return;
                
+       /* clear out the destination list first for precautions... */
+       dst->first= dst->last= NULL;
+               
        /* copy each NLA-track, one at a time */
        for (nlt= src->first; nlt; nlt= nlt->next) {
                /* make a copy, and add the copy to the destination list */
@@ -289,7 +294,7 @@ NlaStrip *add_nlastrip (bAction *act)
        /* determine initial range 
         *      - strip length cannot be 0... ever...
         */
-       calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+       calc_action_range(strip->act, &strip->actstart, &strip->actend, 0);
        
        strip->start = strip->actstart;
        strip->end = (IS_EQ(strip->actstart, strip->actend)) ?  (strip->actstart + 1.0f): (strip->actend);
@@ -326,6 +331,9 @@ NlaStrip *add_nlastrip_to_stack (AnimData *adt, bAction *act)
                BKE_nlatrack_add_strip(nlt, strip);
        }
        
+       /* automatically name it too */
+       BKE_nlastrip_validate_name(adt, strip);
+       
        /* returns the strip added */
        return strip;
 }
@@ -361,16 +369,9 @@ static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short
                        return strip->end - scale*(cframe - strip->actstart);
                }
                else if (mode == NLATIME_CONVERT_UNMAP) {
-                       int repeatsNum = (int)((cframe - strip->start) / (actlength * scale));
-                       
-                       /* this method doesn't clip the values to lie within the action range only 
-                        *      - the '(repeatsNum * actlength * scale)' compensates for the fmod(...)
-                        *      - the fmod(...) works in the same way as for eval 
-                        */
-                       return strip->actend - (repeatsNum * actlength * scale) 
-                                       - (fmod(cframe - strip->start, actlength*scale) / scale);
+                       return strip->actend - (strip->end - cframe) / scale;   
                }
-               else {
+               else /* if (mode == NLATIME_CONVERT_EVAL) */{
                        if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) {
                                /* this case prevents the motion snapping back to the first frame at the end of the strip 
                                 * by catching the case where repeats is a whole number, which means that the end of the strip
@@ -391,14 +392,7 @@ static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short
                        return strip->start + scale*(cframe - strip->actstart);
                }
                else if (mode == NLATIME_CONVERT_UNMAP) {
-                       int repeatsNum = (int)((cframe - strip->start) / (actlength * scale));
-                       
-                       /* this method doesn't clip the values to lie within the action range only 
-                        *      - the '(repeatsNum * actlength * scale)' compensates for the fmod(...)
-                        *      - the fmod(...) works in the same way as for eval 
-                        */
-                       return strip->actstart + (repeatsNum * actlength * scale) 
-                                       + (fmod(cframe - strip->start, actlength*scale) / scale);
+                       return strip->actstart + (cframe - strip->start) / scale;
                }
                else /* if (mode == NLATIME_CONVERT_EVAL) */{
                        if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) {
@@ -503,7 +497,7 @@ float BKE_nla_tweakedit_remap (AnimData *adt, float cframe, short mode)
 }
 
 /* *************************************************** */
-/* Basic Utilities */
+/* NLA API */
 
 /* List of Strips ------------------------------------ */
 /* (these functions are used for NLA-Tracks and also for nested/meta-strips) */
@@ -988,6 +982,28 @@ NlaStrip *BKE_nlastrip_find_active (NlaTrack *nlt)
        return NULL;
 }
 
+/* Make the given NLA-Strip the active one within the given block */
+void BKE_nlastrip_set_active (AnimData *adt, NlaStrip *strip)
+{
+       NlaTrack *nlt;
+       NlaStrip *nls;
+       
+       /* sanity checks */
+       if (adt == NULL)
+               return;
+       
+       /* loop over tracks, deactivating*/
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (nls= nlt->strips.first; nls; nls= nls->next)  {
+                       if (nls != strip)
+                               nls->flag &= ~NLASTRIP_FLAG_ACTIVE;
+                       else
+                               nls->flag |= NLASTRIP_FLAG_ACTIVE;
+               }
+       }
+}
+
+
 /* Does the given NLA-strip fall within the given bounds (times)? */
 short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max)
 {
@@ -1019,9 +1035,34 @@ short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max)
        return 1;
 }
 
+/* Recalculate the start and end frames for the current strip, after changing
+ * the extents of the action or the mapping (repeats or scale factor) info
+ */
+void BKE_nlastrip_recalculate_bounds (NlaStrip *strip)
+{
+       float actlen, mapping;
+       
+       /* sanity checks
+        *      - must have a strip
+        *      - can only be done for action clips
+        */
+       if ((strip == NULL) || (strip->type != NLASTRIP_TYPE_CLIP))
+               return;
+               
+       /* calculate new length factors */
+       actlen= strip->actend - strip->actstart;
+       if (IS_EQ(actlen, 0.0f)) actlen= 1.0f;
+       
+       mapping= strip->scale * strip->repeat;
+       
+       /* adjust endpoint of strip in response to this */
+       if (IS_EQ(mapping, 0.0f) == 0)
+               strip->end = (actlen * mapping) + strip->start;
+}
+
 /* Is the given NLA-strip the first one to occur for the given AnimData block */
 // TODO: make this an api method if necesary, but need to add prefix first
-short nlastrip_is_first (AnimData *adt, NlaStrip *strip)
+static short nlastrip_is_first (AnimData *adt, NlaStrip *strip)
 {
        NlaTrack *nlt;
        NlaStrip *ns;
@@ -1140,8 +1181,219 @@ void BKE_nlastrip_validate_fcurves (NlaStrip *strip)
                }
        }
 }
-/* Tools ------------------------------------------- */
+
+/* Sanity Validation ------------------------------------ */
+
+/* Find (and set) a unique name for a strip from the whole AnimData block 
+ * Uses a similar method to the BLI method, but is implemented differently
+ * as we need to ensure that the name is unique over several lists of tracks,
+ * not just a single track.
+ */
+void BKE_nlastrip_validate_name (AnimData *adt, NlaStrip *strip)
+{
+       GHash *gh;
+       NlaStrip *tstrip;
+       NlaTrack *nlt;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, strip)
+               return;
+               
+       /* give strip a default name if none already */
+       if (strip->name[0]==0) {
+               switch (strip->type) {
+                       case NLASTRIP_TYPE_CLIP: /* act-clip */
+                               sprintf(strip->name, "Act: %s", (strip->act)?(strip->act->id.name+2):("<None>"));
+                               break;
+                       case NLASTRIP_TYPE_TRANSITION: /* transition */
+                               sprintf(strip->name, "Transition");
+                               break;
+                       case NLASTRIP_TYPE_META: /* meta */
+                               sprintf(strip->name, "Meta");
+                               break;
+                       default:
+                               sprintf(strip->name, "NLA Strip");
+                               break;
+               }
+       }
+       
+       /* build a hash-table of all the strips in the tracks 
+        *      - this is easier than iterating over all the tracks+strips hierarchy everytime
+        *        (and probably faster)
+        */
+       gh= BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp);
+       
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (tstrip= nlt->strips.first; tstrip; tstrip= tstrip->next) {
+                       /* don't add the strip of interest */
+                       if (tstrip == strip) 
+                               continue;
+                       
+                       /* use the name of the strip as the key, and the strip as the value, since we're mostly interested in the keys */
+                       BLI_ghash_insert(gh, tstrip->name, tstrip);
+               }
+       }
+       
+       /* if the hash-table has a match for this name, try other names... 
+        *      - in an extreme case, it might not be able to find a name, but then everything else in Blender would fail too :)
+        */
+       if (BLI_ghash_haskey(gh, strip->name)) {
+               char tempname[128];
+               int     number = 1;
+               char *dot;
+               
+               /* Strip off the suffix */
+               dot = strchr(strip->name, '.');
+               if (dot) *dot=0;
+               
+               /* Try different possibilities */
+               for (number = 1; number <= 999; number++) {
+                       /* assemble alternative name */
+                       BLI_snprintf(tempname, 128, "%s%c%03d", strip->name, ".", number);
+                       
+                       /* if hash doesn't have this, set it */
+                       if (BLI_ghash_haskey(gh, tempname) == 0) {
+                               BLI_strncpy(strip->name, tempname, sizeof(strip->name));
+                               break;
+                       }
+               }
+       }
+       
+       /* free the hash... */
+       BLI_ghash_free(gh, NULL, NULL);
+}
+
+/* ---- */
+
+/* Get strips which overlap the given one at the start/end of its range 
+ *     - strip: strip that we're finding overlaps for
+ *     - track: nla-track that the overlapping strips should be found from
+ *     - start, end: frames for the offending endpoints
+ */
+static void nlastrip_get_endpoint_overlaps (NlaStrip *strip, NlaTrack *track, float **start, float **end)
+{
+       NlaStrip *nls;
+       
+       /* find strips that overlap over the start/end of the given strip,
+        * but which don't cover the entire length 
+        */
+       // TODO: this scheme could get quite slow for doing this on many strips...
+       for (nls= track->strips.first; nls; nls= nls->next) {
+               /* check if strip overlaps (extends over or exactly on) the entire range of the strip we're validating */
+               if ((nls->start <= strip->start) && (nls->end >= strip->end)) {
+                       *start= NULL;
+                       *end= NULL;
+                       return;
+               }
+               
+               /* check if strip doesn't even occur anywhere near... */
+               if (nls->end < strip->start)
+                       continue; /* skip checking this strip... not worthy of mention */
+               if (nls->start > strip->end)
+                       return; /* the range we're after has already passed */
+                       
+               /* if this strip is not part of an island of continuous strips, it can be used
+                *      - this check needs to be done for each end of the strip we try and use...
+                */
+               if ((nls->next == NULL) || IS_EQ(nls->next->start, nls->end)==0) {
+                       if ((nls->end > strip->start) && (nls->end < strip->end))
+                               *start= &nls->end;
+               }
+               if ((nls->prev == NULL) || IS_EQ(nls->prev->end, nls->start)==0) {
+                       if ((nls->start < strip->end) && (nls->start > strip->start))
+                               *end= &nls->start;
+               }
+       }
+}
+
+/* Determine auto-blending for the given strip */
+void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls)
+{
+       float *ps=NULL, *pe=NULL;
+       float *ns=NULL, *ne=NULL;
+       
+       /* sanity checks */
+       if ELEM(NULL, nls, nlt)
+               return;
+       if ((nlt->prev == NULL) && (nlt->next == NULL))
+               return;
+       if ((nls->flag & NLASTRIP_FLAG_AUTO_BLENDS)==0)
+               return;
+       
+       /* get test ranges */
+       if (nlt->prev)
+               nlastrip_get_endpoint_overlaps(nls, nlt->prev, &ps, &pe);
+       if (nlt->next)
+               nlastrip_get_endpoint_overlaps(nls, nlt->next, &ns, &ne);
+               
+       /* set overlaps for this strip 
+        *      - don't use the values obtained though if the end in question 
+        *        is directly followed/preceeded by another strip, forming an 
+        *        'island' of continuous strips
+        */
+       if ( (ps || ns) && ((nls->prev == NULL) || IS_EQ(nls->prev->end, nls->start)==0) ) 
+       {
+               /* start overlaps - pick the largest overlap */
+               if ( ((ps && ns) && (*ps > *ns)) || (ps) )
+                       nls->blendin= *ps - nls->start;
+               else
+                       nls->blendin= *ns - nls->start;
+       }
+       else /* no overlap allowed/needed */
+               nls->blendin= 0.0f;
+               
+       if ( (pe || ne) && ((nls->next == NULL) || IS_EQ(nls->next->start, nls->end)==0) ) 
+       {
+               /* end overlaps - pick the largest overlap */
+               if ( ((pe && ne) && (*pe > *ne)) || (pe) )
+                       nls->blendout= nls->end - *pe;
+               else
+                       nls->blendout= nls->end - *ne;
+       }
+       else /* no overlap allowed/needed */
+               nls->blendout= 0.0f;
+}
+
+/* Ensure that auto-blending and other settings are set correctly */
+void BKE_nla_validate_state (AnimData *adt)
+{
+       NlaStrip *strip, *fstrip=NULL;
+       NlaTrack *nlt;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return;
+               
+       /* adjust blending values for auto-blending, and also do an initial pass to find the earliest strip */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* auto-blending first */
+                       BKE_nlastrip_validate_autoblends(nlt, strip);
+                       
+                       /* extend mode - find first strip */
+                       if ((fstrip == NULL) || (strip->start < fstrip->start))
+                               fstrip= strip;
+               }
+       }
+       
+       /* second pass over the strips to adjust the extend-mode to fix any problems */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* apart from 'nothing' option which user has to explicitly choose, we don't really know if 
+                        * we should be overwriting the extend setting (but assume that's what the user wanted)
+                        */
+                       // TODO: 1 solution is to tie this in with auto-blending...
+                       if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) {
+                               if (strip == fstrip)
+                                       strip->extendmode= NLASTRIP_EXTEND_HOLD;
+                               else
+                                       strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
+                       }
+               }
+       }
+}
+
+/* Core Tools ------------------------------------------- */
 
 /* For the given AnimData block, add the active action to the NLA
  * stack (i.e. 'push-down' action). The UI should only allow this 
@@ -1187,6 +1439,9 @@ void BKE_nla_action_pushdown (AnimData *adt)
                        // FIXME: this needs to be more automated, since user can rearrange strips
                        strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
                }
+               
+               /* make strip the active one... */
+               BKE_nlastrip_set_active(adt, strip);
        }
 }